¿Cómo funciona Mapping en Solidity?

Revisé el ejemplo de Crowdfunding en solidity donde encontré lo siguiente:

contract Crowdfunding {
struct CampaignData {
    address recipient;
    uint contributed;
    uint goal;
    uint deadline;
    uint num_contributions;
    mapping(uint => Contribution) contributions;
}

struct Contribution {
    address contributor;
    uint amount;
}

uint nextCampaignId;
mapping(uint256 => CampaignData) campaigns;

// Start a new campaign.
function start(address recipient, uint256 goal, uint256 deadline) returns (uint id) {
    var campaign = campaigns[nextCampaignId];
    campaign.recipient = recipient;
    campaign.goal = goal;
    campaign.deadline = deadline;
    nextCampaignId ++;
    id = nextCampaignId;
}

¿Qué está pasando realmente aquí?

¿Por qué usamos mapeo? ¿Cómo nos está ayudando? No podía entender su funcionamiento y, por lo tanto, no podía usarlo.

Respuestas (3)

Anatomía de un mapeo

Una asignación se utiliza para estructurar tipos de valores, como valores booleanos, enteros, direcciones y estructuras. Consta de dos partes principales: ay _KeyTypea _ValueType; aparecen en la siguiente sintaxis:

mapping (_KeyType => _ValueType) mapName

En el ejemplo de contrato proporcionado anteriormente,

mapping (uint256 => CampaignData) campaigns

el uint256es el _KeyTypey el CampaignDataes el _ValueType. Tenga en cuenta para más adelante que _ValueType, CampaignData, es una estructura.

Asignación de tipos de valor a tipos de clave

Piense en el _KeyTypecomo la clave que pasará a través de una función para obtener un valor deseado, o _ValueType. De forma predeterminada, una asignación está inicialmente vacía, por lo _KeyTypeque primero será necesario asignar un nuevo a a un _ValueType.

La startfunción del contrato de ejemplo maneja 3 procesos básicos: (1) dar un _KeyTypea una nueva _ValueType CampaignDataestructura; (2) poblar la nueva CampaignDataestructura con valores variables; y (3) adquirir una nueva _KeyType nextCampaignIDpara que esté lista en cubierta para la próxima vez startque se llame a la función del contrato de ejemplo. Estos segmentos de la función se pueden diseccionar así:

(1) dando a _KeyTypea una nueva _ValueType CampaignDataestructura:

    var campaign = campaigns[nextCampaignId];

En esta línea, nextCampaignIdse asigna como _KeyType, y la nueva campaignestructura es _ValueType.

(2) poblar la nueva CampaignDataestructura con valores variables:

    campaign.recipient = recipient;
    campaign.goal = goal;
    campaign.deadline = deadline;

(3) obtener una nueva _KeyType nextCampaignIDpara la próxima vez que se llame a la función:

    nextCampaignId ++;

El uso de un mapeo aquí es útil porque un mapeo puede almacenar muchos _KeyTypesa _ValueTypes; en este caso, si hay muchas campañas que ocurren a la vez, cada una puede tener su propio ID de campaña. Cada campaña que tiene su propia ID es poderosa cuando se solicita CampaignData en funciones futuras.

Acceso a tipos de valor desde una asignación con tipos de clave

Este contrato de ejemplo en realidad no proporciona ninguna función que acceda a tipos de valor en la asignación. Pero podemos imaginar cómo se vería uno: tal vez si la fecha límite de una campaña se extiende de alguna manera, una extendDeadlinefunción podría verse así:

function extendDeadline(uint campaignID, uint256 newDeadline) {
    var campaign = campaigns[campaignId];
    campaign.deadline = newDeadline;
}

La extendDeadlinefunción estaría usando campaignID _KeyTypepara consultar el campaignsmapeo para encontrar la CampaignDataestructura adecuada y actualizarla deadlinecon newDeadline.

Entonces, ¿es simplemente una especie de matriz (sin acceso por índice) de objetos javascript? Esta respuesta es bastante buena, pero cuando la veo en el código todavía me confundo.

Una asignación es una colección de pares de valores clave. Entonces, si viene del mundo de JavaScript, puede pensar que el mapeo es muy similar a un objeto.

Viniendo de Ruby son como hashes y si vienes de Python son como diccionarios.

Lo más importante a tener en cuenta sobre una asignación es que todas las claves deben ser del mismo tipo y todos los valores también deben ser del mismo tipo.

Todas las claves tienen que ser del mismo tipo, podemos designar mapeos así:

mapping(string => string)

Decimos mapeo y especificamos el tipo de clave que queremos usar, una flechita y luego el tipo de valor que queremos usar.

Aquí hay un ejemplo de mapeo con clave de tipo int y bool:

mapping(int => bool)

Las asignaciones se utilizan con mayor frecuencia para almacenar colecciones de datos.

  mapping(uint256 => CampaignData) campaigns;

El mapeo anterior dice que las claves serán "uint" y los valores serán la estructura "CampaignData" y etiquetarán la variable con "campañas". "CampaignData" podría almacenarse en una matriz, pero recorrerla en bucle costaría demasiado gas. En ethereum, cada operación te cuesta e imagina que tienes miles de campañas almacenadas y necesitas buscar cada campaña, esto te costaría una gran cantidad de gasolina. En su lugar, se usa el mapeo porque el mapeo tiene una búsqueda de tiempo constante.

El mapeo en solidity es similar al mapeo en javascript pero hay algunas diferencias:

1- En solidez no se almacenan claves. Solo el valor que se almacena en la dirección de la memoria de estado calculada mediante el hash de la clave. En javascript podríamos recuperar todas las "claves" en una matriz con Object.keys(objectName).

2- Los valores en el mapeo de solidez no son iterables. No podemos hacer un bucle. El mapeo es bueno solo para la búsqueda de un solo valor.

3- En la solidez existen todos los valores. En javascript, si intentamos acceder a una clave que no existe, objectName['keyThatDoesNotExist']obtenemos indefinido. Pero en solidez, obtenemos un valor predeterminado basado en los valores. Si todos los valores son cadenas, el valor predeterminado es una cadena vacía '', si todos los valores son enteros, el valor predeterminado es 0, si todos los valores son booleanos, el valor predeterminado es false.