Раздел 30 · Web3 на Rust

Архитектура блокчейна: состояние как машина

senior~30 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Архитектура блокчейна: состояние как машина

Уроки о криптографии и распределённом консенсусе дали нам кирпичи: хеш, подпись, дерево Меркла, консенсус. Теперь из них собираем работающий узел. Восемь уроков, и в конце ты запускаешь локальную сеть, где узлы майнят, переводят монеты и сами разрешают форк. Начнём с одной идеи, на которую нанизано всё остальное: блокчейн это реплицированная машина состояний.

Идея

Сними мишуру с любого блокчейна, и под ней окажется до скуки знакомая вещь из теории автоматов (раздел 02-cs): машина состояний. Есть состояние (кто чем владеет) и функция перехода (применить блок транзакций). Подаёшь блоки по очереди, состояние меняется по строгому правилу.

Слово «реплицированная» это вся соль. Состояние держит не один сервер, а тысячи недоверяющих друг другу узлов, и у каждого своя копия. Чтобы копии не разъехались, переход обязан быть детерминированным до последнего бита. Тот же набор блоков на твоей машине и на моей обязан дать байт-в-байт одно состояние. Если хоть где-то закралась случайность, время или порядок обхода хеш-карты, узлы разойдутся в том, кто сколько должен, и сеть расколется.

Отсюда вся остальная конструкция выводится почти механически:

  • Состояние. Кто чем владеет. У нас это балансы и счётчики nonce по адресам.
  • Транзакция. Подписанное намерение изменить состояние: «переведи 10 с моего адреса на адрес Боба».
  • Блок. Пачка транзакций плюс заголовок, скреплённый PoW и ссылкой на предыдущий блок.
  • Цепь. Блоки, связанные хешем в одну историю, плюс правило, какая ветка главная.
  • Сеть. Узлы, которые гоняют друг другу блоки и транзакции и сходятся к одной истории.

Каждый из этих слоёв это один урок блока. Соберём их снизу вверх.

Состояние и переход

Возьмём аккаунтную модель, как у Ethereum (UTXO Bitcoin разберём в следующем уроке). Состояние это две карты: сколько у адреса монет и какой у него nonce, то есть сколько транзакций он уже отправил.

use std::collections::HashMap;

/// Аккаунтное состояние: балансы и счётчики nonce. Адресов, которых нет в
/// карте, как будто есть с балансом 0 и nonce 0.
#[derive(Clone, Default)]
pub struct State {
    balances: HashMap<Address, u64>,
    nonces: HashMap<Address, u64>,
}

impl State {
    pub fn balance(&self, addr: &Address) -> u64 {
        self.balances.get(addr).copied().unwrap_or(0)
    }

    /// Ожидаемый nonce следующей транзакции адреса.
    pub fn nonce(&self, addr: &Address) -> u64 {
        self.nonces.get(addr).copied().unwrap_or(0)
    }
}

Функция перехода будет проверять подпись, сверять nonce, списывать средства и наращивать счётчик. Её мы напишем в уроке про мемпул, а пока держи в голове: каждая проверка в переходе это правило консенсуса. Ослабь любую (прими чужую подпись, разреши уйти в минус, прими повторный nonce), и твой узел форкнется от сети, потому что придёт к состоянию, с которым остальные не согласны.

премиум-урок

Продолжение в премиуме: 1 200 ₽ в месяц или 10 000 ₽ в год. Вместе с ним серверные проверки задач и школьные токены AI-коуча.

Оплата пока не подключена: премиум и курсы открываются промокодом.