Продолжаем наши уроки по написанию смарт-контрактов. Предыдущий урок можно почитать тут, а полный список уроков тут..
В предыдущем уроке мы написали смарт-контракт визитку. В этом уроке мы улучшим нашу визитку, а в частности запретим всем кроме владельца изменять данные внутри визитки. А также затронем темы:
- переменная msg
- конструктор контракта
- тип address
- исключение throw
Код нашей визитки из предыдущего урока:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
pragma solidity ^0.4.18; contract BusinessCard { mapping (bytes32 => string) data; function setData(string key, string value) public { data[keccak256(key)] = value; } function getData(string key) public constant returns(string) { return data[keccak256(key)]; } } |
Напомню — методом getData мы можем получить значение, записанное ранее на визитке. А метод setData позволял изменить значение.
После того как смарт-контракт будет создан внутри сети Ethereum он будет доступен любому человеку в сети. Это значит что любой участник сети Ethereum может выполнить как функцию getData так и функцию setData. Таким образом мы имеем опасность того что другой пользователь вызвав функцию setData изменит данные на нашей визитке. А мы бы хотели чтобы менять их мог только владелец визитки. Давайте решим эту проблему!
Внутри контракта нам доступна глобальная переменная msg. Это структура с несколькими полями. С этими полями мы будем знакомится по мере необходимости. Сейчас мы познакомимся с полем sender. Итак msg.sender — это адрес того, кто выполняет контракт в данный момент. Того кто вызвал функцию. Тип этого поля address.
Таким образом для того чтобы запретить менять данные визитки посторонним лицам контракту достаточно проверить — является ли адрес того кто выполняет контракт в данный момент адресом владельца визитки. Т.е. если msg.sender равен адресу владельца визитки то мы выполняем setData. В противном случае сообщаем об ошибке.
И тут возникает вопрос. Как получить адрес владельца визитки? При создании контракта вызывается специальная функция — конструктор. Она имеет такое же название как и сам контракт и ничего не возвращает.
1 2 3 |
function ИмяКонтракта() { ... } |
Мы уже знаем что в msg.sender хранится адрес того кто выполняет контракт в данный момент. Поскольку тот кто создает визитку и есть владелец визитки, то нам достаточно получить в конструкторе msg.sender и сохранить его внутри контракта для последующего сравнения.
Поправим наш контракт:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
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 { if(msg.sender != owner) throw; data[keccak256(key)] = value; } function getData(string key) public constant returns(string) { return data[keccak256(key)]; } } |
Теперь наш контракт содержит переменную owner в которую конструктор сохраняет адрес создателя-владельца визитки. А в функции setData e у нас появилось условие:
1 2 |
if(msg.sender != owner) throw; |
Незнакомое нам слово throw заставляет контракт прекратить работу. В последних версиях solidity не рекомендуется использовать throw. А для проверки какого-либо условия прекращения работы функции рекомендуют использовать конструкцию require(условие). Если условие не выполняется то функция останавливается. Между способами остановки работы функции есть существенная разница, но мы ее пока рассматривать не будем. Просто перепишем наш код с require.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
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 getData(string key) public constant returns(string) { return data[keccak256(key)]; } } |
Давайте теперь проверим работу нашего контракта. Создайте контракт в сети Ethereum (см. урок 1). Обратите внимание на поле Account.
Поле account это адрес вашего аккаунта. Именно от его имени вы в данный момент работаете и именно от его имени был создан контракт. Если вы сейчас вызовите setData с какими либо данными (например: «email», «cromlehg@gmail.com») то данные внутри контакта успешно поменяются.
В целях тестирования Remix позволяет сменить адрес аккаунта. В выпадающем списке account выберите любой другой адрес. А теперь попробуйте вызвать setData и вы получите сообщение об ошибке — исключение. Контракт не позволил нам изменить данные потому что адрес владельца визитки другой.
Статус последней операции remix отображает в консоле.
Итак, мы в этом уроке узнали как получить адрес того кто выполняет контракт в данный момент. Узнали что такое конструктор. И что самое важное — научились ограничивать доступ к функциям контракта и тестировать!
На этом пока все. Продолжение читать тут. Предыдущий урок тут.
Если у вас возникли вопросы то можете смело писать на электронную почту (раздел «контакты«). Также приветствуется критика.
Если статья показалась вам полезной и вы желаете отблагодарить автора, то это можно сделать отослав немного эфира на адрес 0xEA15Adb66DC92a4BbCcC8Bf32fd25E2e86a2A770.
Отличные уроки большое спасибо!!!
а тут есть какая то связь в том что имя контракта и имя функции одинаковые (BusinessCard)?
вопрос снят, возник другой вопрос, какие свойства у функции конструктора?
Что имеете в виду под свойствами? Модификаторы, аргументы или что?
ну я как понял, конструктор выполняется только один раз. Может еще у него какие особенности есть?
Вам лучше задавать вопросы в нашей группе в телеграмме — так будет быстрее — https://t.me/blockchainwitness .
Конструктор это функция которая:
Ошибка в коде:
function setData(string key, string value) public {
require(msg.sender != owner);
throw;
data[keccak256(key)] = value;
}
Судя по тексту далее — require наверное надо поменять на if вероятно не хватает фигурных скобок, но я еще не все прочитал)))
Спасибо. Исправил.