Código de operación no válido: pasar la matriz bytes32 a un método de contrato llamado desde otro método de otro contrato

Me he estado golpeando la cabeza con esto durante algunas horas, agradecería cualquier ayuda :)

Esta es una edición final para exponer claramente el problema: ¿ Existe una forma adecuada de pasar una matriz bytes32 a un método en el contrato B, que a su vez instancia un contrato A (desde su dirección) y pasa la matriz bytes32 a un método de ese contrato? ? Pasar el arreglo directamente al método de contrato A funciona bien, pero pasarlo al método de contrato B que a su vez lo pasa al método de contrato A no funciona cuando el arreglo tiene más de un byte32.

¿Es eso una limitación de la solidez? ¿O estoy haciendo algo mal (en solidez o con mi código javascript web3js)? ¿O es un error con testrpc?

Detalles completos a continuación. Avíseme si puede reproducir el problema, o si eso funciona bien o lo que sea.


Tengo un contrato A y un contrato B que heredan de él. Lo que quiero hacer es instanciar un contrato A en el contrato B, desde una dirección de contrato A ya implementada, y llamar a los métodos de esa instancia.

Sé cómo hacer esto (o al menos, creo que lo sé; ¿tal vez no es una forma adecuada de hacerlo?) y funciona. El problema real es con la matriz de bytes32.

Contexto

Antes que nada, aquí está el código de contratos.

contract A {
  struct MyStruct {
    address user;
    bytes32 [] stuff;
  }

  MyStruct [] public myStructs;

  function addStuff (bytes32 [] _stuff) public {
    myStructs.push (
      MyStruct ({user: msg.sender, stuff: _stuff})
    );
  }
}

contract B is A {
  function provideStuff (address to, bytes32 [] stuff) public {
    A(to).addStuff(stuff);
  }
}

Un poco de contexto: stuffes una cadena hexadecimal (que puede ser de 256 bits = 32 bytes (encaja perfectamente en a bytes32), o más (512, etc.); de ahí la matriz bytes32[]).

Primero, implemento un contrato A (que devuelve una instancia contractA) con la dirección de la billetera anAddress. Esto me da, cuando se implementa mi contrato, la dirección del contrato inteligente contractAaddress.

Llamar addStuffcon una matriz de bytes32 funciona perfectamente:

gasEstimate = contractA.addStuff.estimateGas(getBytes32Array(stuff), {from: anAddress);

contractA.addStuff(getBytes32Array(stuff), {from: anAddress, gas: gasEstimate});

Nota - getBytes32Array

Tengo una función, getBytes32Arrayque genera una matriz de Bytes32 (cadenas hexadecimales en realidad) de una cadena hexadecimal dada:

function getBytes32Array(string){
   var splittedString = string.match(/.{1,64}/g);

   for(var i=0; i< splittedString.length; i++){
      splittedString[i] = "0x"+splittedString[i];
   }

   return splittedString;
}

Esto me permite simplemente ingresar mi cadena hexadecimal, por ejemplo e0f89ca8eae95281590977802df657506a151304234d15570c12cc26263a8b7a2bf140bc9f80baa93879634717d9c2cf08cc6c96492c1b56c053ae54546f4f20(512 bits) y obtener ["0xe0f89ca8eae95281590977802df657506a151304234d15570c12cc26263a8b7a","0x2bf140bc9f80baa93879634717d9c2cf08cc6c96492c1b56c053ae54546f4f20"]lo que se entenderá como una matriz de bytes32 que contiene dos bytes32.


En el problema real

Llamar addStuffde esta manera funciona perfectamente con una matriz de bytes32 que contiene más de un byte32.

Ahora, en segundo lugar, estoy implementando mi contrato B (devolviendo una instancia contractB) con la dirección de la billetera anotherAddress. Esto me da, cuando se implementa mi contrato, la dirección del contrato inteligente contractBaddress.

Lo que me gustaría hacer es llamar al provideStuffmétodo del contrato B, dándole la dirección del contrato A implementado contractAaddressy el stuff, que debe instanciar el contrato A desde la dirección y llamar al addStuffcon el stuffproporcionado. (ver código solidez contrato B)

gasEstimate = contractB.provideStuff.estimateGas(contractAaddress, getBytes32Array(stuff), {from: anotherAddress});

contractB.provideStuff(contractAaddress, getBytes32Array(stuff), {from: anotherAddress, gas: gasEstimate});

Pero esto me da un código de operación no válido cada vez... A pesar de que la estimación de Gas funciona (¿no es raro?)... Esto funciona cuando mi matriz bytes32 solo tiene un bytes32. Aún más extraño, también funciona cuando tiene un segundo bytes32 que es una cadena hexadecimal muy corta ( ver PS1 ). Pero no puedo obtener un comportamiento consistente con una matriz de x bytes32, cada cadena hexadecimal de 32 bytes (64 caracteres) (o incluso más corta)...

Gracias de antemano.

PS1: el ejemplo anterior funciona bien si las cosas tienen un valor hexadecimal de 256 bits o un poco más (lo intenté con la codificación ["0x256BitValueHere","0xverySmallValueHere"], 0xverySmallValueHereno puede tener más de unos pocos caracteres (tal vez 10, no lo recuerdo), después de eso, se activa Op invalide Código de nuevo También probé con varios valores pequeños ["0xverySmallValueHere", "0xverySmallValueHere", "0xverySmallValueHere", "0xverySmallValueHere"], tampoco funcionará.

PS2: trabajando localmente con testrpc.


EDITAR : Todo funciona perfectamente en Remix. Entonces no es un problema de código de Solidity.

Respuestas (2)

Está confundiendo el contrato A de "instanciación" con el contrato A "heredado de".

La herencia se parece más a las bibliotecas de clases donde B asume las variables de estado y las funciones de A. No hay A por separado con quien hablar en el caso de que, contract B is Asolo B, que incluye el código fuente de A.

Dado que desea que dos contratos se comuniquen entre sí, primero implemente A, luego hable sobre ello en B. El método más fácil es que el archivo fuente de B incluya una copia de A para que el compilador pueda "verlo" y comprender el ABI (función firmas). No hay B is A, pero puede convertir una variable al tipo "ser" contract A, así:

A a;(algo así como uint x).

Ahora que se ha establecido el "tipo" de "a", el otro factor importante es su dirección. Suponiendo que ya haya implementado A en algún lugar, debe conocer su dirección. Puede pasar eso al constructor de B y terminar de crear una instancia de "a".

function B(address aAddress) public {
  a = A(aAddress); // so "a" is the "A" found at "aAddress"
}

Excelente. Ahora puede enviar mensajes de ida y vuelta con a.function(args).

Aquí hay un pequeño retoque similar al tuyo:

pragma solidity 0.4.17;

contract A {

    event LogTest(address sender, bytes32 element);

    function test(bytes32[] X) public returns(bool success) {

        // This unbounded loop is an anti-pattern, but it shows it working at small scales.

        for(uint i=0; i<X.length; i++) {
            LogTest(msg.sender, X[i]);
        }

        return true;
    }
}

contract B {

    A a; // variable "a" cast as type "A" which is the contract shown above

    function B(address aAddress) public {
        a = A(aAddress); // instantiate an instance of "a" already deployed at the address passed in
    }

    function testIt(bytes32[] array) public returns(bool success) {
        return a.test(array); // send a message to contract "a"
    }
}

Puedes jugar en Remix para verlo funcionar.

  1. Implementar un
  2. Copiar la dirección de A
  3. Implemente B, pegando A en el constructor
  4. Pruebe B.test con ["0x1","0x2"]para pasarle una matriz.

Espero eso ayude.

Hola Rob, en primer lugar, gracias por la rápida respuesta. Es bueno precisar la diferencia entre herencia e instanciación, pero no estaba creando esa confusión; simplemente sucede que mi contrato del que quiero llamar las funciones del contrato A, también se hereda de él. Editaré mi pregunta para que quede más claro. El problema persiste: ¿por qué funciona cuando paso un bytes32[] con solo un bytes32 y no más? ¿Es un problema en mi código de solidez (creo que no, la función addStuff funciona bien), mi código js (lo mismo aquí, incluso las obras de estimación de Gas), o es un error en testrpc?
Hola Rob, he editado mi pregunta para que quede más claro cuál es mi problema. Probé tu código en Remix y funciona perfectamente. También probé el mío (tuve que aprender a usar Remix, nunca probé nada con él antes, pero en realidad es inteligente hacerlo jaja) y todo funciona bien también... ¿Entonces el problema debe estar en testrpc, supongo? (Me he encontrado con cosas extrañas antes y he leído sobre personas que se quejan de que testrpc ya no se mantiene y tiene algunos problemas técnicos) ¿Podría reproducir este comportamiento y decirme si solo está de mi lado? (O dime si algo más está mal)

Acabo de probar tu contrato en Remix (Solidity versión 0.4.24) y funciona bien. Puedo escribir cosas a través del método provideStuffde contrato By luego leerlas a través de contrato A. Sin embargo, necesitaba agregar el siguiente método en el contrato Apara leer cosas de él:

function get (uint i, uint j) public view returns (bytes32) {
  return myStructs [i].stuff [j];
}

porque el captador predeterminado generado por Solidity no lee matrices dinámicas.