В нашем четвертом уроке мы познакомимся с последними темами перед тем как приступить к написанию нашей собственной монеты! Темы этого урока — наследование и модификаторы.
Предыдущий урок можно посмотреть тут.Полный список уроков тут.
Представьте что вам потребовалось сменить адрес кошелька. Тогда вам придется и поменять адрес владельца визитки. А для этого нам потребуется дописать одну функцию. И чтобы ее не мог вызвать кто попало мы вначале ее кода сделаем проверку на адрес владельца.
1 2 3 4 |
function transferOwnership(address newOwner) public { require(msg.sender == owner); owner = newOwner; } |
Теперь весь код нашей визитки выглядит так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
pragma solidity ^0.4.18; contract BusinessCard { mapping (bytes32 => string) data; address owner; function BusinessCard() public { owner = msg.sender; } function setData(string key, string value) public { require(msg.sender == owner); data[keccak256(key)] = value; } function transferOwnership(address newOwner) public { require(msg.sender == owner); owner = newOwner; } function getData(string key) public constant returns(string) { return data[keccak256(key)]; } } |
У нас уже есть две функции, которые содержат одинаковый код проверки владельца. Сейчас у нас контракт довольно простой. Но на практике контракты значительно больше. Поэтому, возможно, в дальнейшем у нас будет больше функций с проверкой владельца. Чтобы каждый раз не писать эту проверку проще было бы вынести этот код куда-нибудь в одно место. Для этого используют модификаторы.
1 2 3 4 |
modifier имя_модификатора() { код_модификатора _; } |
Чтобы к функции применить модификатор нужно после скобок поставить имя модификатора
1 2 3 |
function имя_функции() public имя_модификатора { ... } |
Если в заголовке функции стоит модификатор, то функция выполняется внутри кода модификатора на месте где стоит символ «_».
Посмотрим как использовать модификатор на примере нашей визитки:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
pragma solidity ^0.4.18; contract BusinessCard { mapping (bytes32 => string) data; address owner; function BusinessCard() public { owner = msg.sender; } modifier onlyOwner() { require(msg.sender == owner); _; } function setData(string key, string value) public onlyOwner { data[keccak256(key)] = value; } function transferOwnership(address newOwner) public onlyOwner { owner = newOwner; } function getData(string key) public constant returns(string) { return data[keccak256(key)]; } } |
Мы видим что из функций setData и transferOwnership исчезла проверка на владельца. Но в заголовке у них появилось имя модификатора onlyOwner. Давайте посмотрим на код onlyOwner.
1 2 3 4 |
modifier onlyOwner() public { require(msg.sender == owner); _; } |
Он у нас состоит из двух строк. В первой проверяется является ли исполнитель владельцем. Если не является, то мы выйдем из функции. А если является то будет исполнена вторая строчка, в которой вместо магического символа «_» будет исполнен код функции которая сейчас выполняется с нашим модификатором.
Теперь, если нам потребуется написать функцию, которая может быть исполнена только владельцем контракта, то нам достаточно добавить onlyOwner к ее заголовку как это сделано в setData и tranferOwnership.
В пределах одного контракта мы решили проблему дублирования кода. А теперь представьте что у нас несколько контрактов. У всех у них есть функции с проверкой на владельца. Получается что в каждом контракте придется писать код модификатора. А хотелось бы опять же вынести весь дублирующий код в одно место. Тут нам на помощь приходит наследование.
Контракты в solidity могут наследоваться друг на друга. Это значит что функции и переменные контракта от которого мы наследуемся будут доступны в контракте который наследует.
1 2 3 |
contract A is B, C, D { ... } |
Контракт A наследуется от контрактов B, C и D.
Рассмотрим на примере нашей визитки. Вынесем код, который отвечает за проверку владельца в контракт Ownable и унаследуем от него нашу визитку.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
pragma solidity ^0.4.18; contract Ownable { address owner; function Ownable() public { owner = msg.sender; } modifier onlyOwner() public { require(msg.sender == owner); _; } function transferOwnership(address newOwner) public onlyOwner { owner = newOwner; } } contract BusinessCard is Ownable { mapping (bytes32 => string) data; function setData(string key, string value) public onlyOwner { data[keccak256(key)] = value; } function getData(string key) public constant returns(string) { return data[keccak256(key)]; } } |
Как видите код нашей визитки значительно упростился, а в заголовке появилось «is Ownable». Если вы заметили, то в коде визитке сейчас отсутствует конструктор. Его мы убрали потому как он содержал код сохранения владельца визитки. Этот код мы перенесли в Ownable. Стоит отметить что если и в контракте наследнике и в контракте предке есть конструктор, то сначала будет исполнен конструктор предка, а потом наследника. Т.е. в нашем случае сначала Ownable а потом BuisnessCard.
Теперь если у нас появятся другие контракты, которые требуют проверки доступа, то нам достаточно наследоваться от Ownable в котором мы уже написали все необходимое. Шаблон наследования от Ownable очень распространенная практика в написании смарт-контрактов.
На этом пока все. Продолжение читать тут. Предыдущий урок тут.
Если у вас возникли вопросы то можете смело писать на электронную почту (раздел «контакты«). Также приветствуется критика.
Если статья показалась вам полезной и вы желаете отблагодарить автора, то это можно сделать отослав немного эфира на адрес 0xEA15Adb66DC92a4BbCcC8Bf32fd25E2e86a2A770.
А не перепутано ли местонахождение «_», в псевдокоде модификатора и функции для него?
modifier имя_модификатора() {
код_модификаторы
}
function имя_функции() имя_модификатора {
…
_;
…
}
Судя по дальнейшему описанию перепутано, а иначе какой смысл несет «_» в теле функции?
Верно, спасибо, исправил.
И чтобы ее не !могу! вызвать кто попало мы вначале ее кода сделаем проверку на адрес владельца.
Чтобы к функции применить модификатор нужно !в после! скобок поставить имя модификатора
Спасибо. Исправил.
modifier onlyOwner() public {
require(msg.sender == owner);
_;
}
Вот тут слово public вызывает ошибку компилятора в Remix
(последний вариант визитки в этом уроке)
если убираю то все норм.
в спецификации языка для модификаторов не указаны дополнительные модификаторы типа public, видимо у автора опечатка
Подскажите как додать в контракт BusinessCard для функции setData модификатор проверки введенного пароля (подумать как это можно реализовать используя имеющиеся знания)