[Перевод] Ethereum, смарт-контракты

Перед чтением статьи рекомендую (если еще не знакомы) ознакомиться с whitepaper’ом битка

История появления и мотивация

С появлением концепции PoW в блокчейне биткоина, стал возможен безопасный нецентрализованный обмен токенами. Каждая транзакция проходит следующие этапы проверки, выполняемые некоторым скриптом:

  • сумма UTXO неизменна (UTXO — Unspent transaction output другими словами токены)

  • цифровая подпись отправителя валидна

Для выполнения более сложной логики проверки транзакции нужно менять скрипт, однако платформа биткоина предоставляет неудобный, не тьюринг полный язык для написания таких скриптов

Цель ethereum’a — объединить и усовершенствовать концепции криптовалют, альткоинов и создать on-blockchain протокол, который позволит разработчикам создавать распределенные приложения на основе инфраструктуры, которая предоставляет масштабируемость, тьюринг полноту, простоту разработки и совместимость

Аккаунты

Роль состояний в эфире выполняют аккаунты. Каждый аккаунт имеет 20-ти байтный адрес. Изменение состояний аккаунтов происходит за счет передачи сообщений

Поля аккаунта:

  • nonce — счетчик, который служит для того чтобы каждая транзакция была обработана лишь 1 раз

  • ether balance — внутренняя валюта, используется для оплаты транзакций

  • contract code (if present)

  • storage — массив данных, изначально пустой

Типы аккаунтов:

a8f9c2aeff74ca0a46939bdcb7ae7b56.jpg

Externally owned accounts — externally owned accounts не содержат исполняемого кода. Пользователь может отправить сообщение с externally owned account, подписав транзакцию

Contract accounts — при получении сообщения, contract account активируется и исполняет код скрипта контракта. Исполняемому процессу доступны чтение и запись из внутреннего хранилища, отправка сообщений, создание других контрактов

Сообщения

Сообщения ethereum’a аналог транзакций bitcoin’a с тремя главными отличиями:

  1. Сообщение в эфире может быть создано как externally owned акканутом, так и contract аккаунтом, тогда как транзакция в биткоине может быть создана только externally owned аккаунтом (пользователем)

  2. Сообщения в эфире поддерживают янвым образом передачу данных

  3. У получателя сообщения (если это contract account) есть возможность вернуть ответ

Структура сообщения

Nonce:

Nonce — порядковый номер транзакций, отправленных с данного адреса. При каждой отправке транзакции значение Nonce увеличивается на единицу. Кроме того, Nonce предотвращает повторную атаку: злоумышленник может захотеть исполнить подписанную транзакцию еще раз, однако при валидации Nonce транзакции и текущий Nonce аккаунта будут не совпадать и транзакция будет считаться не валидной

GASPRICE and GASLIMIT:

Для того чтобы предотвратить зацикливание нод при обработки сообщения, каждое сообщение должно указывать за какое максимальное число «шагов» GASLIMIT выполнения кода отправитель готов платить. GASPRICE — сколько отправитель платит за выполнение одного шага

Если при выполнении сообщения заканчивается GAS, то все участники возвращаются в исходное состояние и уже потраченный GAS передается майнеру в качестве комиссии.

Recipient:

В поле recipient хранится 20-ти битный адрес получателя. Получателем может быть как externally owned аккаунт,  так и contract аккаунт

Ether value:

В поле ether value хранится число, которое показывает сколько эфира отправитель передает получателю с учетом комисси за транзакцию (GAS и комиссия за пересылку данных)

Data:

Это поле используется для передачи данных смарт контракту, также возможны вызовы функций внутри смарт контракта, если сообщение содержит одну из инструкций CALL, DELEGATECALL

Если поле data пустое, это означает что сообщение предназначено только для передачи ehter’a, а не для исполнения кода

ECDSA (Elliptic Curve Digital Signature Algorithm):

Ознакомиться с тем как работает ECDSA можно тут

Комбинация {r,s,v} передается как 65-ти байтная строка:

  • 32 байта r

  • 32 байта s

  • 1 байт v

Валидация сообщений

image-loader.svg

Этапы проверки сообщений на нодах:

  1. Проверить корректность кол-ва и содержания полей сообщения (Nonce транзакции совпадает с Nonce аккаунта, сообщение имеет корректную подпись)

  2. STARTGAS*GASPRICE — комиссия которую платит отправитель. Если у отправителя достаточно ether’a → снять сумму комиссии с его аккаунта и увеличить Nonce аккаунта на 1, иначе вернуть ошибку

  3. GAS=STARTGAS*GASPRICE — счетчик того сколько осталось газа для выполнения транзакции, при каждом выполнении команды или передачи байт данных счетчик уменьшается

  4. Передать Ether value получателю, если аккаунта получателя не существует, то создать его. Если получатель — contract account, то исполнить его код до конца или до окончания GAS'a

  5. Если передача данных упала из-за недостаточного кол-ва ether'a на аккаунте или из-за окончания GAS'a, то вернуть состояния получателя и отправителя в исходное состояние и добавить комиссию к кошельку аккаунта майнера

  6. Вернуть весь оставшийся GAS отправителю, и перечислить комиссию майнеру

Contract account code execution environment

Код на смарт контракта исполняется на стековой витруальной машине У операторов байткода Ethereum Virtual Machine (скоращенно EVM) есть доступ к трем типам структур данных:

  1. stack выполнения

  2. memory — динамический массив, который можно расширять бесконечно

  3. long-term contract storage — key/value «холодное» хранилище. Холодное потому что доступно даже после выполнения кода контракта

Высокоуровневые языки программирования смарт-контрактов: Solidity и Vyper

Solidity — объектно-ориентированный c++ like язык:

Пример смарт-контракта вендинг машины на solidity:

--**pragma solidity 0.8.7;
contract VendingMachine {**
    // Declare state variables of the contract
    address public owner;
    mapping (address => uint) public cupcakeBalances;
    
// When 'VendingMachine' contract is deployed:
// 1. set the deploying address as the owner of the contract
// 2. set the deployed smart contract's cupcake balance to 100
constructor() {
    owner = msg.sender;
    cupcakeBalances[address(this)] = 100;
}

// Allow the owner to increase the smart contract's cupcake balance
function refill(uint amount) public {
    require(msg.sender == owner, "Only the owner can refill.");
    cupcakeBalances[address(this)] += amount;
}

// Allow anyone to purchase cupcakes
function purchase(uint amount) public payable {
    require(msg.value >= amount * 1 ether, "You must pay at least 1 ETH per cupcake");
    require(cupcakeBalances[address(this)] >= amount, "Not enough cupcakes in stock to complete this purchase");
    cupcakeBalances[address(this)] -= amount;
    cupcakeBalances[msg.sender] += amount;
}

}

msg — объект сообщения которое вызвало исполнение/создание контракта

На примере видно, что в конструкторе VendingMachine в поле owner записывается адрес отправителя сообщения. Те в будущем увеличивать cupcakeBalances сможет только аккаунт, который задеплоил смарт-контракт

Внешние источники данных

Для адекватной работы алгоритма консенсуса нужно чтобы каждая нода получала одинаковый результат при исполнении сообщения, при обращении внутри смарт-контракта к внешнему API ноды априори не будут получать одинаковый результат, если данные меняются во времени

Как тогда работать с внешними данными? Решение — oracle. Oracle реализует некий middle end между off-chain данными и on-chain смарт-контрактами. Он оформляет доступ к данными в отдельную транзакцию и записывает его на блокчейн. Для этого orcale обычно состоит из смарт-контракта и некоторых off-chain скриптов, которые API, а затем периодически отправлять транзакции для обновления данных смарт-контракта.

image-loader.svg

Однако oracle должен быть тоже же централизованным, иначе теряется смысл всего blockchain«а

Система токенов

fungible токены

on-blockchain токены имеют много приложений, например они могут имитировать фиатные деньги или другие активы, могут служить «документом» на владение каким-либо объектом. Система токенов — это база данных с одной операцией: вычесть X единиц с аккаунта А и прибавить X единиц аккаунту B, при условии что:

Наиболее распространенный стандарт реализации системы fungible токенов — ERC20, который определяется двумя сегментами:

  1. 6 функций:

    • TotalSupply — общее количество токенов

    • BalanceOf — возвращает баланс кошелька по заданному адресу

    • Transfer — позволяет владельцу контракта отправить токены заданному адресу

    • TransferFrom — отправляет токены с одного адреса на другой, главное отличие от Transfer: контракт может отправлять токены владельца автоматически

    • Approve — проверяет возможность передачи токенов с аккаунта владельца контракта заданному адресу

    • Allowance — возвращет остаток на кошельке по заданному адресу

  2. 2 события:

    • Transfer — это событие вызывается во время любого перевода токенов с одного кошелька на другой и дает подробную информацию о них

    • Approve — это событие вызывается каждый раз в функции approve

Non-fungible token

Невзаимозаменяемые токены — вид криптографических токенов, каждый экземпляр которых уникален и не может быть заменен другим токеном. Cтандарт для реализации NFT ERC-721 является аналогом ERC-20 для fungible токенов

Источники:

© Habrahabr.ru