Являются ли символы подчеркивания `_` в коде модификаторов или они просто предназначены для того, чтобы выглядеть круто?

Я часто вижу _в модификаторах

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

Выполняет ли он какой-либо код или предназначен для облегчения чтения кода?

Ответы (1)

Обновление от 12 октября 2016 г.

Начиная с Solidity версии 0.4.0+, теперь вам нужно добавить точку с запятой после _. См. Solidity — версия 0.4.0 :

  • Изменить _на _;в модификаторах.

Приведенные ниже тесты работают только в Solidity < v 0.4.0.



Резюме

  • Код изменяемой функции вставляется там, где _в модификаторе стоит .
  • Вы можете добавить более одного _s в код модификатора. А код модифицируемой функции вставляется в каждое место, где _он находится в модификаторе. См modifier checkThree. . Это может быть предотвращено более поздними версиями solcкомпилятора.
  • Модификаторы вызываются в той последовательности, в которой они были определены ( checkOne checkTwo checkThree), а в конце функции они вызываются в обратном порядке. Модификаторы, кажется, применяются как стек. Во всяком случае, в этом примере.



Подробности

Из Solidity Features - Модификаторы функций :

Модификаторы PT можно использовать для простого изменения поведения функций, например, для автоматической проверки условия перед выполнением функции. Они являются наследуемыми свойствами контрактов и могут быть переопределены производными контрактами.

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


Вот пример с EtherScan.io — The DAO — Source Code .

Модификатор onlyTokenholdersограничивает выполнение «модифицированных» функций лицами, не являющимися владельцами токенов.

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

Вот vote(...)функция с onlyTokenHoldersмодификатором:

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

Код внутри vote(...)функции выполняется только в том случае, если проверка модификатора не выдает ошибку из оператора if (balanceOf(msg.sender) == 0) throw;. Представляет _тело vote(...)функции.


Из Learn X in Y минут — где X=Solidity , вот пример, где _нет в конце функции модификатора:

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


Возьмем '_' для пробного запуска

Вот код для тестирования _:

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

Сгладил код

> 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

Вставил контракт в блокчейн:

> 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]

Отправлена ​​транзакция для вызова test()функции:

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

Проверил результаты:

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


Вывод заявления о возврате в test():

Я удалил оператор возврата, поэтому исходный код test:

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

И повторно запустил тест, чтобы получить следующие результаты:

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: _ представляет тело функции voice(...). Представляет как в облегчении чтения? Я писал модификаторы без _, и это не похоже на «специальный символ», который что-то делает в EVM, это больше похоже на эстетическую вещь. Это как-то связано с тем, как работает код?
Даже с вашим длинным ответом я все еще не понимаю, для чего он используется... извините. Имеет ли это то же значение, что и в языке Swift?
@oIG, некоторые интересные результаты испытаний. @Nicolas Massart, как модификатор работает в Swift?
@dor Bokky ясно сказал: он сообщает функции, куда идти. Если ваш модификатор {action 1; _; action2;}, при вызове он выполнит действие 1, затем функцию, затем действие 2. Если вы его не укажете, я предполагаю, что подчеркивание неявно помещается в конец модификатора.
Это очень хороший и подробный ответ, и его следует принять. :(
Думаю modifierкак decoratorв питоне.
"throw" устарел в пользу "revert()"
Это позволило мне очень ясно понять, где вставляется код без модификаторов, как выполняется поток модификаторов и когда не использовать оператор возврата, если мы используем несколько модификаторов. Благодарю вас!
То, что я получил, сильно отличается от вашего, что может быть связано с различиями в версиях, но я вижу вашу точку зрения на иллюстрацию того, как «_» преобразуется в команды в стеке.
TL;ДР; Это причудливый способ объяснить обратный вызов.