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

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

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

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

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

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

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

Аккаунты

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

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

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

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

  • contract code (if present)

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

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

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

  • GASPRICE

  • GASLIMIT

  • Recipent

  • Ether value

  • Data

  • подпись отправителя: r,s,v (ECDSA)

Nonce:

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

GASPRICE and GASLIMIT:

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

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

(Однако после форка EIP-1559 теперь майнеры получают фиксированное вознаграждение за каждый сформированный блок)

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

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

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

  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, то вернуть состояния получателя и отправителя в исходное состояние и добавить комиссию к кошельку аккаунта майнера (актуально до форка EIP-1559)

  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 язык:

  • статическая типизация

  • наследование

  • модули

  • user-defined types

Пример смарт-контракта вендинг машины на 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 смарт-контрактами. Он оформляет доступ к данным в отдельную транзакцию и записывает его на блокчейн. Для этого oracle обычно состоит из смарт-контракта и некоторых off-chain скриптов, которые состоят из двух основных действий:

  • вытащить данные из какого-то API

  • отправить сообщение с данными нодам

Затем смарт-контракт обращается к данным, которые лежат в сообщении и таким образом являются частью блокчейна

Важно чтобы oracle был тоже децентрализованным, иначе теряется смысл всего блокчейна

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

fungible токены

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

  • Баланс А ≥ X

  • Есть подпись подтверждающая согласие аккаунта А

Наиболее распространенный стандарт реализации системы 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 токенов

Источники:

Комментарии (8)


  1. niksis2012
    14.12.2021 17:08
    -3

    Очень хайповая тема, ставлю лайк


  1. alan008
    14.12.2021 17:26
    +7

    и уже потраченный GAS передается майнеру в качестве комиссии

    С августа 2021 это уже не так. См. EIP-1559. Все комиссии за все транзакции не передаются майнерам, а сжигаются. Майнеры получают только фиксированную награду за блок — 2 ETH, которая делится пропорционально вкладу каждого, если майнинг происходит на пуле, а не в соло (при этом один блок создается каждые 13 секунд).


    1. Al-Kharba Автор
      14.12.2021 22:10
      +1

      Спасибо за комментарий! Добавил замечание про EIP-1559


    1. woddy
      15.12.2021 00:16
      +1

      Справдливости ради, сжигается не вся комиссия, а 90-99% в среднем. Награда майнера получается примерно 2.1 ETH за блок в среднем. Блоки 300 ETH тоже были (несколько штук за три месяца)


  1. fedorro
    15.12.2021 16:28

    хранится 20-ти битный адрес получателя

    Страдания по поводу 32 битных адресов IPv4 ничему не научили ????

    P/S: ... научили - длинна адреса 20 байт ????


    1. Al-Kharba Автор
      15.12.2021 22:03

      Да уж, на 20 бит не разгуляешься)
      Спасибо, это конечно же опечатка


  1. alexqrid
    16.12.2021 15:23

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

    На самом деле, если обратиться к контракту с пустым полем data, то контракт среагирует исполнение fallback функции. Ну и, конечно, без наличия такой функции контракт не сможет принимать Эфир.

    Из документации Solidity:  It is executed on a call to the contract if none of the other functions match the given function identifier (or if no data was supplied at all).


  1. Vitaliy-L
    16.12.2021 15:23

    Спасибо. Очень интересно. Читал два раза. Ничего не понял. Вдохновился.