Kann Vertrags-zu-Vertrags-Funktionsaufrufe nicht zum Laufen bringen

Ich kann ein Problem nicht ganz herausfinden, das verhindert, dass Contract-to-Contract-Funktionsaufrufe ausgeführt werden.
Ich bin ziemlich zuversichtlich, dass es etwas mit meiner Bereitstellung zu tun hat.

Vorwort: Ich habe einen Parentund- ChildVertrag, wo das Kind Anrufe der Eltern aufrufen kann. Das untergeordnete Element enthält eine Adresse des übergeordneten Elements, die nach der Bereitstellung festgelegt wird und aktualisierbar sein sollte, falls ein neuer ParentVertrag bereitgestellt wird.

Verträge:

IParent.sol
pragma solidity >0.4.23 <0.5.0;

interface IParent {
    function setValue(int v) external;
    function getValue() external view returns (int);                                                         
    function getSender() external view returns (address);
}
Parent.sol
pragma solidity >0.4.23 <0.5.0;

import "./IParent.sol";

contract Parent is IParent {
    int value;

    function setValue(int v) public {
        value = v;
    }

    function getValue() external view returns (int) {
        return value;
    }

    function getSender() external view returns (address) {
        return msg.sender;
    }
}
Kind.sol
pragma solidity >0.4.23 <0.5.0;

import "./IParent.sol";

contract Child {
    IParent parent;

    function setParent(address a) public {
        parent = IParent(a);
    }

    function getValue() external view returns (int value) {
        return parent.getValue();
    }

    function getSender() external view returns (address value) {
        return parent.getSender();
    }
}

Schritte

(Ich verwende Mist, um die Bereitstellung durchzuführen, um Probleme zu vermeiden, die ich möglicherweise eingeführt habe, und versuche, das Problem zu isolieren. Ich erhalte auch das gleiche Ergebnis, wenn ich web3js bereitstelle.)

  1. Verträge zusammenstellen:

    solc --bin Child.sol Parent.sol

  2. Stellen Sie die Verträge bereit

    • Kopieren Sie den Binary:Abschnitt für Child.solin den Abschnitt „Vertragsbereitstellung“ von Mist CONTRACT BYTE CODEund stellen Sie ihn bereit
    • Wiederholen Sie für dieParent.sol
  3. Holen Sie sich das ABI:

    solc --abi Child.sol Parent.sol

  4. Sehen Sie sich die Verträge an:

    • Kopieren Sie den Contract JSON ABIAbschnitt für zusammen mit der entsprechenden Adresse Child.solin Mist'sWATCH CONTRACT
    • Wiederholen Sie fürParent.sol
  5. Verknüpfen Sie die Verträge

    • Wählen Sie den ChildVertrag aus
    • Führen Sie die Funktion setParentmit der Adresse des übergeordneten Vertrags aus
  6. Legen Sie den Wert in Parent fest:

    • Wählen Sie den ParentVertrag aus
    • Führen Sie die Funktion setValuemit 999 oder etwas anderem als 0 aus

Wenn Sie von hier aus den ChildVertrag anzeigen, spiegeln sowohl die Werte valueals auch msg.senderdie Standardwerte wider.

Ich habe auch Folgendes versucht, innerhalb der Child:

function getValueSuccess() external view returns (bool) {
    return parentAddress.call(bytes4(keccak256("getValue()")));
}

Dies kehrt zurück true, aber das Folgende kehrt immer zurück 0:

function getDummyValue() external view returns (int) {                                                       
    parent.getValue();                                                                                   
    return 999;                                                                                          
} 

Verwenden Sie auch die folgenden Montagearbeiten:

function getValue() external view returns (int value) {
    address addr = address(parent);
    bytes4 sig = bytes4(keccak256("getValue()"));

    assembly {
       let o := mload(0x40) // Empty storage pointer
       mstore(o,sig)        // Push function signature to memory (function signature is 4 bytes/0x04)
       //mstore(add(ptr,0x40), someInt32Argument); // Append function argument after signature
       // From here, the call data size (input) would be functiona signature size + sum(argument size)
       //   4bytes + 0 in this case, or 4bytes + 32bytes in the above commented `mstore`

       let success := call(
           15000,           // Gas limit
           addr,            // To address
           0,               // No ether to transfer
           o,               // Input location ptr
           0x04,            // Input size (0)
           o,               // Store oputput over input
           0x20)            // Output size (32 bytes)

       value := mload(o)
       mstore(0x40,add(o,0x04))
    }
}

Es scheint, als würde die Umwandlung von der übergeordneten Adresse in IParentnicht funktionieren, da alle Aufrufe fehlzuschlagen scheinen, es sei denn, wir verwenden Assembly oder address.call(), von denen ich annehme, dass sie nur die Nachricht an die Adresse senden und die Typbeschränkung vermeiden (wie die Verwendung von Reflektion).

Wenn ich einfach alle Verträge in einer einzigen Datei zusammenführe, kopiere und füge diese in Mists SOLIDITY CONTRACT SOURCE CODEBox ein und stelle jeden von ihnen aus der Dropdown-Liste bereit. Alles funktioniert wie erwartet.

Welche Schritte fehlen mir oben, damit dies wie erwartet funktioniert?


Aktualisierung 1

Wenn ich einen funktionierenden Satz von Verträgen bereitstelle, Mist verwende, um den Quellcode bereitzustellen, und dann die ParentBinärdatei in Mist bereitstelle, kann ich sie erfolgreich vom Kind aus verknüpfen/aufrufen.
Es sieht also nach etwas Speziellem mit dem ChildDeployment aus.

Aktualisierung 2

Dies funktioniert nur mit obigem Testvertrag. Nachdem ich dies in meinen eigentlichen Vertrag verschoben habe, stehe ich vor dem gleichen Problem

Ich bin auf diese Frage gestoßen , wo @smarx geantwortet hatte mit:

FYI, Sie können dies stattdessen tun (und es von einem Client auf die gleiche Weise aufrufen):
NonFungibleToken public nft;

Funktion setNFTAddress (NonFungibleToken _nft) onlyOwner{
nft = _nft;
}

Aus irgendeinem Grund scheint dies zu funktionieren, während das einfache Übertragen der Adresse in den Vertrag nicht funktioniert?

Warum sollte das funktionieren:

function setParent(Parent p) external {
    parent = p;
}

aber nicht das:

function setParent(address addr) external {
    parent = Parent(addr);
}

... und noch verwirrender, warum sollten sie beide funktionieren, wenn sie mit dem Quellcode in Mist bereitgestellt werden, und nur die erste Arbeit, wenn sie die Binärdateien verwenden?

Versionen:

  • geth: 1.8.13-stabil
  • solc: 0.4.24+commit.e67f0147.Linux.g++ (Docker-Image: ethereum/solc:stable)
  • Nebel: 0.10.0
Haben Sie dies in einem öffentlichen Netzwerk (Mainnet, Ropsten usw.) bereitgestellt? Wenn ja, verlinken Sie bitte auf die Verträge (Eltern und Kind).
Die Tatsache, dass dies funktioniert, wenn Sie in Mist kompilieren, aber nicht, wenn Sie nur den Bytecode einfügen, deutet darauf hin, dass es möglicherweise ein Problem mit dem letzteren Workflow gibt. (Möglicherweise funktioniert die Kompilierung nicht oder Sie verwenden den falschen Bytecode oder Mist ist bei der Verwendung von Bytecode defekt.) Ich habe kein Mist, daher kann ich keine Repro versuchen.
Sie können die Verfolgung aller ausgeführten Anweisungen aktivieren, vielleicht würde dies helfen
@smarx, nur eine lokale/private Instanz. Ich habe ursprünglich mit web3js bereitgestellt, aber am Ende habe ich Mist verwendet, um alles auszuschließen, was ich mit meiner js.
Scheint vertragsspezifisch zu sein Child. Stellen Sie die Verträge einfach erneut bereit, indem Sie die Quellcodemethode in Mist anstelle von Binärdateien verwenden. Verknüpfen Sie dann das untergeordnete Element mit einem der vorherigen übergeordneten Verträge, der mithilfe der binären Methode bereitgestellt wurde. Das scheint zu funktionieren, es sind nur alle ChildVerträge, die mit der binären Methode bereitgestellt wurden.
Für Funzies habe ich auch versucht solcjs, . Gleiches Ergebnis, no go.
Die Lösung, die Sie gefunden haben, ist mit ziemlicher Sicherheit nicht das eigentliche Problem (dasselbe gilt für die Verwendung von Assembly). Diese beeinflussen wahrscheinlich nur etwas anderes (wie Länge oder Aufbau des Bytecodes). Ich stelle mir vor, dass eine andere bedeutungslose Änderung auch Dinge "reparieren" könnte.
Ich habe auch einige andere Dummy-Methoden hinzugefügt, um Dinge zu testen. Es scheint, als würde die Besetzung versagen Parent(addr). Ich hatte eine Ansichtsfunktion wie hinzugefügt function testCast(address p) returns (int) { Parrent parent = Parent(p); parent.setValue(9); return 1; }. Wenn diese Funktion aufgerufen wird, 0ist immer der Rückgabewert.
... auch, wenn ich sowohl die nicht funktionierende getValueFunktion als auch die Assembly mit dem Namen einbeziehe, getValueAssemblyfunktioniert eine und die andere nicht.

Antworten (1)

Aktivieren Sie neue EVM-Funktionen, indem Sie Folgendes Byzantiumzum configAbschnitt Ihrer hinzufügen genesis.json:

  "config": {
    "chainId": 1234,
    "homesteadBlock": 0,
    "eip155Block": 0,
    "eip158Block": 0,
    "eip160Block": 0,
    "byzantiumBlock": 0
},

Danke an @chriseth von Solidity für die Hilfe bei der Suche