Академический Документы
Профессиональный Документы
Культура Документы
Смарт-контракты в сети
Ethereum
© geekbrains.ru
На этом уроке
1 Познакомимся со смарт-контрактами. Узнаем, в какой среде они выполняются.
2 Поговорим об ограничениях смарт-контрактов и их обходных путях.
3 Разберём языки программирования для написания смарт-контрактов.
4 Научимся компилировать, тестировать и развёртывать смарт-контракты.
Оглавление
Глоссарий
Введение в смарт-контракты
Газ и комиссии
Ограничения
Solidity
Viper
YUL и YUL+
YUL
YUL+
Сравнение языков
DATA
Storage
Memory
Функции
Функция Constructor
События и журналы
Библиотеки
© geekbrains.ru 1
Шаблоны и библиотеки
Стандарты
Безопасность и библиотеки
Компиляция
Практическое задание
Дополнительные материалы
Используемые источники
Глоссарий
Виртуальная машина — программная и/или аппаратная система, которая:
Введение в смарт-контракты
Что такое смарт-контракты?
«Умный контракт» — это простая программа, работающая в сети Ethereum. Она находится по
конкретному адресу в блокчейне Ethereum. Смарт-контракт состоит:
© geekbrains.ru 2
Смарт-контракты — это одна из разновидностей учётной записи сети Ethereum. Это означает, что у
смарт-контракта есть баланс, и он отправляет транзакции внутри сети. Такой контракт развёртывается
в сети и работает в соответствии с заложенным алгоритмом. Пользователи взаимодействуют со
смарт-контрактом путём транзакций. Последние вызывают определённые в смарт-контракте функции.
EVM — это среда, в которой живут все учётные записи Ethereum, в том числе и смарт-контракты. В
любом блоке сети Ethereum имеется только одно «каноническое» состояние, а EVM — это то, что
определяет правила для вычисления нового допустимого состояния от блока к блоку.
Аналогия с «распределённым реестром» часто используется для описания таких блокчейнов, как
Биткойн. Криптовалюта ведёт себя как «обычная» валюта из-за правил, которые определяют, что
можно или нельзя делать для изменения этого реестра.
Например, биткойн-адрес не тратит больше биткоинов, чем получил ранее. Эти правила лежат в
основе всех транзакций сети и во многих других блокчейнах.
Хотя у Ethereum есть собственная криптовалюта (Ether), которая следует почти таким же интуитивно
понятным правилам, она также обеспечивает гораздо более мощную функцию — смарт-контракты.
Для этой сложной функции требуется и сложная аналогия. Вместо «распределённого реестра»
Ethereum представляет собой «распределённый конечный автомат».
Состояние Ethereum — это большая структура данных, которая содержит не только все счета и
балансы, но и состояние машины. Последнее изменяется от блока к блоку в соответствии с заранее
определённым набором правил и выполняет произвольный машинный код. Конкретные правила
изменения состояния от блока к блоку определяются EVM.
EVM работает как стековая машина с глубиной 1024 элементов. Каждый элемент представляет собой
256-битное слово, которое используется для максимальной совместимости с хеш-схемой SHA-3-256.
Во время выполнения EVM поддерживает временную память в виде массива байтов. Эта память не
сохраняется между транзакциями.
© geekbrains.ru 3
Газ и комиссии
Газ — это единица измерения. Она измеряет объём вычислительных усилий, требуемых для
выполнения конкретных операций в сети Ethereum. Поскольку для выполнения каждой транзакции
Ethereum нужны вычислительные ресурсы, каждая транзакция требует комиссию. Газ — это комиссия
для успешного выполнения транзакции в сети Ethereum.
По сути, сборы за газ оплачиваются в эфире (ETH). Цены на газ указываются в Gwei, который сам по
себе — номинал ETH. Каждый Gwei равен 0,000000001 ETH (10e-9 ETH).
Оплата газа помогает поддерживать безопасность сети Ethereum. Так как требуется плата за каждое
вычисление, выполняемое в сети, мы предотвращаем рассылку спама участниками сети.
Использование газа предотвращает случайные или вредоносные бесконечные циклы, а также другие
вычислительные потери в коде. Для каждой транзакции устанавливается ограничение на количество
используемых вычислительных шагов. При этом газ, не использованный в транзакции, возвращается
пользователю.
© geekbrains.ru 4
Каждый может развернуть смарт-контракт в сети. Но требуется иметь достаточное число ETH для
разворачивания контракта. Разворачивание смарт-контракта — технически транзакция. Поэтому
нужно платить за газ, как и за простой перевод ETH. Однако затраты на газ для развёртывания
смарт-контракта намного выше.
Ограничения
Сами по себе смарт-контракты не получают информации о «реальных» событиях, потому что они не
отправляют HTTP-запросы. Это сделано намеренно, поскольку использование внешней информации
ставит под угрозу консенсус. А это важно для безопасности и децентрализации. Есть способы обойти
это путём использования оракулов.
Языки программирования
смарт-контрактов
Отличный аспект Ethereum — возможность программировать смарт-контракты с использованием
относительно удобных для разработчиков языков. Если имеется опыт работы с Python или JavaScript,
можно найти язык со знакомым синтаксисом.
© geekbrains.ru 5
Более опытные разработчики иногда используют Yul, промежуточный язык для виртуальной машины
Ethereum, или Yul +, расширение Yul.
Solidity
Статически типизирован, тип переменной известен во время компиляции.
Поддерживает:
1. Наследование — возможность продлевать другие контракты.
2. Библиотеки — возможность создавать повторно используемый код, чтобы вызывать его из
разных контрактов. Например, статические функции в классе других
объектно-ориентированных языков программирования.
3. Сложные пользовательские типы.
Пример кода контракта:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.7.0;
contract Coin {
// The keyword "public" makes variables
// accessible from other contracts
address public minter;
mapping (address => uint) public balances;
© geekbrains.ru 6
Viper
Python — язык программирования с сильной типизацией, он имеет небольшой и понятный код
компилятора.
Этот язык имеет меньше функций, чем Solidity, чтобы сделать контракты более безопасными и
простыми для аудита. Vyper не поддерживает некоторую функциональность, например:
1. Модификаторы.
2. Наследование.
3. Перегрузка функций.
4. Перегрузка оператора.
5. Рекурсивный вызов.
# Auction params
# Beneficiary receives money from the highest bidder
beneficiary: public(address)
auctionStart: public(uint256)
auctionEnd: public(uint256)
© geekbrains.ru 7
assert msg.value > self.highestBid
# Track the refund for the previous high bidder
self.pendingReturns[self.highestBidder] += self.highestBid
# Track new high bid
self.highestBidder = msg.sender
self.highestBid = msg.value
# 1. Conditions
# Check if auction endtime has been reached
assert block.timestamp >= self.auctionEnd
# Check if this function has already been called
assert not self.ended
# 2. Effects
self.ended = True
# 3. Interaction
send(self.beneficiary, self.highestBid)
YUL и YUL+
Использовать Yul или Yul + рекомендуется только после ознакомления с лучшими практиками
безопасной разработки смарт-контрактов и особенностями работы с EVM.
© geekbrains.ru 8
YUL
Промежуточный язык для Ethereum. Поддерживает EVM и eWASM (WebAssembly для Ethereum), и
разработан как общий знаменатель для обеих платформ.
YUL+
Низкоуровневое, высокоэффективное расширение для YUL. YUL + рассматривается как
экспериментальное предложение по обновлению YUL, добавляющее в него новые функции.
Сравнение языков
Как и в случае с другим языком программирования, в основном речь идёт о выборе подходящего
инструмента для правильной работы, а также личных предпочтений. Вот несколько вещей, которые
следует учитывать, если не применён ни один из языков:
© geekbrains.ru 9
2 Позволяет спуститься на уровень EVM, что может оптимизировать использование газа в
контрактах.
Storage
Постоянные данные называются хранилищем и представляются переменными состояния. Эти
значения постоянно хранятся в блокчейне. Требуется объявить тип, чтобы контракт отслеживал,
сколько памяти в блокчейне ему потребуется при компиляции.
contract SimpleStorage {
uint storedData; // State variable
// ...
}
Тип address содержит также адрес сети Ethereum, в котором находятся до 20 байт или 160 бит. При
чтении имеет вид в шестнадцатеричной системе счисления с начальным 0x. Например,
0x607c794cda77efb21f8848b7910ecf27451ae842.
© geekbrains.ru 10
Memory
Эти переменные появляются только во время выполнения функции. Они имеют тип memory.
Поскольку переменные не хранятся постоянно в блокчейне, их использование намного дешевле.
Примеры:
Функции
Функции могут получать (get) данные или сохранять (set) в ответ на входящие транзакции.
© geekbrains.ru 11
Функции типа view
Такие функции не изменяют состояние данных контракта. Это функции getter — они используются,
например, для получения баланса пользователя.
5 Отправка эфира.
Функция Constructor
Функция constructor выполняется только один раз при первом развёртывании контракта. Эти функции
чаще всего инициализируют переменные.
// Initializes the contract's data, setting the `owner` to the address of the
contract creator.
constructor() public {
// All smart contracts rely on external transactions to trigger its
functions.
// `msg` is a global variable that includes relevant data on the given
transaction,
// such as the address of the sender and the ETH value included in the
transaction.
owner = msg.sender;
}
address.send().
События и журналы
События позволяют общаться со смарт-контрактом путём использования внешнего интерфейса или
других приложений-подписчиков. В процессе выполнения транзакции смарт-контракты генерируют
события и записывают их в блокчейн, которые затем может обрабатывать интерфейс. Контракты не
умеют считывать события из другого контракта.
© geekbrains.ru 12
Библиотеки
Не нужно писать каждый смарт-контракт в проекте. Доступно множество библиотек смарт-контрактов
с открытым исходным кодом. Библиотеки избавят от создания смарт-контактов.
Шаблоны и библиотеки
При написании смарт-контрактов есть большая вероятность создания похожих шаблонов. Например,
назначение адреса администратора для выполнения защищённых операций в контракте.
contract Ownable {
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(owner == msg.sender, "Ownable: caller is not the owner");
_;
}
}
Чтобы использовать такой строительный блок в контракте, нужно сначала импортировать его, а затем
наследовать в собственных контрактах. Это позволит использовать модификатор, предусмотренный
базовым Ownable контрактом, для защиты своих функций.
Другой популярный пример — SafeMath или DsMath. Это библиотеки, в отличие от базовых
контрактов, которые предоставляют арифметические функции с проверками переполнения.
Последние до версии 0.8.0 не предусмотрены языком Solidity. Раньше рекомендовалось использовать
любую из этих библиотек вместо собственных арифметических операций для защиты контракта от
переполнений.
© geekbrains.ru 13
Стандарты
Чтобы облегчить совместимость, сообщество Ethereum определило несколько стандартов в форме
ERC. При включении ERC в контракты рекомендуется искать стандартные реализации, а не пытаться
развернуть собственные.
Стоит отметить, что некоторые ERC — не автономны. Они считаются дополнением к другим ERC.
Например, ERC2612 добавляет расширение к ERC20 для повышения удобства использования.
Безопасность и библиотеки
Библиотеки смарт-контрактов с открытым исходным кодом также часто подвергаются тщательной
проверке. Поскольку от них зависят многие проекты, у сообщества есть сильный стимул держать их
под постоянным контролем. Гораздо чаще ошибки обнаруживаются в коде приложения, чем в
многократно используемых библиотеках контрактов.
Компиляция
Чтобы EVM выполняла контракт, он должен быть в байт-коде. После компилирования контракт:
contract Greeter {
PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x41 JUMPI PUSH1 0x0
CALLDATALOAD PUSH29 0x100000000000000000000000000000000000000000000000000000000
SWAP1 DIV PUSH4 0xFFFFFFFF AND DUP1 PUSH4 0xCFAE3217 EQ PUSH2 0x46 JUMPI JUMPDEST
PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x52 JUMPI PUSH1 0x0
© geekbrains.ru 14
DUP1 REVERT JUMPDEST POP PUSH2 0x5B PUSH2 0xD6 JUMP JUMPDEST PUSH1 0x40 MLOAD
DUP1 DUP1 PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE DUP4 DUP2 DUP2 MLOAD DUP2
MSTORE PUSH1 0x20 ADD SWAP2 POP DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP4
DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x9B JUMPI DUP1 DUP3 ADD MLOAD
DUP2 DUP5 ADD MSTORE PUSH1 0x20 DUP2 ADD SWAP1 POP PUSH2 0x80 JUMP JUMPDEST POP
POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0xC8
JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB
NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1
0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH1 0x60 PUSH1 0x40 DUP1 MLOAD
SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x5 DUP2 MSTORE PUSH1 0x20 ADD PUSH32
0x48656C6C6F000000000000000000000000000000000000000000000000000000 DUP2 MSTORE
POP SWAP1 POP SWAP1 JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 SLT 0xec 0xe
0xf5 0xf8 SLT 0xc7 0x2d STATICCALL ADDRESS SHR 0xdb COINBASE 0xb1 BALANCE 0xe8
0xf8 DUP14 0xda 0xad DUP13 LOG1 0x4c 0xb4 0x26 0xc2 DELEGATECALL PUSH7
0x8994D3E002900
[
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_spender",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"name": "",
"type": "bool"
© geekbrains.ru 15
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"payable": true,
"stateMutability": "payable",
"type": "fallback"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "from",
"type": "address"
},
{
"indexed": true,
"name": "to",
"type": "address"
},
{
"indexed": false,
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
}
]
2. Эфир для оплаты комиссии. При этом важно понимать, что разворачивание контракта требует
намного больше газа, чем простой перевод ETH.
3. Доступ к узлу Ethereum: запустить свой или подключиться к общедоступному узлу, например, с
использованием сервиса Infura.
© geekbrains.ru 16
Первый шаг — зайдём на страницу Remix IDE и создадим новый файл. В верхней левой части
интерфейса Remix добавим новый файл и введём желаемое имя файла.
contract Counter {
Если есть навык программирования, то станет понятно, что делает эта программа. Построчное
объяснение:
© geekbrains.ru 17
2. Строка 6: В нашем контракте хранится одно целое число без знака, его имя count, и оно
начинается с 0.
count.
4. Строка 14: Вторая функция — геттер. Он считывает значение count переменной вне
Контракт выглядит как класс из языков ООП, таких как Java или C ++.
© geekbrains.ru 18
Шаг четвёртый — перейдём к экрану развёртывания и выполнения транзакций:
© geekbrains.ru 19
Когда мы перейдём на экран транзакций Deploy and run, нужно дважды убедиться, что отображается
название контракта. Далее нажмём Deploy. В верхней части страницы текущая среда — Javascript VM.
Это означает, что мы развернём и будем взаимодействовать с нашим смарт-контрактом в локальной
тестовой сети. Так появится возможность проводить тестирование быстрее и без каких-либо
комиссий.
© geekbrains.ru 20
После того как мы нажали кнопку Deploy, наш контракт появится внизу. Щёлкнем стрелку слева, чтобы
развернуть её и увидеть содержание нашего контракта. Это переменная counter, функция
increment() и геттер getCounter().
Если нажать кнопку count или getCount, появится содержимое переменной count контракта, и оно
отобразится на экране. Поскольку мы ещё не вызывали increment-функцию, она покажет 0.
© geekbrains.ru 21
Теперь вызовем increment-функцию, нажав на кнопку. Отобразился журнал выполненных транзакций,
которые появятся в нижней части окна. Мы увидим, что записи в журнале отличаются, когда
нажимаем на кнопку для получения данных вместо increment-кнопки. Это потому, что для чтения
данных в блокчейне не нужны транзакции (запись) или комиссии. Только для изменения состояния
блокчейна требуется совершить транзакцию:
После нажатия кнопки увеличения сгенерируется транзакция для вызова нашей increment()-функции.
Если мы снова кликнем на count или getCount, то прочитаем недавно обновлённое состояние нашего
смарт-контракта с переменной count больше 0.
© geekbrains.ru 22
Практическое задание
Повторите процесс развёртывания контракта, но уже используя код контракта для ERC20-токена с
применением Remix IDE.
Дополнительные материалы
1. Документация языка Vyper.
2. Документация языка Solidity.
3. Документация языка YUL.
© geekbrains.ru 23
4. Документация языка YUL+.
5. Рекомендации по созданию безопасных смарт-контрактов.
6. Официальный репозиторий языка eWASM.
7. Сравнение языков разработки смарт-контрактов.
8. Одна из популярнейших библиотек смарт-контрактов.
9. Документация к библиотеке OpenZeppelin.
10. Библиотека смарт-контрактов.
11. Ещё одна библиотека смарт-контрактов.
12. Учебное пособие по стандарту ERC20.
13. Среда разработки.
Используемые источники
1. Ethereum EVM illustrated.
2. Ethereum development documentation.
3. Deploying your first smart contract.
© geekbrains.ru 24