Empujando una estructura a una matriz que contiene una matriz en sí

Estaba leyendo este artículo , que da un ejemplo de un contrato que no funcionará. Aunque lo han arreglado en el artículo, quería saber por qué no funciona. He copiado una versión simplificada a continuación:

pragma solidity ^0.4.18;

contract StructArrayInitWrong {
  struct Room {
    address[] players;       
  }  
  Room[] rooms;

  function createRoom() public {
    address[] adr;
    adr.push(msg.sender);
    Room memory room = Room(adr);   
    rooms.push(room);
  }

  function getRoomsLength() view returns (uint) {
    return rooms.length;
  }
}

Lo extraño es roomsque la longitud de la matriz aumenta en dos cada vez createRoomque se llama en este contrato. ¿Puedes explicar este comportamiento? Gracias.

Respuestas (1)

el problema es con esta linea

address[] adr;

Hay una "advertencia" sobre el almacenamiento no inicializado. Están sucediendo más cosas aquí de lo que podría sugerir una advertencia amable. Realmente no entiendo por qué esto no es un error grave, por lo que el desarrollador no lo acepta.

Debido a que el almacenamiento no estaba inicializado, el compilador no sabía dónde colocar la matriz dinámica, adr[]por lo que la colocó en la primera ranura . Has leído bien. Está pisando fuerte rooms[]. ¡Ay!

Dado que el ocupante de esa ranura también es una matriz dinámica, también usa la primera palabra para describir la longitud de la matriz. Por lo tanto, obtenemos el extraño comportamiento observado... pase a una matriz, luego a la otra y ¡puf! - ambas matrices tienen una longitud de 2.

Este no es el único caso en el que el almacenamiento declarado dentro de las funciones en lugar de en el lugar habitual (fuera) provoca sobrescrituras de datos potencialmente catastróficas. Vea aquí otro ejemplo: ¿Supervisión del compilador de Solc? La declaración de asignación inapropiada sobrescribe el almacenamiento

Teniendo en cuenta que se supone que los contratos inteligentes son claros y libres de defectos, no soy un gran admirador de este tipo de resultado no deseado. Esto y las variables de referencia suman demasiado vudú para mi gusto.

Tal vez alguien más intervenga con mejores heurísticas. Podría sugerir formar dos hábitos

  • Diseñe siempre las variables de estado en el mismo lugar, cerca de la parte superior, fuera de las funciones para evitar este tipo de sobrescritura inesperada.
  • Nunca ajuste/establezca una variable que se leyó previamente del almacenamiento indexado porque podría sobrescribir el almacenamiento de forma inesperada. Ver aquí: http://vessenes.com/solidity-frustrations-references-and-mapping/

Aquí está el código con un cambio y funcionando como se esperaba:

pragma solidity ^0.4.18;

contract StructArrayInitWrong {

  struct Room {
    address[] players;       
  }  
  Room[] rooms;
  address[] adr; // <=== if we're going to store this, then let's store this.

  function createRoom() public {
                                    // <=== note gaping hole
    adr.push(msg.sender);
    Room memory room = Room(adr);   
    rooms.push(room);
  }

  function getRoomsLength() public view returns (uint) {
    return rooms.length;
  }
}

Espero eso ayude.

¡Gracias por la respuesta detallada! No quería almacenar la matriz en el almacenamiento, así que cambié la variable de memoryesta manera: address[] memory adr = new address[](1); adr[0] = msg.sender;. Esto parece funcionar también.