Создаем собственный клиент Ethereum Classic: часть I

Создаем собственный клиент Ethereum Classic: часть I cryptowiki.ru

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

Ещё более серьёзный шаг на пути к децентрализации — разобраться, как работает клиент. Это поможет вам понять, как устроены криптовалюты, самостоятельно судить об изменениях в протоколе и логично обосновывать ваше мнение. Эта серия постов поможет пользователям и разработчикам получить представление о механике Ethereum Classic.

Создаем собственный клиент Ethereum Classic: часть I cryptowiki.ru

В этой статье я расскажу о том, как клиент скачивает блокчейн из P2P-сети. В последующих статьях я подробнее опишу, как создаются и подтверждаются блоки и как осуществляются переходы состояний аккаунтов. Все статьи серии будут сопровождаться кодом на языке Rust, с которым вы сможете поэкспериментировать сами. По окончании серии изо всех частей кода получится примитивный, но полностью рабочий клиент Ethereum Classic.

Слова словами — теперь посмотрим на код

В основу этой статьи легли результаты работы над etclient — минималистичным клиентом Ethereum Classic. По сравнению с другими клиентами Ethereum Classic, например, Parity или Go Ethereum, он очень простой: всего несколько тысяч строк кода обеспечивают его полную функциональность. Он состоит из отдельных компонентов, каждый из которых выполняет определённый набор задач: devp2p отвечает за сетевой уровень P2P, ethash — за консенсус, etcommon — за дерево Меркла и основные структуры блокчейна, и, наконец, SputnikVM — за переходы состояний транзакций.

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

Скачиваем блокчейн

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

Клиент Ethereum Classic постоянно пытается установить соединение сразу с несколькими участниками сети (то есть с другими компьютерами, на которых установлен клиент). Передача информации происходит по общему протоколу RLPx — в свою очередь, он может управлять различными субпротоколами, которые непосредственно контролируют обмен данными. Один из таких субпротоколов — Ethereum Classic.

Каждому клиенту присвается уникальный ID, что предотвращает опасность атаки посредника (man-in-the-middle). RLPx работает на уровне TCP. Как только устанавливается соединение, происходит handshake — запуск сеанса протокола RLPx. Если не вдаваться в детали криптографии, на этом этапе клиенты передают друг другу свои ID и согласуют настройки последующего шифрования.

Как устроен субпротокол «eth»

Субпротокол «eth» работает как в формате «запрос-ответ», так и асинхронно. Сначала происходит обмен статусами. Из них участники узнают о том, в какой сети находятся и какой блок был подтверждён последним. Каждый клиент отправляет другой стороне новые блоки и неподтверждённые транзакции, которых ей может не хватать, судя по текущим данным.

Кроме того, клиенты обмениваются запросами и ответами. Функции GetBlockHeaders и GetBlockBodiesпозволяют запросить информацию о конкретных блоках, которые необходимы клиенту. В ответ клиент получает заголовки и содержимое блоков — BlockHeaders и BlockBodies.

Таким образом, установив связь с остальными участниками сети, клиент может скачать некоторую часть блокчейна.

Связь с остальными узлами

Как вы могли заметить, я описал процесс обмена данными только с одним участником сети. Как же клиент устанавливает связь с остальными участниками? Для этого в Ethereum используется протокол DPT, основанный на UDP.

Новый клиент сначала пытается обменяться данными с узлами, которые запрограммированы работать постоянно и отвечать на все запросы. Эти узлы — так называемые bootnodes — передают клиенту данные об остальных известных им узлах, с которыми клент затем соединяется, снова используя протокол DPT. От них клиент получает информацию о ещё большем числе узлов. Процесс продолжается до тех пор, пока клиент не установит достаточно соединений, чтобы полноценно участвовать в сети.

Пишем код

Полную имплементацию вы можете найти в репозитории devp2p. Здесь я прокомментирую некоторые части кода.

const BOOTSTRAP_NODES: [&str; 10] = [ .. ]

В этой константе записываются bootnodes — постоянные узлы сети. Каждый из них представлен в виде выражения enode:://[client id]@[ip]:[port].

ETHStream — это структура, согласующая работу протоколов RLPx и DPT. Именно она отвечает за поиск узлов и обмен данными между ними. ETHStream устанавливает максимальное время ожидания ответа от остальных узлов. Если по истечении этого времени ответа нет, то ETHStream начинает регулярно отправлять запрос GetBlockHeaders, чтобы получить новые блоки от остальных участников:

client_sender = core.run(client_sender.send(ETHSendMessage {
node: RLPxNode::Any,
data: ETHMessage::GetBlockHeadersByHash {
hash: best_hash,
max_headers: req_max_headers,
skip: 0,
reverse: false,
}
})).unwrap();

Наконец, в ответ на этот запрос клиент получает заголовки блоков — BlockHeaders:

ETHMessage::BlockHeaders(ref headers) => {
println!(«received block headers of len {}», headers.len());
if got_bodies_for_current {
for header in headers {
if header.parent_hash == best_hash {
best_hash = keccak256(&rlp::encode(header).to_vec());
best_number = header.number;
println!(«updated best number: {}», header.number);
println!(«updated best hash: 0x{:x}», best_hash);
}
}
}
client_sender = core.run(client_sender.send(ETHSendMessage {
node: RLPxNode::Any,
data: ETHMessage::GetBlockHeadersByHash {
hash: best_hash,
max_headers: req_max_headers,
skip: 0,
reverse: false,
}
})).unwrap();
timeout = Timeout::new(dur, &handle).unwrap().boxed();
}

Источник: cryptocurrency.tech

Оцените автора
( Пока оценок нет )
КриптоВики
Добавить комментарий