Ungültiger Op-Code – Übergeben des bytes32-Arrays an eine Vertragsmethode, die von einer anderen Methode eines anderen Vertrags aufgerufen wird

Ich schlage mir seit einigen Stunden den Kopf darüber, ich würde mich über jede Hilfe freuen :)

Dies ist eine abschließende Bearbeitung, um das Problem klar zu formulieren: Gibt es eine geeignete Möglichkeit, ein bytes32-Array an eine Methode für Vertrag B zu übergeben, die selbst einen Vertrag A (von seiner Adresse) instanziiert und das bytes32-Array an eine Methode dieses Vertrags übergibt ? Das Array direkt an die Methode von Vertrag A zu übergeben, funktioniert problemlos, aber es an die Methode von Vertrag B zu übergeben, die es wiederum an die Methode von Vertrag A übergibt, funktioniert nicht, wenn das Array mehr als ein Byte hat32.

Ist das eine Einschränkung der Solidität? Oder mache ich etwas falsch (in Solidität oder mit meinem Javascript-Web3js-Code)? Oder ist das ein Fehler bei testrpc?

Alle Details unten. Lassen Sie mich wissen, ob Sie das Problem reproduzieren können, oder ob das gut funktioniert oder so.


Ich habe einen Vertrag A und einen Vertrag B, der davon erbt. Ich möchte einen Vertrag A in Vertrag B von einer bereits bereitgestellten Adresse des Vertrags A instanziieren und die Methoden dieser Instanz aufrufen.

Ich weiß, wie das geht (oder zumindest denke ich, dass ich es tue; vielleicht ist es nicht der richtige Weg, es zu tun?) Und es funktioniert. Das eigentliche Problem ist das Array von bytes32.

Kontext

Hier ist vor allem der Vertragscode.

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);
  }
}

Ein bisschen Kontext: stuffist ein hexadezimaler String (der 256 Bit = 32 Bytes sein kann (passt perfekt in ein bytes32) oder mehr (512 usw.); daher das Array bytes32[]).

Zuerst stelle ich einen Vertrag A (Rückgabe einer Instanz contractA) mit der Wallet-Adresse bereit anAddress. Dies gibt mir, wenn mein Vertrag bereitgestellt wird, die Smart-Contract-Adresse contractAaddress.

Der Aufruf addStuffmit einem Array von bytes32 funktioniert einwandfrei:

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

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

Hinweis - getBytes32Array

Ich habe eine Funktion, getBytes32Arraydie ein Array von Bytes32 (tatsächlich hexadezimale Zeichenfolgen) aus einer bestimmten hexadezimalen Zeichenfolge ausgibt:

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;
}

Dadurch kann ich beispielsweise einfach meinen Hex-String e0f89ca8eae95281590977802df657506a151304234d15570c12cc26263a8b7a2bf140bc9f80baa93879634717d9c2cf08cc6c96492c1b56c053ae54546f4f20(512 Bit) eingeben und erhalten, ["0xe0f89ca8eae95281590977802df657506a151304234d15570c12cc26263a8b7a","0x2bf140bc9f80baa93879634717d9c2cf08cc6c96492c1b56c053ae54546f4f20"]was als Bytes32-Array verstanden wird, das zwei Bytes32 enthält.


Auf zum eigentlichen Thema

Der Aufruf addStuffauf diese Weise funktioniert perfekt mit einem Array von Bytes32, das mehr als ein Byte32 enthält.

Zweitens stelle ich jetzt meinen Vertrag B (Rückgabe einer Instanz contractB) mit der Wallet-Adresse bereit anotherAddress. Dies gibt mir, wenn mein Vertrag bereitgestellt wird, die Smart-Contract-Adresse contractBaddress.

Was ich tun möchte, ist, die provideStuffMethode von Vertrag B aufzurufen und ihr die Adresse des bereitgestellten Vertrags A contractAaddressund die stuff, die den Vertrag A aus der Adresse instanziieren und die addStuffmit der stuffbereitgestellten aufrufen soll, zuzuführen. (siehe Vertrag B Soliditätscode)

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

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

Aber das gibt mir jedes Mal einen ungültigen Op-Code ... Trotz der Arbeit von "schätzenGas" (ist das nicht komisch?) ... Das funktioniert , wenn mein bytes32-Array nur ein bytes32 hat. Noch seltsamer, es funktioniert auch, wenn es ein zweites bytes32 hat, das eine sehr kurze Hex-Zeichenfolge ist ( siehe PS1 ). Aber ich kann kein konsistentes Verhalten mit einem Array von x Bytes32 bekommen, jeweils 32 Bytes (64 Zeichen) Hex-Strings (oder noch kürzer) ...

Vielen Dank im Voraus.

PS1: Das vorherige Beispiel funktioniert gut, wenn das Zeug ein 256-Bit-Hex-Wert oder etwas länger ist (ich habe es mit Hardcoding versucht ["0x256BitValueHere","0xverySmallValueHere"], 0xverySmallValueHerekann nicht länger als ein paar Zeichen sein (vielleicht 10, ich erinnere mich nicht), danach wird ungültige Op ausgelöst Codieren Sie erneut. Ich habe es auch mit mehreren kleinen Werten versucht ["0xverySmallValueHere", "0xverySmallValueHere", "0xverySmallValueHere", "0xverySmallValueHere"], es wird auch nicht funktionieren.

PS2: lokal mit testrpc arbeiten.


EDIT : Alles funktioniert perfekt in Remix. Es ist also kein Solidity-Code-Problem.

Antworten (2)

Sie verwechseln das "Instanziieren" von Vertrag A mit dem "Erben von" Vertrag A.

Vererbung ist eher wie Klassenbibliotheken, in denen B die Zustandsvariablen und Funktionen von A übernimmt. Es gibt kein separates A, mit dem man in dem Fall sprechen kann - nur contract B is AB, das zufällig Quellcode von A enthält.

Da Sie möchten, dass zwei Verträge miteinander kommunizieren, stellen Sie zuerst A bereit und sprechen Sie dann in B darüber. Die einfachste Methode besteht darin, dass die Quelldatei von B eine Kopie von A enthält, damit der Compiler sie „sehen“ und die ABI (Funktion) verstehen kann Zeichen). Es gibt keine B is A, aber Sie können eine Variable contract Awie folgt in den Typ "be" umwandeln:

A a;(so ähnlich uint x).

Nachdem nun der „Typ“ von „a“ festgelegt ist, ist der andere wichtige Faktor seine Adresse. Angenommen, Sie haben A bereits irgendwo bereitgestellt, sollten Sie seine Adresse kennen. Sie können das an den Konstruktor von B übergeben und die Instanziierung von "a" abschließen.

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

Großartig. Jetzt können Sie mit Nachrichten hin und her senden a.function(args).

Hier ist eine kleine Bastelei ähnlich wie bei dir:

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"
    }
}

Sie können in Remix herumspielen, um zu sehen, wie es funktioniert.

  1. Stellen Sie A bereit
  2. Kopieren Sie die Adresse von A
  3. Stellen Sie B bereit und fügen Sie A in den Konstruktor ein
  4. Probieren Sie B.test mit aus, ["0x1","0x2"]um ihm ein Array zu übergeben.

Ich hoffe es hilft.

Hallo Rob, erstmal danke für die schnelle Antwort. Es ist gut, den Unterschied zwischen Vererbung und Einsetzung zu präzisieren, aber ich habe diese Verwirrung nicht verursacht; es passiert einfach, dass mein Vertrag, von dem ich die Funktionen von Vertrag A aufrufen möchte, auch von ihm erbt. Ich werde meine Frage bearbeiten, um das klarer zu machen. Das Problem bleibt: Warum funktioniert es, wenn ich ein bytes32[] mit nur einem bytes32 übergebe und nicht mehr? Ist es ein Problem in meinem Solidity-Code (ich glaube nicht, die Funktion addStuff funktioniert gut), in meinem js-Code (hier derselbe, sogar schätztGas funktioniert) oder ist es ein Fehler in testrpc?
Hallo Rob, ich habe meine Frage bearbeitet, um klarer zu machen, was mein Problem ist. Ich habe Ihren Code in Remix ausprobiert und er funktioniert perfekt. Ich habe auch meins ausprobiert (musste lernen, Remix zu verwenden, habe noch nie etwas damit getestet, aber es ist eigentlich intelligent, das zu tun, haha) und alles funktioniert auch gut ... Das Problem muss dann in testrpc liegen, denke ich? (Ich bin schon früher auf seltsame Dinge gestoßen und habe von Leuten gelesen, die sich darüber beschwert haben, dass testrpc nicht mehr gewartet wird und einige Störungen hat.) Können Sie dieses Verhalten reproduzieren und mir sagen, ob es nur auf meiner Seite liegt? (Oder sagen Sie mir, wenn etwas anderes nicht stimmt)

Ich habe gerade Ihren Vertrag in Remix (Solidity Version 0.4.24) ausprobiert und es funktioniert gut. Ich kann Sachen per Vertragsmethode schreiben provideStuffund Bsie dann per Vertrag lesen A. Allerdings musste ich die folgende Methode in den Vertrag aufnehmen, Aum Sachen daraus zu lesen:

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

da der von Solidity generierte Standard-Getter keine dynamischen Arrays liest.