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

Bitcoin на практике: regtest, RPC и rust-bitcoin

senior~40 мин

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

Bitcoin на практике: regtest, RPC и rust-bitcoin

Три урока мы разбирали Bitcoin по чертежам. Пора собрать всё руками: поднять собственный узел, где ты сам майнишь блоки одной командой, поговорить с ним по RPC и построить транзакцию на Rust. Это тот же путь, что мы прошли со своим самодельным узлом, только теперь узел настоящий.

Идея

Гонять эксперименты в основной сети Bitcoin невозможно: настоящие деньги, блок раз в десять минут. Поэтому у Bitcoin Core есть тренировочные режимы, и главный из них это regtest: приватная сеть из одного узла, где блоки создаются мгновенно по твоей команде.

Ступени тренировки:

  • regtest: твоя личная сеть, ты майнишь сам, монеты игрушечные. Для разработки и тестов.
  • testnet и signet: публичные тестовые сети с бесплатными монетами. signet новее и стабильнее, блоки в нём подписывает известный набор ключей, поэтому нет войн за хешрейт.
  • mainnet: настоящая сеть с настоящими деньгами.

Код везде один, меняется только сеть в конфиге. Отладил в regtest, запустил в mainnet.

bitcoin-cli: узел одной командой

Узел это демон bitcoind, а bitcoin-cli шлёт ему команды. Шаг 0, поставить Bitcoin Core:

brew install bitcoin          # macOS
sudo apt install bitcoind     # Debian/Ubuntu
# или вообще без установки, в докере:
docker run -d --name bitcoind bitcoin/bitcoin bitcoind -regtest

Дальше один раз настроим доступ по RPC. Логин и пароль запишем в конфиг: их подхватят и bitcoind, и bitcoin-cli, и наш будущий Rust-клиент, так все трое говорят на одном языке. Поднять regtest и намайнить себе монеты это несколько строк:

# один раз: RPC-логин и пароль в конфиг
mkdir -p ~/.bitcoin
printf 'rpcuser=user\nrpcpassword=pass\n' >> ~/.bitcoin/bitcoin.conf

# запускаем узел в режиме regtest
# -rest пригодится ниже, а -fallbackfee нужен, потому что на свежем
# regtest нет истории комиссий и узлу не из чего оценить fee
bitcoind -regtest -daemon -rest -fallbackfee=0.0001

# создаём кошелёк и адрес
bitcoin-cli -regtest createwallet "test"
addr=$(bitcoin-cli -regtest getnewaddress)

# майним 101 блок на свой адрес (мгновенно, без перебора PoW)
# 101 а не 1, потому что награда за блок созревает только через 100 блоков
bitcoin-cli -regtest generatetoaddress 101 "$addr"

# смотрим баланс: 50 BTC с первого блока стали тратимыми
bitcoin-cli -regtest getbalance

Обрати внимание на 101 блок: субсидия coinbase из урока 20 становится тратимой только через 100 блоков сверху. Это защита от трат монет с блока, который может устареть при реорге (урок 22).

Отправь и разбери транзакцию

Теперь сквозной сценарий: отправить монеты и разобрать получившуюся транзакцию по полям из урока 20.

# новый адрес-получатель и платёж на него
# (без -fallbackfee при запуске узла sendtoaddress тут бы упал:
# на пустом regtest узлу не из чего оценить комиссию)
dest=$(bitcoin-cli -regtest getnewaddress)
txid=$(bitcoin-cli -regtest sendtoaddress "$dest" 0.3)

# подтверждаем платёж: майним один блок поверх
bitcoin-cli -regtest generatetoaddress 1 "$addr" > /dev/null

# полный разбор транзакции (true значит развёрнутый JSON, а не сырые байты)
bitcoin-cli -regtest getrawtransaction "$txid" true

Выдача, сокращённая до главного:

{
  "txid": "9f3c1a...",
  "size": 222,
  "vsize": 141,
  "weight": 561,
  "vin": [
    {
      "txid": "4b1e77...",
      "vout": 0,
      "scriptSig": { "asm": "", "hex": "" },
      "txinwitness": ["30440220...01", "02a1b2..."]
    }
  ],
  "vout": [
    {
      "value": 0.30000000,
      "n": 0,
      "scriptPubKey": {
        "asm": "0 e8df018c7e326cc253faac7e46cdc51e68542c42",
        "hex": "0014e8df018c7e326cc253faac7e46cdc51e68542c42",
        "address": "bcrt1qar0srrr...",
        "type": "witness_v0_keyhash"
      }
    },
    {
      "value": 49.69998590,
      "n": 1,
      "scriptPubKey": { "address": "bcrt1q9h5k...", "type": "witness_v0_keyhash" }
    }
  ]
}

Вся теория блока про Bitcoin тут как на ладони:

  • Вход один. Кошелёк потратил целиком coinbase-выход на 50 BTC (помнишь, UTXO тратится только целиком). scriptSig пустой, а подпись и ключ уехали в txinwitness: это SegWit из урока 21 в живом виде.
  • Выхода два. 0.3 BTC получателю и почти 49.7 BTC сдачи себе. type: witness_v0_keyhash это P2WPKH, замок «докажи владение ключом с этим хешем».
  • Поля fee нет. Комиссия это входы минус выходы: 50 минус 0.3 минус 49.69998590 даёт 0.0000141 BTC. Ровно та арифметика из урока 20.
  • vsize меньше size. Witness тарифицируется по льготному весу, отсюда и разница: комиссию считают по виртуальному размеру.

RPC: узел это сервер

За bitcoin-cli стоит JSON-RPC: узел это сервер, а CLI лишь тонкий клиент. Тот же интерфейс доступен любой программе. Есть и REST-интерфейс для чтения (блоки, транзакции по HTTP GET), но пишущие операции идут через JSON-RPC с аутентификацией.

REST мы уже включили флагом -rest при запуске узла, так что его можно пощупать обычным curl, безо всякой аутентификации:

curl -s http://127.0.0.1:18443/rest/chaininfo.json
# {"chain":"regtest","blocks":102,"headers":102,"bestblockhash":"3f9a...",...}

Из Rust в узел ходят через крейт bitcoincore-rpc:

use bitcoincore_rpc::{Auth, Client, RpcApi};

fn main() -> anyhow::Result<()> {
    // подключаемся к локальному regtest-узлу;
    // логин и пароль те же, что мы записали в bitcoin.conf
    let rpc = Client::new(
        "http://127.0.0.1:18443",
        Auth::UserPass("user".into(), "pass".into()),
    )?;

    let info = rpc.get_blockchain_info()?;
    println!("сеть: {}, высота: {}", info.chain, info.blocks);

    // майним блоки прямо из кода, как наш самодельный узел майнил сам
    let address = rpc.get_new_address(None, None)?.assume_checked();
    let hashes = rpc.generate_to_address(10, &address)?;
    println!("намайнено блоков: {}", hashes.len());

    Ok(())
}

Это ровно то, что делал наш узел-CLI из урока 19, только теперь на другом конце настоящий Bitcoin Core.

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

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

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