¿Cómo puedo persistir una asignación a una estructura en un mapeo?

Lo siguiente es una simplificación del problema al que me enfrento;

pragma solidity 0.4.18;

contract ValueSetter {
  struct BasicValue {
    uint256 value;
  }

  address my_address; // Just for the require later
  BasicValue fixed_value;
  mapping(address => BasicValue) fixed_values;

  function ValueSetter(address init_address) public {
    my_address = init_address;
    fixed_values[init_address] = fixed_value;
  }

  function set_value(uint256 my_value) external {
    // Just to prove that the bug isn't in my calling params
    require(msg.sender == my_address && my_value != 0);

    fixed_values[msg.sender].value = my_value;

    assert(fixed_value.value != 0);
  }
}

Cuando llamo set_valuecon un valor distinto de cero, presiono la afirmación. Parece que el problema es que en esta línea,

fixed_values[msg.sender].value = my_value;

mi intento de asignar a la estructura dentro del mapeo no se asigna a la estructura almacenada en fixed_value. También probé lo siguiente en su lugar;

BasicValue storage basic_val = fixed_values[msg.sender];
basic_val.value = my_value;

Pero esto no cambia el resultado. Leí en la documentación de solidez que,

las asignaciones entre el almacenamiento y la memoria y también a una variable de estado (incluso de otras variables de estado) siempre crean una copia independiente

Si ese es el caso, ¿cómo puedo obtener una estructura "fuera" del mapeo para usarla localmente?

(Notas: parece que el contrato anterior hace cosas ridículamente más complicadas de lo necesario porque el contrato real tiene más requisitos. Por ejemplo, hay más de un atributo en la estructura, hay más de una estructura declarada en la inicialización, hay más de una estructura en el mapeo, y necesito hacer una lógica que involucre a más de uno de ellos).

Respuestas (2)

Estas líneas son innecesarias.

BasicValue fixed_value;
fixed_values[my_address] = fixed_value;

pero el verdadero problema es que esta línea sería

assert(fixed_values[msg.sender].value != 0);

porque quieres la estructura en ese índice.

pragma solidity 0.4.18;

contract ValueSetter {

  struct ValueStruct {
    uint256 value;
  }

  address public my_address; // Just for the require later

  mapping(address => ValueStruct) public valueStructs;

  function ValueSetter() public {
    my_address = msg.sender;
  }

  function set_value(uint256 my_value) external {

    // this shows the function is called by the deployer account
    require(msg.sender == my_address && my_value != 0);

    // this stores a uint in the .value element of the ValueStruct 
    // stored in the mapping at the index which is the sender

    valueStructs[msg.sender].value = my_value;

    // this just gets confirms the value is where we put it
    assert(valueStructs[msg.sender].value != 0);
  }
}

Espero eso ayude.

Sí, todas las variables de estado se almacenan en blockchain antes del matrimonio. Pero llegando a tu pregunta por qué:

function ValueSetter(address init_address) public {
    my_address = init_address;
    fixed_values[init_address] = fixed_value;
}

En el código anterior, su inicialización fixed_values[init_address] = fixed_value;significa internamente fixed_value = BasicValue(0)y esto se asignará afixed_values[init_address] = fixed_value;

Internamente su código se parece a:

function ValueSetter(address init_address) public {
    fixed_value = BasicValue(0);
    my_address = init_address;
    fixed_values[init_address] = fixed_value;
}

Y llegando a tu hay un error de lógica:

  function set_value(uint256 my_value) external {
    // Just to prove that the bug isn't in my calling params
    require(msg.sender == my_address && my_value != 0);

    fixed_values[msg.sender].value = my_value;

    assert(fixed_value.value != 0);
  }

Según mi explicación, fixed_value.value es cero. Afirmar fallará. Y sus transacciones volverán al estado anterior. No guardará el valor actual en la cadena de bloques. Pero la transacción se registrará en la cadena como un error.

Código modificado: pragma solidity 0.4.18;

contract ValueSetter {
  struct BasicValue {
    uint256 value;
  }

  address my_address; // Just for the require later
  BasicValue fixed_value;
  mapping(address => BasicValue) fixed_values;

  function ValueSetter(address init_address) public {
    my_address = init_address;
    fixed_values[init_address] = BasicValue(1000);
  }

  function set_value(uint256 my_value) external {
    // Just to prove that the bug isn't in my calling params
    require(msg.sender == my_address && my_value != 0);

    fixed_values[msg.sender].value = my_value;

    //assert(fixed_value.value != 0);
  }
}