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.
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: stuff
es 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 addStuff
con una matriz de bytes32 funciona perfectamente:
gasEstimate = contractA.addStuff.estimateGas(getBytes32Array(stuff), {from: anAddress);
contractA.addStuff(getBytes32Array(stuff), {from: anAddress, gas: gasEstimate});
Tengo una función, getBytes32Array
que 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.
Llamar addStuff
de 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 provideStuff
método del contrato B, dándole la dirección del contrato A implementado contractAaddress
y el stuff
, que debe instanciar el contrato A desde la dirección y llamar al addStuff
con el stuff
proporcionado. (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"]
, 0xverySmallValueHere
no 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.
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 A
solo 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.
["0x1","0x2"]
para pasarle una matriz.Espero eso ayude.
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 provideStuff
de contrato B
y luego leerlas a través de contrato A
. Sin embargo, necesitaba agregar el siguiente método en el contrato A
para 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.
Hillfias
Hillfias