TokenCreator/OwnedToken-Beispiel aus der Solidity-Dokumentation verstehen

Hier ist die Seite mit dem Beispiel https://solidity.readthedocs.io

Hier sind zwei Verträge, die zusammenarbeiten sollen (meine Fragen sind unten):

Geben Sie hier die Bildbeschreibung ein

Hier ist, wie ich denke, dass es funktioniert. Bitte korrigieren Sie meine Schritte, während ich versuche, Sie hindurchzuführen.

  1. Da es für eine Adresse einen Vertrag gibt, werden diese beiden Verträge an zwei verschiedenen Adressen bereitgestellt.

  2. In der Konstruktorfunktion von OwnedToken wird der Vertragserstellervariable ein Vertrag zugewiesen, der sich unter der Adresse msg.sender befindet . Über diese Variable ist es dann möglich, auf Funktionen dieses Vertrages zuzugreifen. Es lässt mich denken, dass der Ersteller dieses Vertrags ein anderer Vertrag sein muss ( TokenCreator ) .

  3. Dann schaue ich mir den Code des TokenCreator- Vertrags an, um herauszufinden, welche Funktion für die Erstellung des OwnedToken- Vertrags verantwortlich ist. Ich finde es in Zeile 43 .

  4. Jetzt bin ich ratlos. Wie funktioniert new OwnedToken(name) ? Wie sieht der TokenCreator- Vertrag OwnedToken ?

Außerdem habe ich gerade einen Compilerfehler entdeckt:

Geben Sie hier die Bildbeschreibung ein

Es ist so aufregend, sich durch all diese Dinge zu pflügen! Danke, du bist großartig!


EDIT: Einige Änderungen vorgenommen, und es macht jetzt mehr SinnGeben Sie hier die Bildbeschreibung ein

Antworten (1)

Ersetzen Sie den Code, in dem Sie den Fehler gefunden haben, durch:

function changeName(bytes32 newName) {
    // Only the creator can alter the name --
    // the comparison is possible since contracts
    // are implicitly convertible to addresses.
    if (msg.sender == address(creator))
        name = newName;
}

und es wird im Solidity Online Compiler und im solcCompiler via kompiliert geth.


Q1 Da es für eine Adresse einen Vertrag gibt, werden diese beiden Verträge an zwei verschiedenen Adressen bereitgestellt.

Sie können beide Verträge separat kompilieren und bereitstellen – dies würde einige Änderungen am Code erfordern. Sehen Sie sich die ClassicCheck contractund die SafeConditionalHFTransferVerträge an, die sich auf ClassicCheckdie Adresse beziehen, in Wie man Ether bedingt an ein anderes Konto nach einer Hard-Fork sendet, um sich vor Replay-Angriffen zu schützen .

Aber dieses Beispiel wurde für Sie geschrieben, um sie zusammen zu kompilieren. Sie stellen TokenCreatoran eine Vertragsadresse bereit.

Wenn Sie aufrufen TokenCreator.createToken(...), wird ein neuer OwnedToken(...)Vertrag erstellt und die Vertragsadresse zurückgegeben. Diese Adresse müssen Sie speichern, da der TokenCreatorVertrag sie nicht speichert.


F2 In der Konstruktorfunktion der OwnedToken -Vertragserstellervariable wird ein Vertrag zugewiesen, der sich unter der Adresse msg.sender befindet. Über diese Variable ist es dann möglich, auf Funktionen dieses Vertrages zuzugreifen. Es lässt mich denken, dass der Ersteller dieses Vertrags ein anderer Vertrag sein muss (TokenCreator)

Ja.


F3 Ich schaue mir dann den Code des TokenCreator-Vertrags an, um herauszufinden, welche Funktion für die Erstellung des OwnedToken-Vertrags verantwortlich ist. Ich finde es in Zeile 43.

Und es ist #50, das den OwnedTokenVertrag erstellt.


Q4 Jetzt bin ich ratlos. Wie funktioniert new OwnedToken(name)? Wie sieht der TokenCreator-Vertrag OwnedToken?

new OwnedToken(name)erstellt den neuen OwnedTokenVertrag. Die createToken(...)Funktion gibt dann die Adresse zurück, an OwnedToken(...)der sie bereitgestellt wurde.


Notiz

Es gibt ein Problem mit dem Beispielcode, da nicht konstante Funktionen nicht ohne weiteres Werte zurückgeben können. Sie müssen ein Ereignis generieren, um die Token-Erstellungsadresse anzuzeigen. Siehe So erhalten Sie Werte, die von nicht konstanten Transaktionsfunktionen zurückgegeben werden? für mehr Details.

Oder Sie können den Vertrag ändern TokenCreator, um die erstellte Vertragsadresse zu speichern, wie in How to get return values ​​when function with argument is called? wo das Ergebnis gespeichert und von der answer.getNumResult("idOne")konstanten Funktion aufgerufen wurde.


TokenCreatorspeichert den Rückgabewert des createToken(...)Aufrufs nicht und weiß daher nicht, wo OwnedTokenes bereitgestellt wurde.

Wenn Sie anrufen changeName(...), müssen Sie die Adresse angeben, an der Sie OwnedTokenbereitgestellt wurden.



Nehmen wir einen modifizierten TokenCreatorfür einen Testlauf

Hier ist ein geänderter TokenCreatorVertrag gespeichert in TokenCreator.sol:

contract OwnedToken {
    // TokenCreator is a contract type that is defined below.
    // It is fine to reference it as long as it is not used
    // to create a new contract.
    TokenCreator public creator;
    address public owner;
    string public name;

    // This is the constructor which registers the
    // creator and the assigned name.
    function OwnedToken(string _name) {
        owner = msg.sender;
        // We do an explicit type conversion from `address`
        // to `TokenCreator` and assume that the type of
        // the calling contract is TokenCreator, there is
        // no real way to check that.
        creator = TokenCreator(msg.sender);
        name = _name;
    }

    function changeName(string newName) {
        // Only the creator can alter the name --
        // the comparison is possible since contracts
        // are implicitly convertible to addresses.
        if (msg.sender == address(creator))
            name = newName;
    }

    function transfer(address newOwner) {
        // Only the current owner can transfer the token.
        if (msg.sender != owner) 
            return;
        // We also want to ask the creator if the transfer
        // is fine. Note that this calls a function of the
        // contract defined below. If the call fails (e.g.
        // due to out-of-gas), the execution here stops
        // immediately.
        if (creator.isTokenTransferOK(owner, newOwner))
            owner = newOwner;
    }
}

contract TokenCreator {

    mapping(string => address) addresses;

    function getAddress(string name) constant returns (address) {
        return addresses[name];
    }

    function createToken(string name)
       returns (OwnedToken tokenAddress)
    {
        // Create a new Token contract and return its address.
        // From the JavaScript side, the return type is simply
        // "address", as this is the closest type available in
        // the ABI.
        tokenAddress = new OwnedToken(name);
        addresses[name] = tokenAddress;
    }

    function changeName(string oldName, string newName) {
        // Again, the external type of "tokenAddress" is
        // simply "address".
        address tokenAddress = addresses[oldName];
        delete addresses[oldName];
        addresses[newName] = tokenAddress;
        OwnedToken(tokenAddress).changeName(newName);
    }

    function isTokenTransferOK(
        address currentOwner,
        address newOwner
    ) returns (bool ok) {
        // Check some arbitrary condition.
        address tokenAddress = msg.sender;
        return (sha3(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff);
    }
}

Mit dem stripCrLfSkript in How to load Solidity source file into geth habe ich den Quellcode auf Folgendes reduziert:

user@Kumquat:~$ echo "var tokenCreatorSource='`stripCrLf TokenCreator.sol`'"
var tokenCreatorSource='contract OwnedToken {    TokenCreator public creator; address public owner; string public name;   function OwnedToken(string _name) { owner = msg.sender;     creator = TokenCreator(msg.sender); name = _name; } function changeName(string newName) {    if (msg.sender == address(creator)) name = newName; } function transfer(address newOwner) {  if (msg.sender != owner)  return;      if (creator.isTokenTransferOK(owner, newOwner)) owner = newOwner; }}contract TokenCreator { mapping(string => address) addresses; function getAddress(string name) constant returns (address) { return addresses[name]; } function createToken(string name) returns (OwnedToken tokenAddress) {     tokenAddress = new OwnedToken(name); addresses[name] = tokenAddress; } function changeName(string oldName, string newName) {   address tokenAddress = addresses[oldName]; delete addresses[oldName]; addresses[newName] = tokenAddress; OwnedToken(tokenAddress).changeName(newName); } function isTokenTransferOK( address currentOwner, address newOwner ) returns (bool ok) {  address tokenAddress = msg.sender; return (sha3(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff); }}'

Ich betreibe ein Entwicklungsnetzwerk mit dem folgenden Befehl (ich habe mein Passwort in passwordfile):

geth --datadir ~/devdata --dev --mine --minerthreads 1 --unlock 0 --password ~/passwordfile console

Ich füge den abgeflachten Code in die gethBefehlszeile ein:

> var tokenCreatorSource='contract OwnedToken {    TokenCreator public creator; address public owner; string public name;   function OwnedToken(string _name) { owner = msg.sender;     creator = TokenCreator(msg.sender); name = _name; } function changeName(string newName) {    if (msg.sender == address(creator)) name = newName; } function transfer(address newOwner) {  if (msg.sender != owner)  return;      if (creator.isTokenTransferOK(owner, newOwner)) owner = newOwner; }}contract TokenCreator { mapping(string => address) addresses; function getAddress(string name) constant returns (address) { return addresses[name]; } function createToken(string name) returns (OwnedToken tokenAddress) {     tokenAddress = new OwnedToken(name); addresses[name] = tokenAddress; } function changeName(string oldName, string newName) {   address tokenAddress = addresses[oldName]; delete addresses[oldName]; addresses[newName] = tokenAddress; OwnedToken(tokenAddress).changeName(newName); } function isTokenTransferOK( address currentOwner, address newOwner ) returns (bool ok) {  address tokenAddress = msg.sender; return (sha3(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff); }}'

Und kompilieren Sie den Code mit dem folgenden Befehl:

> var tokenCreatorCompiled = web3.eth.compile.solidity(tokenCreatorSource);
Version: 0.3.5-0/RelWithDebInfo-Linux/g++/Interpreter

path: /usr/bin/solc
undefined

Sie können die generierte ABI sehen:

> tokenCreatorCompiled.TokenCreator.info.abiDefinition
[{
    constant: false,
    inputs: [{
        name: "name",
        type: "string"
    }],
    name: "createToken",
    outputs: [{
        name: "tokenAddress",
        type: "address"
    }],
    type: "function"
}, {
    constant: false,
    inputs: [{
        name: "oldName",
        type: "string"
    }, {
        name: "newName",
        type: "string"
    }],
    name: "changeName",
    outputs: [],
    type: "function"
}, {
    constant: true,
    inputs: [{
        name: "name",
        type: "string"
    }],
    name: "getAddress",
    outputs: [{
        name: "",
        type: "address"
    }],
    type: "function"
}, {
    constant: false,
    inputs: [{
        name: "currentOwner",
        type: "address"
    }, {
        name: "newOwner",
        type: "address"
    }],
    name: "isTokenTransferOK",
    outputs: [{
        name: "ok",
        type: "bool"
    }],
    type: "function"
}]

> tokenCreatorCompiled.OwnedToken.info.abiDefinition
[{
    constant: true,
    inputs: [],
    name: "creator",
    outputs: [{
        name: "",
        type: "address"
    }],
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "name",
    outputs: [{
        name: "",
        type: "string"
    }],
    type: "function"
}, {
    constant: false,
    inputs: [{
        name: "newOwner",
        type: "address"
    }],
    name: "transfer",
    outputs: [],
    type: "function"
}, {
    constant: false,
    inputs: [{
        name: "newName",
        type: "string"
    }],
    name: "changeName",
    outputs: [],
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "owner",
    outputs: [{
        name: "",
        type: "address"
    }],
    type: "function"
}, {
    inputs: [{
        name: "_name",
        type: "string"
    }],
    type: "constructor"
}]

Erstellen Sie den Vertrag auf der Blockchain:

> var tokenCreatorContract = web3.eth.contract(tokenCreatorCompiled.TokenCreator.info.abiDefinition);
undefined
> var tokenCreator = tokenCreatorContract.new({
    from:web3.eth.accounts[0], 
    data: tokenCreatorCompiled.TokenCreator.code, gas: 1000000}, 
    function(e, contract) {
      if (!e) {
        if (!contract.address) {
          console.log("Contract transaction send: TransactionHash: " + 
            contract.transactionHash + " waiting to be mined...");
        } else {
          console.log("Contract mined! Address: " + contract.address);
          console.log(contract);
        }
    }
})
...
Contract mined! Address: 0xfdb30244a9d9b8e98e9dd57ac81728725830f54b

Erstellen Sie die BokkyPooBahTokenmit dem folgenden Befehl:

> tokenCreator.createToken("BokkyPooBahToken", {from:eth.accounts[0]})
"0xc55cc14b02bd0ca783dc7457bd5f09263fb5750135e7f7e4e2c070f6dd855785"

Sehen wir uns die Adresse des erstellten Tokens an:

> var tokenAddress = tokenCreator.getAddress("BokkyPooBahToken")

> tokenAddress
"0xcd347e1c299b02bba605f74e06031e7d170a1aed"

Sehen wir uns die Details des erstellten Tokens an:

> var token = eth.contract(tokenCreatorCompiled.OwnedToken.info.abiDefinition).at(tokenAddress);
undefined
> token.owner()
"0xfdb30244a9d9b8e98e9dd57ac81728725830f54b"
// Above is the TokenCreator contract address
> token.name()
"BokkyPooBahToken"

Lassen Sie uns den Namen des Tokens ändern:

> tokenCreator.changeName("BokkyPooBahToken", "BoppyKooBahToken", {from: eth.accounts[0]})
"0xa68045d36f313bad2abed292e86729ac9cf94c12a368b4d73a78f4a2826edf32"

> token.name()
"BoppyKooBahToken"
Der mächtige @BokkyPooBah! Eine Milliarde Dank an Sie für Ihre Zeit und Bemühungen! Jetzt ist alles klar, bis auf eine Kleinigkeit...
In Zeile 12 ... Ist msg.sender die Adresse des TokenCreator- Vertrags oder die Adresse des Benutzers, der TokenCreator.createToken(...) aufgerufen hat ? Wenn ersteres zutrifft, wird Zeile 31 immer zurückkehren und niemand könnte Geld überweisen, außer dem TokenCreator- Vertrag. Es tut mir wirklich leid, dass ich so ein Dummkopf bin!
In Zeile 12 ist msg.sender das Konto des Benutzers, der angerufen hat TokenCreator.createToken(...). Dieses Konto ist das einzige autorisierte Konto, um anzurufen changeName(...)und transfer(...). Nach der Übertragung ist das OwnedTokenEigentum des Kontos, auf das der ursprüngliche Eigentümer übertragen hat. Entschuldigen Sie sich nicht – ich habe einige Zeit mit Solidity und der Ethereum-Blockchain verbracht und lerne jeden Tag mehr dazu. Dieses Beispiel ist etwas kompliziert, zumal man den Rückgabewert nicht so einfach aus bekommen kann TokenCreator.createToken(...).
Vielleicht möchten Sie das Greeter- Beispiel durchsehen und sich einige andere Beispiele ansehen .
alles macht Sinn, außer Zeile 17. Wenn im OwnedToken-Konstruktor msg.sender die Adresse des Kontos des Benutzers (EOA) ist, dann wird der Creator -Variablen nichts zugewiesen, da es keinen Vertragscode für diese zu importierende Adresse gibt . Irgendwo muss tx.origin sein ...
@EugeneEpifanov, habe ich ein Schritt-für-Schritt-Beispiel für die Bereitstellung und Ausführung der Funktionen eines modifizierten TokenCreator-Vertrags hinzugefügt. Die Modifikationen dienen dazu, die erstellte Token-Adresse für eine spätere Verwendung zu speichern.
Ich schwöre bei Gott, du bist der Mann!!! Sie sind das, was Ethereum großartig macht! Alles macht Sinn, aber ... Dann gibt es keine Möglichkeit, den Token zu übertragen. Sie können tokenCreator.transfer() nicht aufrufen, da es im tokenCreator - Vertrag keine solche Funktion gibt. Sie können OwnedToken.transfer() jedoch direkt von Ihrer Kontoadresse aufrufen, aber die if -Anweisung würde einen Fehler auslösen. Ich habe dem Code in meiner Frage einige Änderungen hinzugefügt, und der Vertrag scheint jetzt logischer zu sein. Bitte schau es dir an!
Paging Sie, falls Sie neuen Code verpasst haben. Ich habe es später hinzugefügt, als ich die vorherige Nachricht hier gepostet habe ...