Эта статья является первой в серии статей о исходном коде Geth. С помощью этой серии мы создадим структуру для исследования реализации Geth, и разработчики смогут углубленно изучить интересующие их части. В этой серии всего шесть статей, и в первой статье будет исследована архитектура дизайна клиента Geth на уровне исполнения и процесс запуска узла Geth. Код Geth обновляется очень быстро, и в дальнейшем увиденный код может отличаться, но общая структура остается в основном одинаковой, новый код также можно читать с той же точки зрения.
01\Клиент Ethereum
До обновления Ethereum The Merge у Ethereum был только один клиент, который отвечал за выполнение транзакций и консенсус блокчейна, гарантируя, что блокчейн производит новые блоки в определенном порядке. После обновления The Merge клиент Ethereum делится на уровень исполнения и уровень консенсуса, уровень исполнения отвечает за выполнение транзакций, поддержание состояния и данных, уровень консенсуса отвечает за реализацию функций консенсуса, а уровень исполнения и уровень консенсуса взаимодействуют через API. Уровень исполнения и уровень консенсуса имеют свои собственные спецификации, и клиент может реализовать их на разных языках, но они должны соответствовать соответствующим спецификациям, а Geth является реализацией клиента уровня выполнения. В настоящее время основные клиенты уровня исполнения и уровня консенсуса имеют следующие реализации:
Исполнительный уровень
Geth: Поддерживается командой, финансируемой фондом Ethereum, разработан на языке Go, признан самым стабильным и проверенным клиентом.
Nethermind: Разработан и поддерживается командой Nethermind, написан на языке C#, первоначально финансировался Фондом Ethereum и сообществом Gitcoin.
Besu: Изначально разработан командой PegaSys от ConsenSys, теперь является проектом сообщества Hyperledger, разработан на языке Java.
Erigon: Разработан и поддерживается командой Erigon, финансируется Фондом Ethereum и BNB Chain. В 2017 году был создан на основе Geth с целью повышения скорости синхронизации и эффективности диска.
Reth: Разработан при поддержке Paradigm, язык разработки - Rust, акцент на модульности и высокой производительности, в настоящее время уже близок к зрелости и может использоваться в производственной среде.
Слой консенсуса
Prysm: поддерживаемый Prysmatic Labs, является одним из первых клиентов уровня консенсуса для Ethereum, разработан на языке Go, сосредоточен на удобстве использования и безопасности, в начале получил финансирование от фонда Ethereum.
Lighthouse: поддерживается командой Sigma Prime, разработан на языке Rust, ориентирован на высокую производительность и корпоративную безопасность, подходит для сценариев с высокой нагрузкой.
Teku: Разработан командой PegaSys от ConsenSys, затем стал частью сообщества Hyperledger Besu, разработан на языке Java.
Nimbus: Разработан и поддерживается командой Status Network, разработан на языке Nim, оптимизирован для устройств с ограниченными ресурсами (таких как мобильные телефоны, устройства Интернета вещей), целью является легковесная работа в встраиваемых системах.
02\Обзор исполнительного уровня
Этаж выполнения Ethereum можно рассматривать как состояние, управляемое транзакциями, где основная функция этажа выполнения заключается в обновлении данных состояния за счет выполнения транзакций через EVM. Кроме выполнения транзакций, также есть функции сохранения и проверки блоков и данных состояния, работы p2p-сети и поддержания пула транзакций.
Транзакция создается пользователем (или программой) в соответствии с форматом, определенным спецификацией уровня исполнения Ethereum. Пользователь должен подписать транзакцию, и если транзакция является законной (Nonce последовательно, подпись верна, плата за газ достаточна, бизнес-логика правильна), то транзакция в конечном итоге будет выполнена EVM, что приведет к обновлению состояния сети Ethereum. Здесь состояние относится к набору структур данных, данных и баз данных, включая адреса внешних аккаунтов, адреса контрактов, балансы адресов, а также коды и данные.
Исполнительный уровень отвечает за выполнение транзакций и поддержание состояния после выполнения транзакций, уровень консенсуса отвечает за выбор транзакций для выполнения. EVM является функцией преобразования состояния в этой машине состояний, входные данные функции могут поступать из различных источников, возможно, из информации о последних блоках, предоставленной уровнем консенсуса, или из блоков, загруженных из p2p сети.
Слой согласия и слой исполнения общаются через Engine API, который является единственным способом связи между слоем исполнения и слоем согласия. Если слой согласия получает право на создание блока, он использует Engine API, чтобы заставить слой исполнения создать новый блок. Если право на создание блока не получено, он синхронизирует последние блоки, чтобы слой исполнения мог проверить и выполнить их, тем самым поддерживая согласие с всей сетью Ethereum.
Исполнительный уровень логически можно разделить на 6 частей:
EVM: отвечает за выполнение транзакций, выполнение транзакций также является единственным способом изменения состояния.
Хранение: отвечает за хранение данных состояния и блоков.
Торговый пул: используется для временного хранения транзакций, поданных пользователями, и будет передаваться через p2p сеть между различными Узелами.
p2p сеть: для обнаружения узлов, синхронизации транзакций, загрузки блоков и других функций
RPC сервис: предоставляет возможность доступа к узлу, например, пользователи могут отправлять транзакции на узел, взаимодействие между слоем консенсуса и слоем исполнения.
BlockChain: отвечает за управление данными блокчейна Ethereum
На рисунке ниже показаны ключевые процессы уровня исполнения и функции каждой части:
Для уровня выполнения (здесь мы временно обсуждаем только Узел полного типа) есть три ключевых процесса:
Если это новый присоединившийся узел Ethereum, необходимо синхронизировать блоки и данные состояния с других узлов через p2p сеть. Если это полная синхронизация (Full Sync), то начнется загрузка блоков по одному с генезис-блока, проверка блоков и восстановление базы данных состояния через EVM. Если это быстрая синхронизация (Snap Sync), то пропускается весь процесс проверки блоков, и загружаются данные состояния последней контрольной точки и последующие данные блоков.
Если узел уже синхронизирован с последним состоянием, он будет продолжать получать текущие последние сгенерированные блоки через Engine API из уровня консенсуса, проверять блоки, а затем выполнять все транзакции в блоке через EVM для обновления базы данных состояния и записывать блок в локальную цепочку.
Если узел уже синхронизирован до последнего состояния и получил право на создание блока в слое согласования, он будет использовать Engine API для запуска слоя выполнения, чтобы создать последний блок. Слой выполнения извлекает транзакции из пула транзакций и выполняет их, а затем собирает в блок, который передается в слой согласования через Engine API, откуда блок рассылается по p2p сети слоя согласования.
03\Структура исходного кода
Структура кода go-ethereum довольно большая, но большая часть кода представляет собой вспомогательный код и модульные тесты. При изучении исходного кода Geth следует сосредоточиться на основной реализации протокола. Функции различных модулей следующие. Необходимо уделить особое внимание таким модулям, как core, eth, ethdb, Узел, p2p, rlp, trie и triedb.
Учетные записи: управление учетными записями Ethereum, включая генерацию пар открытого и закрытого ключей, проверку подписи, получение адреса и т. д
beacon: Обработка логики взаимодействия с этериумом Beacon Chain, поддержка функций после слияния (The Merge) консенсуса на основе доказательства доли (PoS)
build:скрипты сборки и конфигурации компиляции (например, Dockerfile, поддержка кроссплатформенной компиляции)
cmd: Вход в инструмент командной строки, содержащий несколько подкоманд
common:Общие утилиты, такие как обработка байтов, преобразование формата адресов, математические функции
консенсус: определение механизма консенсуса, включая предыдущий Proof-of-Work (Ethash), Single-Node Proof-of-Stake (Clique), движок Beacon и т. д
консоль: предоставляет интерактивную JavaScript консоль, позволяя пользователям напрямую взаимодействовать с узлом Ethereum через командную строку (например, вызывать Web3 API, управлять счетами, запрашивать данные блокчейна)
core: Основная логика блокчейна, которая управляет жизненным циклом блоков/транзакций, конечных автоматов, газовых вычислений и т. д
docs:Документы (например, проектные спецификации, описания API)
ETH: Полная реализация сетевого протокола Ethereum, включая услуги узлов, синхронизацию блоков (например, быструю синхронизацию, режим архива), трансляцию транзакций и т. д
ethclient: Реализация библиотеки клиента Ethereum, обертывающей интерфейс JSON-RPC, для взаимодействия разработчиков Go с узлом Ethereum (например, запрос блоков, отправка транзакций, развертывание контрактов)
ethdb: абстрактный уровень базы данных, поддерживающий LevelDB, Pebble, базы данных в памяти и т.д., хранящий данные блокчейна (блоки, состояния, транзакции)
ethstats: Сбор и передача состояния работы узла в статистический сервис для мониторинга состояния сети.
событие: Реализуйте механизм подписки и публикации событий, а также поддерживайте асинхронное взаимодействие между модулями внутри узла (например, поступление нового блока, обновление пула транзакций)
graphql: предоставляет интерфейс GraphQL, поддерживает сложные запросы (замена части функций JSON-RPC)
internal:Внутренние инструменты или код, ограничивающий доступ извне
log: Система журналирования, поддерживающая многоуровневый вывод журналов и запись контекстных журналов
miner: Логика, связанная с добычей, генерация новых блоков и упаковка транзакций (в контексте PoW)
узел:Управление сервисами узла, интеграция запуска и конфигурации модулей p2p, RPC, базы данных и др.
p2p: реализация протокола одноранговой сети, поддерживающая обнаружение узлов, передачу данных, шифрованную связь
params:Определение параметров сети Ethereum (основная сеть, тестовая сеть, конфигурация генезис-блока)
rlp: Реализация специализированного протокола сериализации данных для Ethereum RLP (Рекурсивный префикс длины), используемого для кодирования/декодирования структур данных, таких как блоки и транзакции.
rpc: Реализует интерфейсы JSON-RPC и IPC для взаимодействия внешних программ с узлом
тесты: интеграционные тесты и тесты состояния, проверка совместимости протокола
trie & triedb: Реализация дерева Меркла Патриции (Merkle Patricia Trie) для эффективного хранения и управления состоянием аккаунтов, хранением контрактов.
04\Разделение модуля исполнительного уровня
Существует два способа внешнего доступа к узлу Geth: один через RPC, другой через Console. RPC подходит для использования внешними пользователями, а Console подходит для администраторов узла. Но независимо от того, используете ли вы RPC или Console, все это использует уже встроенные возможности, которые построены с помощью многоуровневого подхода.
На внешнем уровне находятся API, которые предоставляют различные возможности для доступа к узлу. Engine API используется для связи между уровнем выполнения и уровнем консенсуса, Eth API предназначен для внешних пользователей или программ для отправки транзакций и получения информации о блоках, а Net API используется для получения состояния p2p сети и так далее. Например, если пользователь отправил транзакцию через API, то эта транзакция в конечном итоге будет отправлена в пул транзакций для управления. Если пользователь хочет получить данные о блоке, то необходимо использовать возможности базы данных для получения соответствующего блока.
На следующем уровне API реализация основных функций включает в себя пул транзакций, упаковку транзакций, производство блоков, синхронизацию блоков и состояний и т. д. Эти функции должны опираться на низкоуровневые возможности, такие как способность сети P2P синхронизировать пулы транзакций, блоки и состояния, а создание блоков и блоков, синхронизированных с другими узлами, должно быть проверено, прежде чем они могут быть записаны в локальную базу данных, которая должна полагаться на EVM и возможности хранения данных.
Основная структура данных исполнительного уровня
Эфириум
Структура Ethereum в eth/backend.go является абстракцией всего протокола Ethereum и, в основном, включает в себя основные компоненты Ethereum, но EVM является исключением, так как он экземплярируется каждый раз при обработке транзакции и не требует инициализации всего узла. В дальнейшем под Ethereum будет пониматься именно эта структура:
type Ethereum struct { // Конфигурация Ethereum, включая конфигурацию цепочки *ethconfig. Config // Пул транзакций, после отправки транзакции пользователя переходим в пул транзакций txPool *txpool. TxPool // Используется для отслеживания и управления локальными транзакциями localTxTracker *locals. TxTracker // Структура блокчейна *core. BlockChain // является основным компонентом сетевого уровня узла Ethereum, отвечающим за обработку всех коммуникаций с другими узлами, включая синхронизацию блоков, трансляцию и получение транзакций, а также за управление обработчиком соединения однорангового узла // отвечает за обнаружение узлов и управление исходным кодом узла discmix *enode. FairMix // Отвечает за постоянное хранение цепочки данных блокчейнаDb ethdb. База данных // Отвечает за публикацию и подписку на различные внутренние события в eventMux *event. TypeMux // Консенсус движка. Engine // Управление учетными записями пользователей и ключами accountManager *accounts. Manager // Управление фильтрами журналов и chunk filterMaps *filtermaps. FilterMaps // Канал для безопасного завершения работы filterMaps, гарантирующий, что ресурсы будут правильно очищены при выключении узлов closeFilterMaps chan chan struct{} // Обеспечьте поддержку бэкенда для RPC API APIBackend *EthAPIBackend // В PoS работайте с механизмом консенсуса для проверки майнера блока *miner. Miner // Самая низкая цена газа, принятая нодой, равна gasPrice *big. Int // Идентификатор сети networkID uint64 // Предоставление сетевых RPC-сервисов, позволяющих запрашивать состояние сети через RPC netRPCService *ethapi. NetAPI // Управление сетевыми соединениями P2P, обнаружение узлов и установление соединений, а также предоставление базовых сетевых транспортных функций p2pServer *p2p. Сервер // Защита параллельного доступа к синхронизации блокировки изменяемых полей. RWMutex // Отслеживает, корректно ли не работает узел, и помогает восстановить shutdownTracker после аварийного завершения работы *shutdowncheck. ShutdownTracker }
Узел
В Node, находящемся в node/node.go, является еще одной основной структурой данных, которая служит контейнером, отвечающим за управление и координацию работы различных сервисов. В приведенной ниже структуре следует обратить внимание на поле lifecycles, которое используется для управления жизненным циклом внутренних функций. Например, вышеупомянутая абстракция Ethereum зависит от Node для запуска и регистрируется в lifecycles. Это позволяет разделить конкретные функции и абстракцию узла, повышая общую расширяемость архитектуры. Этот Node необходимо отличать от Node в devp2p.
type Node struct { eventmux *event. TypeMux config *Config // Менеджер аккаунтов, отвечающий за управление кошельками и аккаунтами аккаунтов accman *accounts. Журнал журнала менеджера. Logger keyDir string keyDirTemp bool dirLock *flock. Flock stop chan struct{} // сервер экземпляра сети p2p *p2p. Синхронизация с запуском сервера StopLock. Мьютекс // Отслеживание состояния жизненного цикла узла (инициализированный, работающий, закрытый) int lock sync. Мьютекс // Все зарегистрированные бэкенды, сервисы и вспомогательные сервисы жизненные циклы []Жизненный цикл // Список API, доступных в настоящее время rpcAPI []rpc. API // Различные методы доступа для RPC http *httpServer ws *httpServer httpAuth *httpServer wsAuth *httpServer ipc *ipcServer inprocHandler *rpc. Серверные базы данных map[*closeTrackingDB]struct{} }
Если мы посмотрим на уровень исполнения Ethereum из абстрактного измерения, то Ethereum, как мировой компьютер, должен включать в себя три части: сеть, вычисления и хранилище, то компоненты, соответствующие этим трем частям в уровне исполнения Ethereum, следующие:
Сеть: devp2p
Вычисление: EVM
Хранение: ethdb
devp2p
Эфир по сути является распределенной системой, каждый узел соединен с другими узлами через p2p сеть. Реализация p2p сетевого протокола в Эфире называется devp2p.
devp2p имеет две основные функции: первая — это обнаружение узлов, позволяющее узлам устанавливать связь с другими узлами при подключении к сети; вторая — это сервис передачи данных, который позволяет обмениваться данными после установления связи с другими узлами.
Структура Node в p2p/enode/node.go представляет собой узел в p2p-сети, где enr. В структуре Record хранятся пары ключ-значение сведений об узле, включая идентификационную информацию (алгоритм подписи, используемый удостоверением узла, открытый ключ), информацию о сети (IP-адрес, номер порта), информацию о поддерживаемом протоколе (например, поддержку протоколов eth/68 и snap) и другую пользовательскую информацию, которая закодирована в RLP и определена в EIP-778:
type Node struct { // Запись узла, содержащая различные свойства узла r enr. Запись // Уникальный идентификатор узла длиной 32 байта id ID // имя хоста Трассировка DNS-имени узла hostname string // IP-адрес узла ip netip. Addr // UDP порт udp uint16 // TCP порт tcp uint16 }// enr. Recordtype Record struct { // Серийный номер seq uint64 // Подпись подписи []byte // Запись в кодировке RLP raw []byte // Отсортированный список всех пар ключ-значение: пары []pair }
Структура Table в p2p/discover/table.go является основной структурой данных для реализации протокола обнаружения узлов devp2p, она реализует распределенную хэш-таблицу, аналогичную Kademlia, для поддержания и управления информацией о узлах в сети.
printf("type Table struct { mutex sync. Мьютекс // Индексируем известные сегменты узлов по расстоянию [nBuckets]*bucket // начальная нода nursery []*enode. Node rand reseedingRandom ips netutil. DistinctNetSet revalidation tableRevalidation // База данных известных узлов db *enode. Сетевой транспорт БД cfg Журнал конфигураций. Logger // Периодически обрабатывает различные события в сети refreshReq chan chan struct{} revalResponseCh chan revalidationResponse addNodeCh chan addNodeOp addNodeHandled chan bool trackRequestCh chan trackRequestOp initDone chan struct{} closeReq chan struct{} closed chan struct{} // Добавление и удаление интерфейсов для узлов nodes nodeAddedHook func(*bucket, *tableNode) nodeRemovedHook func(*ведро, *tableNode)} мир!" );
ethdb
EthDB завершает абстракцию хранилища данных Ethereum и предоставляет унифицированный интерфейс хранения, а базовой конкретной базой данных может быть levelDB, Pebble или другие базы данных. Расширений может быть много, если они согласованы на уровне интерфейса.
Некоторые данные (такие как данные блоков) могут быть прочитаны и записаны напрямую в базу данных через интерфейс ethdb, другие интерфейсы хранения данных строятся на основе ethdb. Например, большая часть данных в базе данных является состоянием, и эти данные организуются в структуру MPT, соответствующая реализация в Geth - это trie. В процессе работы узла данные trie создают множество промежуточных состояний, которые не могут быть напрямую прочитаны и записаны через ethdb; для управления этими данными и промежуточными состояниями требуется triedb, и только затем данные персистируются через ethdb.
Интерфейс, определяющий возможности чтения и записи базовой базы данных в ethdb/database.go, не включает в себя конкретную реализацию, которая будет реализована самими базами данных. Например, базы данных leveldb или pebble. В базе данных определены два уровня интерфейсов чтения/записи данных, среди которых интерфейс KeyValueStore используется для хранения активных и часто изменяющихся данных, таких как последний блок, состояние и т. д. AncientStore используется для обработки исторических данных блоков, которые редко изменяются после записи.
type Database interface { KeyValueStore AncientStore}// Type KeyValueStore interface { KeyValueReader, KeyValueWriter, KeyValueStater, KeyValueRangeDeleter Batcher Iteratee Compacter io. Closer}// type AncientStore interface { AncientReader AncientWriter AncientStater io. Ближе}
EVM
EVM является функцией преобразования состояния этого состояния машины Ethereum, все обновления данных состояния могут происходить только через EVM. P2P сеть может получать информацию о транзакциях и блоках, эта информация после обработки EVM станет частью базы данных состояния. EVM скрывает различия в аппаратном обеспечении, позволяя программам получать одинаковые результаты при выполнении на различных платформах EVM. Это очень зрелый способ разработки, аналогичный дизайну JVM в языке Java.
Основные компоненты реализации EVM включают три основных элемента: структура EVM, определенная в core/vm/evm.go, описывает общую структуру EVM и зависимости, включая контекст выполнения, зависимости от базы данных состояния и так далее; структура EVMInterpreter, определенная в core/vm/interpreter.go, реализует интерпретатор, который отвечает за выполнение байт-кода EVM; структура Contract, заключенная в core/vm/contract.go, инкапсулирует конкретные параметры вызова контракта, включая вызывающего, код контракта, входные данные и так далее, и в core/vm/opcodes.go определены все текущие операционные коды:
EVMtype EVM struct { // Контекст блока, содержащий информацию о блоке Context BlockContext // Контекст транзакции, содержащий информацию о транзакции TxContext // База данных состояний, используемая для доступа и изменения состояния аккаунта StateDB StateDB // Текущая глубина вызова int // Параметр конфигурации цепочки chainConfig *params. ChainConfig параметры chainRules. Rules // EVM Config Config // Интерпретатор байт-кода *EVMInterpreter // Флаг прерывания выполнения abort atomic. Bool callGasTemp uint64 // прекомпилирует map[common. Address]PrecompiledContract jumpDests map[common. Hash]bitvec }type EVMInterpreter struct { // Указываем на экземпляр EVM, к которому он принадлежит, evm *EVM // Таблица переходов по кодам операций *JumpTable // Экземпляр хешера Keccak256, делим криптографию хешера между опкодами. KeccakState // Хеш-буфер результата Keccak256 hasherBuf common. Hash // В режиме только для чтения изменение состояния не допускается readOnly bool // Возвращаемые данные последнего CALL используются для последующего повторного использования returnData []byte }type Contract struct { // адрес вызывающего абонента, общий. Адрес // Адрес адреса контракта общий. Address jumpdests map[common. Hash]bitvec analysis bitvec // Код байт-кода контракта []byte // Хэш кода CodeHash common. Hash // Вызов input []byte // Развертывать ли IsDeployment bool для контракта // Вызывать ли IsSystemCall bool // Доступный газ gas uint64 // Количество ETH, прикрепленное к значению вызова *uint256. Int }
Другие модули реализации
Функции уровня выполнения реализуются с помощью иерархической структуры, а другие модули и функции строятся на основе этих трех основных компонентов. Здесь представлено несколько основных модулей.
В разделе eth/protocols реализованы текущие подсProtocols p2p сети Ethereum. Есть подсProtocols eth/68 и snap, которые построены на devp2p.
eth/68 является核心协议 Ethereum, название протокола - eth, 68 - это его номер версии, затем на основе этого протокола реализованы функции, такие как пул транзакций (TxPool), синхронизация блоков (Downloader) и синхронизация транзакций (Fetcher). Протокол snap используется для быстрой синхронизации блоков и состояния данных при добавлении нового узла в сеть, что значительно сокращает время запуска нового узла.
ethdb предоставляет возможности чтения и записи для базового уровня базы данных. Поскольку в протоколе Ethereum много сложных структур данных, управление этими данными невозможно напрямую через ethdb. Поэтому на основе ethdb были реализованы rawdb и statedb для управления данными блоков и состояния соответственно.
EVM пронизывает все основные процессы, независимо от того, идет ли речь о построении блока или проверке блока, для выполнения транзакций требуется использовать EVM.
05\Запуск узла Geth
Запуск Geth будет разделен на два этапа: на первом этапе будут инициализированы компоненты и ресурсы, необходимые для запуска узла, а на втором этапе узел будет официально запущен и начнет предоставлять услуги.
Инициализация узла
При запуске узла geth будет использоваться следующий код:
Инициализация каждого модуля следующая:
cmd/geth/main.go:точка входа для запуска узла geth
eth/ethconfig/config.go: Инициализация экземпляра консенсусного движка (консенсусный движок здесь не участвует в консенсусе, он только проверяет результаты уровня консенсуса и обрабатывает запросы на вывод средств от валидаторов)
core/blockchain.go: инициализация блокчейна
core/filterMaps.go: Инициализация карт фильтров
core/txpool/blobpool/blobpool.go: инициализация пула транзакций blob
core/txpool/legacypool/legacypool.go: инициализация обычного пула транзакций
cord/txpool/locals/tx_tracker.go: Локальное отслеживание транзакций (необходимо настроить для включения локального отслеживания транзакций, локальные транзакции будут обрабатываться с более высоким приоритетом)
узел/node.go(RegisterLifecycle):Регистрация жизненного цикла различных компонентов
cmd/utils/flags.go(RegisterFilterAPI): регистрирует API RPC фильтра
cmd/utils/flags.go(RegisterGraphQLService): Регистрация API RPC GraphQL (если настроено)
cmd/utils/flags.go(RegisterEthStatsService): Регистрация EthStats RPC API (если настроено)
eth/catalyst/api.go: Зарегистрируйте API движка
Инициализация узла будет выполнена в makeFullNode в файле cmd/geth/config.go, основное внимание будет уделено инициализации следующих трех модулей.
На первом шаге будет инициализирована структура Node в файле node/node.go, которая представляет собой контейнер для узлов, все функции должны выполняться в этом контейнере. На втором шаге будет инициализирована структура Ethereum, которая включает реализацию различных основных функций Ethereum, Etherereum также необходимо зарегистрировать в узле. Третий шаг заключается в регистрации Engine API в узле.
Инициализация узла заключается в создании экземпляра узла, а затем инициализации p2p-сервера, управления аккаунтами и протокольных портов http, доступных для внешних соединений.
Инициализация Ethereum будет намного сложнее, большинство основных функций инициализируются здесь. Сначала будет инициализирован ethdb и загружены конфигурации цепи из хранилища, затем будет создан механизм консенсуса, который не будет выполнять операции консенсуса, а только проверять результаты, возвращаемые уровнем консенсуса. Если уровень консенсуса получит запрос на вывод средств, здесь также будет выполнена фактическая операция вывода. Затем будет инициализирована структура блокчейна и пул транзакций.
Как только это сделано, инициализируется обработчик, который является точкой входа для всех запросов P2P-сети, включая синхронизацию транзакций, загрузку блоков и т. д., и является ключевым компонентом децентрализованной работы Ethereum. После того, как они будут завершены, некоторые подпротоколы, реализованные на основе devp2p, такие как eth/68, snap и т.д., будут зарегистрированы в контейнере Node, и, наконец, Ethereum будет зарегистрирован в качестве жизненного цикла в контейнере Node, и инициализация Ethereum будет завершена.
В конце инициализация Engine API относительно проста, просто зарегистрируйте Engine API в Узле. На этом инициализация узла полностью завершена.
Запуск узла
После завершения инициализации узла необходимо запустить узел. Процесс запуска узла относительно прост: нужно запустить все зарегистрированные RPC-сервисы и Lifecycle, и тогда весь узел сможет предоставлять услуги внешнему миру.
Перед тем как углубиться в реализацию уровня исполнения Ethereum, необходимо иметь общее представление о самом Ethereum. Весь Ethereum можно рассматривать как машину состояния, управляемую транзакциями, где уровень исполнения отвечает за выполнение транзакций и изменение состояния, а уровень консенсуса отвечает за управление работой уровня исполнения, включая создание блоков, определение порядка транзакций, голосование за блоки и предоставление блокам окончательности. Поскольку эта машина состояния децентрализована, необходимо общаться с другими узлами через p2p сеть для совместного поддержания согласованности данных состояния.
Исполнительный уровень не отвечает за определение порядка транзакций, а только за выполнение транзакций и запись изменений состояния после их выполнения. Здесь запись имеет две формы: одна - это запись всех изменений состояния в виде блоков, другая - это запись текущего состояния в базе данных. В то же время исполнительный уровень также является входом для транзакций, храня транзакции, которые еще не упакованы в блок, в пуле транзакций. Если другие узлы нуждаются в получении данных о блоках, состоянии и транзакциях, исполнительный уровень отправит эту информацию через p2p сеть.
Для исполнительного уровня есть три основных модуля: вычисления, хранения и сети. Вычисления соответствуют реализации EVM, хранение соответствует реализации ethdb, а сеть соответствует реализации devp2p. Имея такое общее понимание, можно углубиться в изучение каждого подсмодуля, не теряясь в конкретных деталях.
Содержание носит исключительно справочный характер и не является предложением или офертой. Консультации по инвестициям, налогообложению или юридическим вопросам не предоставляются. Более подробную информацию о рисках см. в разделе «Дисклеймер».
Основной клиент Ethereum: Архитектура Geth
Эта статья является первой в серии статей о исходном коде Geth. С помощью этой серии мы создадим структуру для исследования реализации Geth, и разработчики смогут углубленно изучить интересующие их части. В этой серии всего шесть статей, и в первой статье будет исследована архитектура дизайна клиента Geth на уровне исполнения и процесс запуска узла Geth. Код Geth обновляется очень быстро, и в дальнейшем увиденный код может отличаться, но общая структура остается в основном одинаковой, новый код также можно читать с той же точки зрения.
01\Клиент Ethereum
До обновления Ethereum The Merge у Ethereum был только один клиент, который отвечал за выполнение транзакций и консенсус блокчейна, гарантируя, что блокчейн производит новые блоки в определенном порядке. После обновления The Merge клиент Ethereum делится на уровень исполнения и уровень консенсуса, уровень исполнения отвечает за выполнение транзакций, поддержание состояния и данных, уровень консенсуса отвечает за реализацию функций консенсуса, а уровень исполнения и уровень консенсуса взаимодействуют через API. Уровень исполнения и уровень консенсуса имеют свои собственные спецификации, и клиент может реализовать их на разных языках, но они должны соответствовать соответствующим спецификациям, а Geth является реализацией клиента уровня выполнения. В настоящее время основные клиенты уровня исполнения и уровня консенсуса имеют следующие реализации:
Исполнительный уровень
Слой консенсуса
02\Обзор исполнительного уровня
Этаж выполнения Ethereum можно рассматривать как состояние, управляемое транзакциями, где основная функция этажа выполнения заключается в обновлении данных состояния за счет выполнения транзакций через EVM. Кроме выполнения транзакций, также есть функции сохранения и проверки блоков и данных состояния, работы p2p-сети и поддержания пула транзакций.
Транзакция создается пользователем (или программой) в соответствии с форматом, определенным спецификацией уровня исполнения Ethereum. Пользователь должен подписать транзакцию, и если транзакция является законной (Nonce последовательно, подпись верна, плата за газ достаточна, бизнес-логика правильна), то транзакция в конечном итоге будет выполнена EVM, что приведет к обновлению состояния сети Ethereum. Здесь состояние относится к набору структур данных, данных и баз данных, включая адреса внешних аккаунтов, адреса контрактов, балансы адресов, а также коды и данные.
Исполнительный уровень отвечает за выполнение транзакций и поддержание состояния после выполнения транзакций, уровень консенсуса отвечает за выбор транзакций для выполнения. EVM является функцией преобразования состояния в этой машине состояний, входные данные функции могут поступать из различных источников, возможно, из информации о последних блоках, предоставленной уровнем консенсуса, или из блоков, загруженных из p2p сети.
Слой согласия и слой исполнения общаются через Engine API, который является единственным способом связи между слоем исполнения и слоем согласия. Если слой согласия получает право на создание блока, он использует Engine API, чтобы заставить слой исполнения создать новый блок. Если право на создание блока не получено, он синхронизирует последние блоки, чтобы слой исполнения мог проверить и выполнить их, тем самым поддерживая согласие с всей сетью Ethereum.
Исполнительный уровень логически можно разделить на 6 частей:
На рисунке ниже показаны ключевые процессы уровня исполнения и функции каждой части:
Для уровня выполнения (здесь мы временно обсуждаем только Узел полного типа) есть три ключевых процесса:
03\Структура исходного кода
Структура кода go-ethereum довольно большая, но большая часть кода представляет собой вспомогательный код и модульные тесты. При изучении исходного кода Geth следует сосредоточиться на основной реализации протокола. Функции различных модулей следующие. Необходимо уделить особое внимание таким модулям, как core, eth, ethdb, Узел, p2p, rlp, trie и triedb.
04\Разделение модуля исполнительного уровня
Существует два способа внешнего доступа к узлу Geth: один через RPC, другой через Console. RPC подходит для использования внешними пользователями, а Console подходит для администраторов узла. Но независимо от того, используете ли вы RPC или Console, все это использует уже встроенные возможности, которые построены с помощью многоуровневого подхода.
На внешнем уровне находятся API, которые предоставляют различные возможности для доступа к узлу. Engine API используется для связи между уровнем выполнения и уровнем консенсуса, Eth API предназначен для внешних пользователей или программ для отправки транзакций и получения информации о блоках, а Net API используется для получения состояния p2p сети и так далее. Например, если пользователь отправил транзакцию через API, то эта транзакция в конечном итоге будет отправлена в пул транзакций для управления. Если пользователь хочет получить данные о блоке, то необходимо использовать возможности базы данных для получения соответствующего блока.
На следующем уровне API реализация основных функций включает в себя пул транзакций, упаковку транзакций, производство блоков, синхронизацию блоков и состояний и т. д. Эти функции должны опираться на низкоуровневые возможности, такие как способность сети P2P синхронизировать пулы транзакций, блоки и состояния, а создание блоков и блоков, синхронизированных с другими узлами, должно быть проверено, прежде чем они могут быть записаны в локальную базу данных, которая должна полагаться на EVM и возможности хранения данных.
Основная структура данных исполнительного уровня
Эфириум
Структура Ethereum в eth/backend.go является абстракцией всего протокола Ethereum и, в основном, включает в себя основные компоненты Ethereum, но EVM является исключением, так как он экземплярируется каждый раз при обработке транзакции и не требует инициализации всего узла. В дальнейшем под Ethereum будет пониматься именно эта структура:
type Ethereum struct { // Конфигурация Ethereum, включая конфигурацию цепочки *ethconfig. Config // Пул транзакций, после отправки транзакции пользователя переходим в пул транзакций txPool *txpool. TxPool // Используется для отслеживания и управления локальными транзакциями localTxTracker *locals. TxTracker // Структура блокчейна *core. BlockChain // является основным компонентом сетевого уровня узла Ethereum, отвечающим за обработку всех коммуникаций с другими узлами, включая синхронизацию блоков, трансляцию и получение транзакций, а также за управление обработчиком соединения однорангового узла // отвечает за обнаружение узлов и управление исходным кодом узла discmix *enode. FairMix // Отвечает за постоянное хранение цепочки данных блокчейнаDb ethdb. База данных // Отвечает за публикацию и подписку на различные внутренние события в eventMux *event. TypeMux // Консенсус движка. Engine // Управление учетными записями пользователей и ключами accountManager *accounts. Manager // Управление фильтрами журналов и chunk filterMaps *filtermaps. FilterMaps // Канал для безопасного завершения работы filterMaps, гарантирующий, что ресурсы будут правильно очищены при выключении узлов closeFilterMaps chan chan struct{} // Обеспечьте поддержку бэкенда для RPC API APIBackend *EthAPIBackend // В PoS работайте с механизмом консенсуса для проверки майнера блока *miner. Miner // Самая низкая цена газа, принятая нодой, равна gasPrice *big. Int // Идентификатор сети networkID uint64 // Предоставление сетевых RPC-сервисов, позволяющих запрашивать состояние сети через RPC netRPCService *ethapi. NetAPI // Управление сетевыми соединениями P2P, обнаружение узлов и установление соединений, а также предоставление базовых сетевых транспортных функций p2pServer *p2p. Сервер // Защита параллельного доступа к синхронизации блокировки изменяемых полей. RWMutex // Отслеживает, корректно ли не работает узел, и помогает восстановить shutdownTracker после аварийного завершения работы *shutdowncheck. ShutdownTracker }
Узел
В Node, находящемся в node/node.go, является еще одной основной структурой данных, которая служит контейнером, отвечающим за управление и координацию работы различных сервисов. В приведенной ниже структуре следует обратить внимание на поле lifecycles, которое используется для управления жизненным циклом внутренних функций. Например, вышеупомянутая абстракция Ethereum зависит от Node для запуска и регистрируется в lifecycles. Это позволяет разделить конкретные функции и абстракцию узла, повышая общую расширяемость архитектуры. Этот Node необходимо отличать от Node в devp2p.
type Node struct { eventmux *event. TypeMux config *Config // Менеджер аккаунтов, отвечающий за управление кошельками и аккаунтами аккаунтов accman *accounts. Журнал журнала менеджера. Logger keyDir string keyDirTemp bool dirLock *flock. Flock stop chan struct{} // сервер экземпляра сети p2p *p2p. Синхронизация с запуском сервера StopLock. Мьютекс // Отслеживание состояния жизненного цикла узла (инициализированный, работающий, закрытый) int lock sync. Мьютекс // Все зарегистрированные бэкенды, сервисы и вспомогательные сервисы жизненные циклы []Жизненный цикл // Список API, доступных в настоящее время rpcAPI []rpc. API // Различные методы доступа для RPC http *httpServer ws *httpServer httpAuth *httpServer wsAuth *httpServer ipc *ipcServer inprocHandler *rpc. Серверные базы данных map[*closeTrackingDB]struct{} }
Если мы посмотрим на уровень исполнения Ethereum из абстрактного измерения, то Ethereum, как мировой компьютер, должен включать в себя три части: сеть, вычисления и хранилище, то компоненты, соответствующие этим трем частям в уровне исполнения Ethereum, следующие:
devp2p
Эфир по сути является распределенной системой, каждый узел соединен с другими узлами через p2p сеть. Реализация p2p сетевого протокола в Эфире называется devp2p.
devp2p имеет две основные функции: первая — это обнаружение узлов, позволяющее узлам устанавливать связь с другими узлами при подключении к сети; вторая — это сервис передачи данных, который позволяет обмениваться данными после установления связи с другими узлами.
Структура Node в p2p/enode/node.go представляет собой узел в p2p-сети, где enr. В структуре Record хранятся пары ключ-значение сведений об узле, включая идентификационную информацию (алгоритм подписи, используемый удостоверением узла, открытый ключ), информацию о сети (IP-адрес, номер порта), информацию о поддерживаемом протоколе (например, поддержку протоколов eth/68 и snap) и другую пользовательскую информацию, которая закодирована в RLP и определена в EIP-778:
type Node struct { // Запись узла, содержащая различные свойства узла r enr. Запись // Уникальный идентификатор узла длиной 32 байта id ID // имя хоста Трассировка DNS-имени узла hostname string // IP-адрес узла ip netip. Addr // UDP порт udp uint16 // TCP порт tcp uint16 }// enr. Recordtype Record struct { // Серийный номер seq uint64 // Подпись подписи []byte // Запись в кодировке RLP raw []byte // Отсортированный список всех пар ключ-значение: пары []pair }
Структура Table в p2p/discover/table.go является основной структурой данных для реализации протокола обнаружения узлов devp2p, она реализует распределенную хэш-таблицу, аналогичную Kademlia, для поддержания и управления информацией о узлах в сети.
printf("type Table struct { mutex sync. Мьютекс // Индексируем известные сегменты узлов по расстоянию [nBuckets]*bucket // начальная нода nursery []*enode. Node rand reseedingRandom ips netutil. DistinctNetSet revalidation tableRevalidation // База данных известных узлов db *enode. Сетевой транспорт БД cfg Журнал конфигураций. Logger // Периодически обрабатывает различные события в сети refreshReq chan chan struct{} revalResponseCh chan revalidationResponse addNodeCh chan addNodeOp addNodeHandled chan bool trackRequestCh chan trackRequestOp initDone chan struct{} closeReq chan struct{} closed chan struct{} // Добавление и удаление интерфейсов для узлов nodes nodeAddedHook func(*bucket, *tableNode) nodeRemovedHook func(*ведро, *tableNode)} мир!" );
ethdb
EthDB завершает абстракцию хранилища данных Ethereum и предоставляет унифицированный интерфейс хранения, а базовой конкретной базой данных может быть levelDB, Pebble или другие базы данных. Расширений может быть много, если они согласованы на уровне интерфейса.
Некоторые данные (такие как данные блоков) могут быть прочитаны и записаны напрямую в базу данных через интерфейс ethdb, другие интерфейсы хранения данных строятся на основе ethdb. Например, большая часть данных в базе данных является состоянием, и эти данные организуются в структуру MPT, соответствующая реализация в Geth - это trie. В процессе работы узла данные trie создают множество промежуточных состояний, которые не могут быть напрямую прочитаны и записаны через ethdb; для управления этими данными и промежуточными состояниями требуется triedb, и только затем данные персистируются через ethdb.
Интерфейс, определяющий возможности чтения и записи базовой базы данных в ethdb/database.go, не включает в себя конкретную реализацию, которая будет реализована самими базами данных. Например, базы данных leveldb или pebble. В базе данных определены два уровня интерфейсов чтения/записи данных, среди которых интерфейс KeyValueStore используется для хранения активных и часто изменяющихся данных, таких как последний блок, состояние и т. д. AncientStore используется для обработки исторических данных блоков, которые редко изменяются после записи.
type Database interface { KeyValueStore AncientStore}// Type KeyValueStore interface { KeyValueReader, KeyValueWriter, KeyValueStater, KeyValueRangeDeleter Batcher Iteratee Compacter io. Closer}// type AncientStore interface { AncientReader AncientWriter AncientStater io. Ближе}
EVM
EVM является функцией преобразования состояния этого состояния машины Ethereum, все обновления данных состояния могут происходить только через EVM. P2P сеть может получать информацию о транзакциях и блоках, эта информация после обработки EVM станет частью базы данных состояния. EVM скрывает различия в аппаратном обеспечении, позволяя программам получать одинаковые результаты при выполнении на различных платформах EVM. Это очень зрелый способ разработки, аналогичный дизайну JVM в языке Java.
Основные компоненты реализации EVM включают три основных элемента: структура EVM, определенная в core/vm/evm.go, описывает общую структуру EVM и зависимости, включая контекст выполнения, зависимости от базы данных состояния и так далее; структура EVMInterpreter, определенная в core/vm/interpreter.go, реализует интерпретатор, который отвечает за выполнение байт-кода EVM; структура Contract, заключенная в core/vm/contract.go, инкапсулирует конкретные параметры вызова контракта, включая вызывающего, код контракта, входные данные и так далее, и в core/vm/opcodes.go определены все текущие операционные коды:
EVMtype EVM struct { // Контекст блока, содержащий информацию о блоке Context BlockContext // Контекст транзакции, содержащий информацию о транзакции TxContext // База данных состояний, используемая для доступа и изменения состояния аккаунта StateDB StateDB // Текущая глубина вызова int // Параметр конфигурации цепочки chainConfig *params. ChainConfig параметры chainRules. Rules // EVM Config Config // Интерпретатор байт-кода *EVMInterpreter // Флаг прерывания выполнения abort atomic. Bool callGasTemp uint64 // прекомпилирует map[common. Address]PrecompiledContract jumpDests map[common. Hash]bitvec }type EVMInterpreter struct { // Указываем на экземпляр EVM, к которому он принадлежит, evm *EVM // Таблица переходов по кодам операций *JumpTable // Экземпляр хешера Keccak256, делим криптографию хешера между опкодами. KeccakState // Хеш-буфер результата Keccak256 hasherBuf common. Hash // В режиме только для чтения изменение состояния не допускается readOnly bool // Возвращаемые данные последнего CALL используются для последующего повторного использования returnData []byte }type Contract struct { // адрес вызывающего абонента, общий. Адрес // Адрес адреса контракта общий. Address jumpdests map[common. Hash]bitvec analysis bitvec // Код байт-кода контракта []byte // Хэш кода CodeHash common. Hash // Вызов input []byte // Развертывать ли IsDeployment bool для контракта // Вызывать ли IsSystemCall bool // Доступный газ gas uint64 // Количество ETH, прикрепленное к значению вызова *uint256. Int }
Другие модули реализации
Функции уровня выполнения реализуются с помощью иерархической структуры, а другие модули и функции строятся на основе этих трех основных компонентов. Здесь представлено несколько основных модулей.
В разделе eth/protocols реализованы текущие подсProtocols p2p сети Ethereum. Есть подсProtocols eth/68 и snap, которые построены на devp2p.
eth/68 является核心协议 Ethereum, название протокола - eth, 68 - это его номер версии, затем на основе этого протокола реализованы функции, такие как пул транзакций (TxPool), синхронизация блоков (Downloader) и синхронизация транзакций (Fetcher). Протокол snap используется для быстрой синхронизации блоков и состояния данных при добавлении нового узла в сеть, что значительно сокращает время запуска нового узла.
ethdb предоставляет возможности чтения и записи для базового уровня базы данных. Поскольку в протоколе Ethereum много сложных структур данных, управление этими данными невозможно напрямую через ethdb. Поэтому на основе ethdb были реализованы rawdb и statedb для управления данными блоков и состояния соответственно.
EVM пронизывает все основные процессы, независимо от того, идет ли речь о построении блока или проверке блока, для выполнения транзакций требуется использовать EVM.
05\Запуск узла Geth
Запуск Geth будет разделен на два этапа: на первом этапе будут инициализированы компоненты и ресурсы, необходимые для запуска узла, а на втором этапе узел будет официально запущен и начнет предоставлять услуги.
Инициализация узла
При запуске узла geth будет использоваться следующий код:
Инициализация каждого модуля следующая:
Инициализация узла будет выполнена в makeFullNode в файле cmd/geth/config.go, основное внимание будет уделено инициализации следующих трех модулей.
На первом шаге будет инициализирована структура Node в файле node/node.go, которая представляет собой контейнер для узлов, все функции должны выполняться в этом контейнере. На втором шаге будет инициализирована структура Ethereum, которая включает реализацию различных основных функций Ethereum, Etherereum также необходимо зарегистрировать в узле. Третий шаг заключается в регистрации Engine API в узле.
Инициализация узла заключается в создании экземпляра узла, а затем инициализации p2p-сервера, управления аккаунтами и протокольных портов http, доступных для внешних соединений.
Инициализация Ethereum будет намного сложнее, большинство основных функций инициализируются здесь. Сначала будет инициализирован ethdb и загружены конфигурации цепи из хранилища, затем будет создан механизм консенсуса, который не будет выполнять операции консенсуса, а только проверять результаты, возвращаемые уровнем консенсуса. Если уровень консенсуса получит запрос на вывод средств, здесь также будет выполнена фактическая операция вывода. Затем будет инициализирована структура блокчейна и пул транзакций.
Как только это сделано, инициализируется обработчик, который является точкой входа для всех запросов P2P-сети, включая синхронизацию транзакций, загрузку блоков и т. д., и является ключевым компонентом децентрализованной работы Ethereum. После того, как они будут завершены, некоторые подпротоколы, реализованные на основе devp2p, такие как eth/68, snap и т.д., будут зарегистрированы в контейнере Node, и, наконец, Ethereum будет зарегистрирован в качестве жизненного цикла в контейнере Node, и инициализация Ethereum будет завершена.
В конце инициализация Engine API относительно проста, просто зарегистрируйте Engine API в Узле. На этом инициализация узла полностью завершена.
Запуск узла
После завершения инициализации узла необходимо запустить узел. Процесс запуска узла относительно прост: нужно запустить все зарегистрированные RPC-сервисы и Lifecycle, и тогда весь узел сможет предоставлять услуги внешнему миру.
! Основной клиент Ethereum: общая архитектура Geth
06\итог
Перед тем как углубиться в реализацию уровня исполнения Ethereum, необходимо иметь общее представление о самом Ethereum. Весь Ethereum можно рассматривать как машину состояния, управляемую транзакциями, где уровень исполнения отвечает за выполнение транзакций и изменение состояния, а уровень консенсуса отвечает за управление работой уровня исполнения, включая создание блоков, определение порядка транзакций, голосование за блоки и предоставление блокам окончательности. Поскольку эта машина состояния децентрализована, необходимо общаться с другими узлами через p2p сеть для совместного поддержания согласованности данных состояния.
Исполнительный уровень не отвечает за определение порядка транзакций, а только за выполнение транзакций и запись изменений состояния после их выполнения. Здесь запись имеет две формы: одна - это запись всех изменений состояния в виде блоков, другая - это запись текущего состояния в базе данных. В то же время исполнительный уровень также является входом для транзакций, храня транзакции, которые еще не упакованы в блок, в пуле транзакций. Если другие узлы нуждаются в получении данных о блоках, состоянии и транзакциях, исполнительный уровень отправит эту информацию через p2p сеть.
Для исполнительного уровня есть три основных модуля: вычисления, хранения и сети. Вычисления соответствуют реализации EVM, хранение соответствует реализации ethdb, а сеть соответствует реализации devp2p. Имея такое общее понимание, можно углубиться в изучение каждого подсмодуля, не теряясь в конкретных деталях.
07\Ссылка
[1]
[2]
[3]
[4]
[5]
[6]
· КОНЕЦ·
Содержание | Ray
Редактирование & Верстка | Хуанхуан
Дизайн | Daisy