Sind Unterstriche `_` im Modifikatorcode oder sollen sie nur cool aussehen?

Ich sehe oft _in Modifikatoren

modifier onlyOwner() {
    if (msg.sender != owner) throw;
    _
}

Führt es irgendeinen Code aus oder soll es den Code leichter lesbar machen?

Antworten (1)

Aktualisierung 12. Oktober 2016

Ab Solidity-Version 0.4.0+ müssen Sie jetzt ein Semikolon nach hinzufügen _. Siehe Solidity - Version 0.4.0 :

  • Wechseln Sie _zu _;in Modifikatoren.

Die folgenden Tests funktionieren nur in Solidity < v 0.4.0.



Zusammenfassung

  • Der Code für die geänderte Funktion wird dort eingefügt, wo _im Modifikator steht.
  • _Sie können dem Modifikatorcode mehr als ein s hinzufügen . Und der Code der zu ändernden Funktion wird an jeder Stelle eingefügt, an _der sich der Modifikator befindet. Siehe modifier checkThree. Dies kann durch spätere Versionen des solcCompilers verhindert werden.
  • Die Modifikatoren werden in der Reihenfolge aufgerufen, in der sie definiert wurden ( checkOne checkTwo checkThree) und am Ende der Funktion werden sie umgekehrt aufgerufen. Die Modifikatoren scheinen wie ein Stapel angewendet zu werden. In diesem Beispiel sowieso.



Einzelheiten

Aus Solidity Features - Funktionsmodifikatoren :

PT-Modifikatoren können verwendet werden, um das Verhalten von Funktionen einfach zu ändern, beispielsweise um eine Bedingung vor dem Ausführen der Funktion automatisch zu überprüfen. Sie sind vererbbare Eigenschaften von Verträgen und können durch abgeleitete Verträge außer Kraft gesetzt werden.

contract owned {
  function owned() { owner = msg.sender; }
  address owner;

  // This contract only defines a modifier but does not use it - it will
  // be used in derived contracts.
  // The function body is inserted where the special symbol "_" in the
  // definition of a modifier appears.
  modifier onlyowner { if (msg.sender == owner) _ }
}


Hier ist ein Beispiel von EtherScan.io - The DAO - Source Code .

Der Modifikator onlyTokenholdersverhindert, dass "modifizierte" Funktionen von Nicht-Token-Inhabern ausgeführt werden.

modifier onlyTokenholders {
    if (balanceOf(msg.sender) == 0) throw;
        _
}

Hier ist die vote(...)Funktion mit dem onlyTokenHoldersModifikator:

function vote(
    uint _proposalID,
    bool _supportsProposal
) onlyTokenholders noEther returns (uint _voteID) {

    Proposal p = proposals[_proposalID];
    if (p.votedYes[msg.sender]
        || p.votedNo[msg.sender]
        || now >= p.votingDeadline) {

        throw;
    }

Der Code innerhalb der vote(...)Funktion wird nur ausgeführt, wenn die Modifikatorprüfung keinen Fehler aus der Anweisung auswirft if (balanceOf(msg.sender) == 0) throw;. Das _repräsentiert den Hauptteil der vote(...)Funktion.


Aus Learn X in Y minutes - Where X=Solidity , hier ist ein Beispiel, in dem _das nicht am Ende der Modifikatorfunktion steht:

// underscore can be included before end of body,
// but explicitly returning will skip, so use carefully
modifier checkValue(uint amount) {
    _
    if (msg.value > amount) {
        msg.sender.send(amount - msg.value);
    }
}


Nehmen wir '_' für einen Testlauf

Hier ist ein Code zum Testen _:

contract TestModifier {

    string[] public messages;
    uint256 testVariable;

    function numberOfMessages() constant returns (uint256) {
        return messages.length;
    }

    modifier checkOne {
        messages.push("checkOne - 1");
        if (testVariable == 123) 
            throw;
        _
        messages.push("checkOne - 2");
        if (testVariable == 123) 
            throw;
    }

    modifier checkTwo {
        messages.push("checkTwo - 1");
        if (testVariable == 123) 
            throw;
        _
        messages.push("checkTwo - 2");
        if (testVariable == 123) 
            throw;
    }

    modifier checkThree {
        messages.push("checkThree - 1");
        if (testVariable == 123) 
            throw;
        _
        messages.push("checkThree - 2");
        if (testVariable == 123) 
            throw;
        _
        messages.push("checkThree - 3");
        if (testVariable == 123) 
            throw;
    }

    function test() checkOne checkTwo checkThree returns (uint256) {
        messages.push("test - 1");
        testVariable = 345;
        messages.push("test - 2");
        return testVariable;
    }
}

Code abgeflacht

> var testModifierSource='contract TestModifier { string[] public messages; uint256 testVariable; function numberOfMessages() constant returns (uint256) { return messages.length; } modifier checkOne { messages.push("checkOne - 1"); if (testVariable == 123)  throw; _ messages.push("checkOne - 2"); if (testVariable == 123)  throw; } modifier checkTwo { messages.push("checkTwo - 1"); if (testVariable == 123)  throw; _ messages.push("checkTwo - 2"); if (testVariable == 123)  throw; } modifier checkThree { messages.push("checkThree - 1"); if (testVariable == 123)  throw; _ messages.push("checkThree - 2"); if (testVariable == 123)  throw; _ messages.push("checkThree - 3"); if (testVariable == 123)  throw; } function test() checkOne checkTwo checkThree returns (uint256) { messages.push("test - 1"); testVariable = 345; messages.push("test - 2"); return testVariable; }}'
undefined

Vertrag in die Blockchain eingefügt:

> var testModifierCompiled = web3.eth.compile.solidity(testModifierSource);
undefined
> var testModifierContract = web3.eth.contract(testModifierCompiled.TestModifier.info.abiDefinition);
var testModifier = testModifierContract.new({
    from:web3.eth.accounts[0], 
    data: testModifierCompiled.TestModifier.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: 0xd2ca2d34da6e50d28407f78ded3a07962b56181c
[object Object]

Transaktion zum Aufrufen der test()Funktion gesendet:

> testModifier.test(eth.accounts[0], {
  from:web3.eth.accounts[0], 
  data: testModifierCompiled.TestModifier.code,
  gas: 1000000
});

Ergebnisse überprüft:

> var i;
> for (i = 0; i < testModifier.numberOfMessages(); i++) {
    console.log(testModifier.messages(i));
}
checkOne - 1
checkTwo - 1
checkThree - 1
test - 1
test - 2


Rücksendeerklärung herausnehmen in test():

Ich habe die return-Anweisung entfernt, sodass der Quellcode für Folgendes testlautet:

function test() checkOne checkTwo checkThree returns (uint256) {
    messages.push("test - 1");
    testVariable = 345;
    messages.push("test - 2");
    // return testVariable;
}

Führen Sie den Test erneut aus, um die folgenden Ergebnisse zu erzielen:

var i;
undefined
> for (i = 0; i < testModifier.numberOfMessages(); i++) {
..     console.log(testModifier.messages(i));
.. }
checkOne - 1
checkTwo - 1
checkThree - 1
test - 1
test - 2
checkThree - 2
test - 1
test - 2
checkThree - 3
checkTwo - 2
checkOne - 2
undefined
re: Das _ repräsentiert den Hauptteil der Funktion vote(...). Stellt dar, wie es leichter zu lesen ist ? Ich habe Modifikatoren ohne das _ geschrieben und es scheint kein "spezielles Symbol" zu sein, das irgendetwas im EVM tut, es scheint eher eine ästhetische Sache zu sein. Hat es etwas damit zu tun, wie der Code ausgeführt wird?
Trotz Ihrer langen Antwort verstehe ich immer noch nicht, wofür es verwendet wird ... Entschuldigung. Hat es die gleiche Bedeutung wie in der Swift-Sprache?
@oIG, einige interessante Testergebnisse. @Nicolas Massart, wie funktioniert der Modifikator in Swift?
@dor Bokky hat es klar gesagt: Es sagt der Funktion, wohin sie gehen soll. Wenn Ihr Modifikator ist {action 1; _; action2;}, führt er beim Aufrufen Aktion 1 aus, dann die Funktion, dann Aktion 2. Wenn Sie ihn nicht angeben, gehe ich davon aus, dass der Unterstrich implizit am Ende des Modifikators platziert wird.
Dies ist eine sehr gute und ausführliche Antwort und sollte akzeptiert werden. :(
Ich denke modifierwie decoratorin Python.
"throw" wird zugunsten von "revert()" verworfen
Dadurch wurde mir klar, wo der Nicht-Modifizierer-Code eingefügt wird, wie der Fluss der Modifikatoren ausgeführt wird und wann die return-Anweisung nicht verwendet werden sollte, wenn wir mehrere Modifikatoren verwenden. Vielen Dank!
Was ich bekommen habe, unterscheidet sich stark von Ihnen, was möglicherweise auf Versionsunterschiede zurückzuführen ist, aber ich sehe Ihren Sinn darin, zu veranschaulichen, wie "_" in Befehle auf dem Stapel übersetzt wird.
TL;DR; Das ist eine schicke Art, einen Rückruf zu erklären.