이 문서는 Geth 소스 코드 시리즈의 첫 번째 문서입니다. 이 시리즈를 통해 Geth 구현을 연구하기 위한 프레임워크를 구축하겠습니다. 개발자는 이 프레임워크를 사용하여 관심 있는 부분을 깊이 파고들 수 있습니다. 이 시리즈는 6개의 기사로 구성되어 있습니다. 첫 번째 기사에서는 실행 계층 클라이언트 Geth의 디자인 아키텍처와 Geth 노드의 시작 프로세스를 살펴보겠습니다. Geth 코드는 매우 빠르게 업데이트되며, 나중에 보는 코드는 다를 수 있지만 전반적인 디자인은 일반적으로 일관되며, 새로운 코드도 같은 아이디어로 읽을 수 있습니다.
01\이더리움 클라이언트
이더리움이 Merge 업그레이드를 수행하기 전에는 트랜잭션 실행과 블록체인 합의를 담당하는 클라이언트가 하나뿐이었으며, 블록체인이 특정 순서로 새로운 블록을 생성하도록 보장했습니다. Merge 업그레이드 이후, 이더리움 클라이언트는 실행 계층과 합의 계층으로 나뉩니다. 실행 계층은 거래 실행, 상태 및 데이터 유지 관리를 담당하는 반면, 합의 계층은 합의 기능 구현을 담당합니다. 실행 계층과 합의 계층은 API를 통해 통신합니다. 실행 계층과 합의 계층은 각자의 사양을 갖습니다. 클라이언트는 다양한 언어를 사용하여 구현될 수 있지만, 해당 사양을 준수해야 합니다. Geth는 실행 계층 클라이언트의 구현입니다. 현재 주류 실행 계층과 합의 계층 클라이언트는 다음과 같이 구현됩니다.
실행 계층
- Geth: Ethereum Foundation에서 직접 자금을 지원받는 팀이 유지 관리하고 Go로 개발되었으며 가장 안정적이고 시간에 검증된 클라이언트로 인정받고 있습니다.
- Nethermind: Nethermind 팀에서 개발 및 유지 관리하고 C#로 개발했으며 초기 단계에서는 Ethereum Foundation과 Gitcoin 커뮤니티에서 자금을 지원받았습니다.
- Besu: 원래 ConsenSys의 PegaSys 팀에서 개발했으며 현재는 Hyperledger 커뮤니티 프로젝트로 Java로 개발되었습니다.
- Erigon: Erigon 팀이 개발 및 유지 관리하고 있으며, Ethereum Foundation과 BNB Chain의 지원을 받고 있습니다. 2017년 Geth에서 포크되었으며, 목표는 동기화 속도와 디스크 효율성을 개선하는 것입니다.
- Reth: Paradigm에서 개발했으며, 개발 언어로 Rust를 사용하고 모듈성과 고성능을 강조합니다. 이제 성숙되어 프로덕션 환경에서 사용할 수 있습니다.
합의 계층
- Prysm: Prysmatic Labs에서 관리하고 있으며, 이더리움의 가장 초기 컨센서스 계층 클라이언트 중 하나입니다. Go 언어로 개발되었으며, 사용성과 보안에 중점을 두고 있으며, 초창기에는 이더리움 재단의 자금 지원을 받았습니다.
- Lighthouse: Sigma Prime 팀에서 유지 관리하고 Rust로 개발되었으며 고성능 및 엔터프라이즈급 보안에 중점을 두고 고부하 시나리오에 적합합니다.
- Teku: ConsenSys의 PegaSys 팀에서 개발했으며 나중에 Java 언어를 사용하여 Hyperledger Besu 커뮤니티의 일부가 되었습니다.
- Nimbus: Status Network 팀에서 Nim 언어를 사용하여 개발 및 유지 관리하며, 리소스가 제한된 장치(예: 모바일 폰 및 IoT 장치)에 최적화되어 있으며 임베디드 시스템에서 가벼운 작업을 달성하는 것을 목표로 합니다.
02\임원 레벨 소개
이더리움 실행 계층은 트랜잭션 기반 상태 머신으로 볼 수 있습니다. 실행 계층의 가장 기본적인 기능은 EVM을 통해 트랜잭션을 실행하여 상태 데이터를 업데이트하는 것입니다. 거래 실행 외에도 블록 및 상태 데이터의 저장 및 검증, P2P 네트워크 실행, 거래 풀 유지 관리 등의 기능도 있습니다.
거래는 Ethereum 실행 계층 사양에 정의된 형식으로 사용자(또는 프로그램)에 의해 생성됩니다. 사용자는 거래에 서명해야 합니다. 거래가 합법적인 경우(Nonce가 연속적이고, 서명이 정확하고, 가스 요금이 충분하고, 비즈니스 로직이 정확한 경우) 거래는 결국 EVM에 의해 실행되어 이더리움 네트워크의 상태를 업데이트합니다. 여기서 상태란 외부 계정 주소, 계약 주소, 주소 잔액은 물론 코드와 데이터를 포함한 데이터 구조, 데이터 및 데이터베이스의 모음을 말합니다.
실행 계층은 거래를 실행하고 거래 실행 후 상태를 유지하는 역할을 하며, 합의 계층은 어떤 거래를 실행할지 선택하는 역할을 합니다. EVM은 이 상태 머신의 상태 전환 함수입니다. 함수의 입력은 여러 곳에서 올 수 있습니다. 이는 합의 계층에서 제공한 최신 블록 정보에서 나올 수도 있고, P2P 네트워크에서 다운로드한 블록에서 나올 수도 있습니다.
합의 계층과 실행 계층은 엔진 API를 통해 통신하는데, 이는 실행 계층과 합의 계층 간의 유일한 통신 방법입니다. 합의 계층이 블록을 생성할 권한을 얻으면 실행 계층에 엔진 API를 통해 새로운 블록을 생성하도록 요청합니다. 블록을 생성할 권한을 얻지 못하면 실행 계층이 검증하고 실행할 수 있도록 최신 블록을 동기화하여 전체 이더리움 네트워크와의 합의를 유지합니다.
실행 계층은 논리적으로 6개 부분으로 나눌 수 있습니다.
- EVM: 거래 실행을 담당합니다. 거래 실행은 상태 번호를 수정하는 유일한 방법이기도 합니다.
- 저장: 상태, 블록 및 기타 데이터의 저장을 담당합니다.
- 거래 풀: 사용자가 제출한 거래에 사용되며, 임시로 저장되고 P2P 네트워크를 통해 여러 노드 간에 전파됩니다.
- p2p 네트워크: 노드 검색, 거래 동기화, 블록 다운로드 등에 사용됨
- RPC 서비스: 사용자가 노드에 트랜잭션을 보내는 등 노드에 접근하고 합의 계층과 실행 계층 간의 상호 작용을 가능하게 하는 기능을 제공합니다.
- 블록체인: 이더리움의 블록체인 데이터를 관리하는 역할을 담당합니다.
다음 다이어그램은 실행 계층의 주요 프로세스와 각 부분의 기능을 보여줍니다.
실행 계층(여기서는 전체 노드만 논의)에는 세 가지 핵심 프로세스가 있습니다.
- 이더리움에 새롭게 가입하는 노드인 경우, P2P 네트워크를 통해 다른 노드의 블록과 상태 데이터를 동기화해야 합니다. Full Sync인 경우, 제네시스 블록부터 하나씩 블록을 다운로드하고, 블록을 검증한 후 EVM을 통해 상태 데이터베이스를 다시 구축합니다. Snap Sync인 경우 블록 검증 프로세스 전체를 건너뛰고 최신 체크포인트 상태 데이터와 후속 블록 데이터를 직접 다운로드합니다.
- 최신 상태로 동기화된 노드인 경우 Engine API를 통해 합의 계층에서 최신 출력 블록을 계속 가져와서 블록을 검증한 다음 EVM을 통해 블록의 모든 트랜잭션을 실행하여 상태 데이터베이스를 업데이트하고 블록을 로컬 체인에 씁니다.
- 노드가 최신 상태로 동기화되었고 합의 계층이 블록을 생성할 권한을 얻은 경우, Engine API를 통해 실행 계층이 최신 블록을 생성하도록 합니다. 실행 계층은 거래 풀에서 거래를 가져와 실행한 다음 이를 블록으로 조립하여 엔진 API를 통해 합의 계층으로 전달합니다. 합의 계층은 블록을 합의 계층 P2P 네트워크로 브로드캐스트합니다.
03\소스코드 구조
go-ethereum의 코드 구조는 방대하지만, 그 중 많은 부분이 보조 코드와 단위 테스트에 속합니다. Geth 소스 코드를 공부할 때는 프로토콜의 핵심 구현에만 집중하면 됩니다. 각 모듈의 기능은 다음과 같습니다. core, eth, ethdb, node, p2p, rlp, trie & triedb 모듈에 집중해야 합니다.
- 계정: 공개 및 개인 키 쌍 생성, 서명 검증, 주소 파생 등을 포함하여 이더리움 계정을 관리합니다.
- 비콘: 이더리움 비콘 체인과의 상호 작용 로직을 처리하고 지분 증명(PoS) 합의의 병합 후 기능을 지원합니다.
- 빌드: 빌드 스크립트 및 컴파일 구성(Dockerfile, 크로스 플랫폼 컴파일 지원 등)
- cmd: 여러 하위 명령을 포함한 명령줄 도구 항목
- 공통: 바이트 처리, 주소 형식 변환, 수학 함수와 같은 일반 도구 클래스
- 합의: 이전 작업 증명(Ethash), 단일 머신 지분 증명(Clique), 비콘 엔진 등을 포함한 합의 엔진을 정의합니다.
- 콘솔: 사용자가 명령줄을 통해 Ethereum 노드와 직접 상호 작용할 수 있는 대화형 JavaScript 콘솔을 제공합니다(예: Web3 API 호출, 계정 관리, 블록체인 데이터 쿼리)
- 핵심: 블록체인의 핵심 로직으로, 블록/거래, 상태 머신, 가스 계산 등의 수명 주기를 관리합니다.
- crypto: 타원 곡선(secp256k1), 해시(Keccak-256), 서명 검증을 포함한 암호화 알고리즘 구현
- docs: 문서(예: 디자인 사양, API 설명)
- eth: 노드 서비스, 블록 동기화(빠른 동기화, 아카이브 모드 등), 트랜잭션 브로드캐스팅 등을 포함한 이더리움 네트워크 프로토콜의 완전한 구현입니다.
- ethclient: Ethereum 클라이언트 라이브러리를 구현하고 Go 개발자가 Ethereum 노드와 상호 작용할 수 있도록 JSON-RPC 인터페이스를 캡슐화합니다(예: 블록 쿼리, 트랜잭션 전송, 계약 배포).
- ethdb: 데이터베이스 추상화 계층, LevelDB, Pebble, 메모리 데이터베이스 등을 지원하며 블록체인 데이터(블록, 상태, 트랜잭션)를 저장합니다.
- ethstats: 네트워크 상태를 모니터링하기 위해 노드 작업 상태를 수집하여 통계 서비스에 보고합니다.
- 이벤트: 노드 내 모듈 간 비동기 통신(예: 새 블록 도착, 트랜잭션 풀 업데이트)을 지원하여 이벤트 구독 및 게시 메커니즘을 구현합니다.
- graphql: GraphQL 인터페이스를 제공하고 복잡한 쿼리를 지원합니다(일부 JSON-RPC 함수 대체)
- 내부: 외부 접근이 제한된 내부 도구 또는 코드
- 로그: 로그 시스템, 계층적 로그 출력 및 상황별 로그 기록 지원
- metrics: 성능 지표 수집(Prometheus 지원)
- 마이너: 채굴 관련 로직, 새로운 블록 생성 및 거래 패키징(PoW 시나리오)
- 노드: 노드 서비스 관리, p2p, RPC, 데이터베이스 및 기타 모듈의 시작 및 구성 통합
- p2p: 노드 검색, 데이터 전송 및 암호화된 통신을 지원하는 피어 투 피어 네트워크 프로토콜 구현
- params: Ethereum 네트워크 매개변수(메인넷, 테스트넷, 제네시스 블록 구성)를 정의합니다.
- rlp: 블록 및 트랜잭션과 같은 데이터 구조를 인코딩/디코딩하는 데 사용되는 Ethereum의 전용 데이터 직렬화 프로토콜 RLP(Recursive Length Prefix)를 구현합니다.
- rpc: 외부 프로그램이 노드와 상호 작용할 수 있도록 JSON-RPC 및 IPC 인터페이스를 구현합니다.
- 서명자: 거래 서명 관리(하드웨어 지갑 통합)
- 테스트: 프로토콜 호환성을 확인하기 위한 통합 테스트 및 상태 테스트
- trie & triedb: 계정 상태 및 계약 저장의 효율적인 저장 및 관리를 위한 Merkle Patricia Trie 구현
04\실행 계층 모듈 분할
외부에서 Geth 노드에 접근하는 방법은 두 가지가 있습니다. 하나는 RPC를 통하는 것이고 다른 하나는 콘솔을 통하는 것입니다. RPC는 외부 사용자에게 적합하고, Console은 노드 관리자에게 적합합니다. 그러나 RPC나 콘솔을 통해 모두 계층화된 방식으로 구성된 내부 캡슐화된 기능을 사용합니다.
가장 바깥쪽 계층은 노드의 다양한 기능에 대한 외부 액세스를 위한 API입니다. 엔진 API는 실행 계층과 합의 계층 간의 통신에 사용됩니다. Eth API는 외부 사용자나 프로그램이 거래를 보내고 블록 정보를 얻는 데 사용됩니다. Net API는 p2p 네트워크의 상태 등을 얻는 데 사용됩니다. 예를 들어, 사용자가 API를 통해 거래를 보내면 해당 거래는 결국 거래 풀에 제출되고 거래 풀에서 관리됩니다. 또 다른 예로, 사용자가 데이터 블록을 얻어야 하는 경우, 해당 블록을 얻기 위해 데이터베이스의 기능을 호출해야 합니다.
API의 다음 계층은 거래 풀, 거래 패키징, 블록 출력, 블록 및 상태 동기화 등 핵심 기능을 구현하는 것입니다. 이러한 기능은 하위 수준 기능에 의존해야 합니다. 예를 들어, 거래 풀, 블록, 상태의 동기화는 P2P 네트워크의 기능에 의존해야 합니다. 다른 노드에서 생성된 블록과 동기화된 블록은 로컬 데이터베이스에 기록되기 전에 검증되어야 합니다. 이를 위해서는 EVM과 데이터 저장 기능이 필요합니다.
실행 계층 핵심 데이터 구조
이더리움
eth/backend.go의 Ethereum 구조는 기본적으로 Ethereum의 주요 구성 요소를 포함하는 전체 Ethereum 프로토콜의 추상화이지만 EVM은 예외입니다. 거래가 처리될 때마다 인스턴스화되므로 전체 노드로 초기화할 필요가 없습니다. 다음 텍스트에서 Ethereum은 이 구조를 참조합니다.
type Ethereum struct { // 체인 구성을 포함한 Ethereum 구성config *ethconfig.Config // 트랜잭션 풀, 제출 후 사용자의 트랜잭션은 트랜잭션 풀로 이동txPool *txpool.TxPool // 로컬 트랜잭션을 추적하고 관리하는 데 사용localTxTracker *locals.TxTracker // 블록체인 구조blockchain *core.BlockChain // 블록 동기화, 트랜잭션 브로드캐스팅 및 수신, 피어 노드 연결 관리를 포함하여 다른 노드와의 모든 통신을 처리하는 Ethereum 노드의 네트워크 계층의 핵심 구성 요소입니다.handler *handler // 노드 검색 및 노드 소스 관리를 담당합니다.discmix *enode.FairMix // 블록체인 데이터의 영구 저장을 담당합니다.chainDb ethdb.Database // 다양한 내부 이벤트의 게시 및 구독을 처리하는 것을 담당합니다.eventMux *event.TypeMux // 합의 엔진engine consensus.Engine // 사용자 계정 및 키 관리accountManager *accounts.Manager // 로그 필터 및 블록 필터 관리filterMaps *filtermaps.FilterMaps // 노드가 종료될 때 리소스가 제대로 정리되도록 filterMaps 채널을 안전하게 닫는 데 사용됨 closeFilterMaps chan chan struct{} // RPC API에 대한 백엔드 지원 제공 APIBackend *EthAPIBackend // PoS에서 합의 엔진과 협력하여 블록을 검증함 miner *miner.Miner // 노드에서 허용하는 최소 가스 가격 gasPrice *big.Int // 네트워크 ID networkID uint64 // 네트워크 관련 RPC 서비스를 제공하여 RPCnetRPCService를 통해 네트워크 상태를 쿼리할 수 있음 *ethapi.NetAPI // P2P 네트워크 연결을 관리하고, 노드 검색 및 연결 설정을 처리하고, 기본 네트워크 전송 기능을 제공함 p2pServer *p2p.Server // 변경 가능한 필드에 대한 동시 액세스를 보호함 lock sync.RWMutex // 노드가 정상적으로 종료되었는지 추적하고 비정상적인 shutdownTracker 이후 복구를 도움 *shutdowncheck.ShutdownTracker }
마디
node/node.go의 노드는 또 다른 핵심 데이터 구조입니다. 컨테이너로서 다양한 서비스의 운영을 관리하고 조정하는 역할을 담당합니다. 다음 구조에서는 라이프사이클 필드에 주의해야 합니다. Lifecycle은 내부 함수의 수명 주기를 관리하는 데 사용됩니다. 예를 들어, 위의 Ethereum 추상화는 라이프사이클에서 시작 및 등록하기 위해 Node에 의존해야 합니다. 이를 통해 노드 추상화에서 특정 기능을 분리하고 전체 아키텍처의 확장성을 개선할 수 있습니다. 이 노드는 devp2p의 노드와 구별되어야 합니다.
type Node struct { eventmux *event.TypeMux config *Config // 지갑과 계좌 관리를 담당하는 계정 관리자 accman *accounts.Manager log log.Logger keyDir string keyDirTemp bool dirLock *flock.Flock stop chan struct{} // p2p 네트워크 인스턴스 서버 *p2p.Server startStopLock sync.Mutex // 노드 수명 주기 상태(초기화, 실행 중, 닫힘) 추적 state int lock sync.Mutex // 등록된 모든 백엔드, 서비스 및 보조 서비스 수명 주기 []Lifecycle // 현재 제공되는 API 목록 rpcAPIs []rpc.API // RPC에 대해 제공되는 다양한 액세스 방법 http *httpServer ws *httpServer httpAuth *httpServer wsAuth *httpServer ipc *ipcServer inprocHandler *rpc.Server databases map[*closeTrackingDB]struct{} }
이더리움의 실행 계층을 추상적인 차원에서 살펴보면, 세계 컴퓨터인 이더리움은 네트워크, 컴퓨팅, 스토리지라는 세 가지 부분을 포함해야 합니다. 그러면 이더리움 실행 계층에서 이 세 부분에 해당하는 구성요소는 다음과 같습니다.
- 네트워크: devp2p
- 계산: EVM
- 저장소: ethdb
데브피투피
이더리움은 본질적으로 분산 시스템으로, 각 노드는 P2P 네트워크를 통해 다른 노드와 연결되어 있습니다. 이더리움의 P2P 네트워크 프로토콜 구현은 devp2p입니다.
devp2p에는 두 가지 핵심 기능이 있습니다. 하나는 노드 검색으로, 노드가 네트워크에 액세스할 때 다른 노드와 연결을 설정할 수 있도록 합니다. 다른 하나는 데이터 전송 서비스로, 노드가 다른 노드와 연결을 설정한 후 데이터를 교환할 수 있도록 해줍니다.
p2p/enode/node.go의 Node 구조는 p2p 네트워크의 노드를 나타내며, 여기서 enr.Record 구조는 노드 세부 정보의 키-값 쌍을 저장합니다. 여기에는 ID 정보(서명 알고리즘 및 노드 ID에서 사용하는 공개 키), 네트워크 정보(IP 주소, 포트 번호), 지원되는 프로토콜 정보(예: eth/68 및 snap 프로토콜 지원) 및 기타 사용자 정의 정보가 포함됩니다. 이 정보는 RLP로 인코딩되었습니다. 구체적인 사양은 eip-778에 정의되어 있습니다.
type Node struct { // 노드의 다양한 속성을 포함하는 노드 레코드 enr.Record // 노드의 고유 식별자, 길이는 32바이트 id ID // 호스트 이름 노드의 DNS 이름 추적 호스트 이름 문자열 // 노드의 IP 주소 ip netip.Addr // UDP 포트 udp uint16 // TCP 포트 tcp uint16 }// enr.Recordtype Record struct { // 일련 번호 seq uint64 // 서명 signature []byte // RLP 인코딩된 레코드 raw []byte // 모든 키-값 쌍의 정렬된 목록 pairs []pair }
p2p/discover/table.go의 테이블 구조는 devp2p 노드 검색 프로토콜의 핵심 데이터 구조입니다. Kademlia와 유사한 분산 해시 테이블을 구현하며 네트워크의 노드 정보를 유지 관리하고 관리하는 데 사용됩니다.
printf("type Table struct { mutex sync.Mutex // 거리 버킷으로 알려진 노드 인덱스 [nBuckets]*bucket // 부트 노드 보육원 []*enode.Node rand reseedingRandom ips netutil.DistinctNetSet 재검증 tableRevalidation // 알려진 노드의 데이터베이스 db *enode.DB net transport cfg Config log log.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{} // 노드를 추가하고 제거하기 위한 인터페이스 nodeAddedHook func(*bucket, *tableNode) nodeRemovedHook func(*bucket, *tableNode)} world!");
ethdb
ethdb는 이더리움 데이터 저장소의 추상화를 완성하고 통합된 저장소 인터페이스를 제공합니다. 기본 데이터베이스는 leveldb, pebble 또는 다른 데이터베이스가 될 수 있습니다. 인터페이스 수준에서 통일성을 유지하는 한 많은 확장이 가능할 수 있습니다.
일부 데이터(예: 블록 데이터)는 ethdb 인터페이스를 통해 기본 데이터베이스에 직접 읽고 쓸 수 있습니다. 다른 데이터 저장 인터페이스는 ethdb를 기반으로 구축되었습니다. 예를 들어, 데이터베이스에 있는 데이터의 상당 부분은 상태 데이터입니다. 이 데이터는 MPT 구조로 구성됩니다. Geth에서 해당 구현은 trie입니다. 노드가 작동하는 동안 트라이 데이터는 많은 중간 상태를 생성합니다. 이러한 데이터는 ethdb를 읽고 쓰는 데 직접 호출될 수 없습니다. 이러한 데이터와 중간 상태를 관리하고, 최종적으로 ethdb를 통해 이를 유지하려면 Triedb가 필요합니다.
기본 데이터베이스의 읽기 및 쓰기 기능에 대한 인터페이스는 ethdb/database.go에 정의되어 있지만, 구체적인 구현은 포함되어 있지 않습니다. 구체적인 구현은 각기 다른 데이터베이스에서 직접 구현됩니다. 예를 들어, leveldb나 pebble 데이터베이스입니다. 데이터베이스에는 두 계층의 데이터 읽기 및 쓰기 인터페이스가 정의되어 있으며, KeyValueStore 인터페이스는 최신 블록, 상태 등 자주 변경될 수 있는 활성 데이터를 저장하는 데 사용됩니다. AncientStore는 작성된 후 거의 변경되지 않는 이전 블록 데이터를 처리하는 데 사용됩니다.
//데이터베이스 유형의 최상위 인터페이스 데이터베이스 인터페이스 { KeyValueStore AncientStore} // KV 데이터 유형에 대한 읽기 및 쓰기 인터페이스 KeyValueStore 인터페이스 { KeyValueReader KeyValueWriter KeyValueStater KeyValueRangeDeleter Batcher Iteratee Compacter io.Closer} // 이전 데이터 유형을 읽고 쓰기 위한 인터페이스 AncientStore 인터페이스 { AncientReader AncientWriter AncientStater io.Closer}
EVM
EVM은 이더리움 상태 머신의 상태 전환 함수입니다. 모든 상태 데이터 업데이트는 EVM을 통해서만 수행할 수 있습니다. p2p 네트워크는 거래 및 블록 정보를 수신할 수 있으며, 이 정보는 EVM에서 처리된 후 상태 데이터베이스의 일부가 됩니다. EVM은 기본 하드웨어의 차이점을 마스킹하여 다양한 플랫폼의 EVM에서 프로그램을 실행해도 일관된 결과를 생성할 수 있도록 합니다. 이는 매우 성숙한 설계 접근 방식이며, Java 언어의 JVM도 비슷한 설계를 가지고 있습니다.
EVM 구현에는 세 가지 주요 구성 요소가 있습니다. core/vm/evm.go의 EVM 구조는 실행 컨텍스트, 상태 데이터베이스 종속성 등을 포함하여 EVM의 전반적인 구조와 종속성을 정의합니다. core/vm/interpreter.go의 EVMInterpreter 구조는 EVM 바이트코드를 실행하는 인터프리터의 구현을 정의합니다. core/vm/contract.go의 계약 구조는 호출자, 계약 코드, 입력 등을 포함한 계약 호출의 특정 매개변수를 캡슐화하고, 모든 현재 명령어는 core/vm/opcodes.go에 정의되어 있습니다.
// EVMtype EVM struct { // 블록 관련 정보를 포함한 블록 컨텍스트Context BlockContext // 트랜잭션 관련 정보를 포함한 트랜잭션 컨텍스트TxContext // 계정 상태에 액세스하고 수정하는 데 사용되는 상태 데이터베이스StateDB StateDB // 현재 호출 깊이 depth int // 체인 구성 매개변수chainConfig *params.ChainConfig chainRules params.Rules // EVM configurationConfig Config // 바이트코드 인터프리터인터프리터 *EVMInterpreter // 중단 플래그abort atomic.Bool callGasTemp uint64 // 사전 컴파일된 계약 매핑precompiles map[common.Address]PrecompiledContract jumpDests map[common.Hash]bitvec }type EVMInterpreter struct { // EVM 인스턴스를 가리킴 evm *EVM // Opcode 점프 테이블 *JumpTable // Keccak256 해셔 인스턴스, opcodeshasher와 공유 crypto.KeccakState // Keccak256 해시 결과 버퍼hasherBuf common.Hash // Is it 읽기 전용 모드? 읽기 전용 모드에서는 상태 수정이 허용되지 않습니다.readOnly bool // 이후 재사용을 위해 마지막 CALL의 데이터를 반환합니다.returnData []byte }type Contract struct { // 호출자 주소caller common.Address // 계약 주소주소 common.Address jumpdests map[common.Hash]bitvec 분석 bitvec // 계약 bytecodeCode []byte // 코드 hashCodeHash common.Hash // 호출 inputInput []byte // 계약인가요?deploymentIsDeployment bool // 시스템 호출인가요?IsSystemCall bool // 사용 가능한 gasGas uint64 // 호출에 첨부된 ETH 양value *uint256.Int }
다른 모듈 구현
실행 계층의 기능은 계층적으로 구현되며, 다른 모듈과 기능은 이 세 가지 핵심 구성 요소를 기반으로 구축됩니다. 핵심 모듈은 다음과 같습니다.
eth/protocols에는 현재 Ethereum p2p 네트워크 하위 프로토콜의 구현이 있습니다. Devp2p를 기반으로 구축된 eth/68과 snap 하위 프로토콜이 있습니다.
eth/68은 이더리움의 핵심 프로토콜입니다. 프로토콜 이름은 eth이고, 버전 번호는 68입니다. 그리고 이 프로토콜을 기반으로 거래 풀(TxPool), 블록 동기화(Downloader), 거래 동기화(Fetcher) 등의 기능이 구현됩니다. 스냅 프로토콜은 새로운 노드가 네트워크에 가입할 때 블록과 상태 데이터를 빠르게 동기화하는 데 사용되며, 이를 통해 새로운 노드를 시작하는 데 걸리는 시간을 크게 줄일 수 있습니다.
ethdb는 기본 데이터베이스의 읽기 및 쓰기 기능을 제공합니다. 이더리움 프로토콜에는 복잡한 데이터 구조가 많기 때문에 ethdb를 통해 이러한 데이터를 직접 관리하는 것은 불가능합니다. 따라서 ethdb에는 rawdb와 statedb가 구현되어 각각 블록과 상태 데이터를 관리합니다.
EVM은 모든 주요 프로세스를 실행합니다. 블록 생성이든 블록 검증이든 거래를 실행하려면 EVM이 필요합니다.
05\Geth 노드 시작 프로세스
Geth의 시작은 두 단계로 나뉩니다. 첫 번째 단계에서는 노드를 시작하는 데 필요한 구성 요소와 리소스를 초기화합니다. 두 번째 단계에서는 노드를 공식적으로 시작한 후 외부 서비스를 제공합니다.
노드 초기화
Geth 노드를 시작할 때 다음 코드가 필요합니다.
각 모듈의 초기화는 다음과 같습니다.
- cmd/geth/main.go: geth 노드 시작 항목
- cmd/geth/config.go(makeFullNode): 구성을 로드하고 노드를 초기화합니다.
- node/node.go: Ethereum 노드를 초기화하기 위한 핵심 컨테이너
- node.rpcstack.go: RPC 모듈 초기화
- account.manager.go: accountManager 초기화
- eth/backend.go: Ethereum 인스턴스 초기화
- node/node.go OpenDatabaseWithFreezer: chaindb 초기화
- 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: 로컬 거래 추적(로컬 거래 추적을 활성화하도록 구성해야 함, 로컬 거래는 더 높은 우선순위로 처리됨)
- eth/handler.go: 프로토콜의 Handler 인스턴스를 초기화합니다.
- miner/miner.go: 트랜잭션 패키징을 인스턴스화하기 위한 모듈(원래 마이닝 모듈)
- eth/api_backend.go: RPC 서비스 인스턴스화
- eth/gasprice/gasprice.go: 가스 가격 쿼리 서비스 인스턴스화
- internal/ethapi/api.go: P2P 네트워크 RPC API를 인스턴스화합니다.
- node/node.go(RegisterAPIs): RPC API 등록
- node/node.go(RegisterProtocols): P2P 프로토콜 등록
- node/node.go(RegisterLifecycle): 각 컴포넌트의 라이프사이클을 등록합니다.
- cmd/utils/flags.go(RegisterFilterAPI): 필터 RPC API 등록
- cmd/utils/flags.go(RegisterGraphQLService): GraphQL RPC API 등록(구성된 경우)
- cmd/utils/flags.go(RegisterEthStatsService): EthStats RPC API 등록(구성된 경우)
- eth/catalyst/api.go: 엔진 API 등록
노드 초기화는 cmd/geth/config.go의 makeFullNode에서 완료되며, 다음 세 가지 모듈 초기화에 중점을 둡니다.
첫 번째 단계에서는 node/node.go에 있는 Node 구조가 초기화됩니다. 이는 전체 노드 컨테이너입니다. 모든 기능은 이 컨테이너에서 실행되어야 합니다. 두 번째 단계에서는 이더리움의 다양한 핵심 기능 구현을 포함하는 이더리움 구조를 초기화합니다. 이더리움도 노드에 등록되어야 합니다. 세 번째 단계는 Node.js에 Engine API를 등록하는 것입니다.
노드 초기화는 노드 인스턴스를 생성한 후 외부에 노출되는 P2P 서버, 계정 관리, http 프로토콜 포트를 초기화합니다.
이더리움의 초기화는 훨씬 더 복잡하며, 대부분의 핵심 기능이 여기서 초기화됩니다. 먼저, ethdb가 초기화되고 체인 구성이 저장소에서 로드됩니다. 그러면 합의 엔진이 생성됩니다. 여기의 합의 엔진은 합의 작업을 수행하지 않고, 합의 계층에서 반환된 결과만 검증합니다. 합의 계층에서 출금 요청이 발생하면 실제 출금 작업도 여기서 완료됩니다. 그런 다음 블록체인 구조와 거래 풀을 초기화합니다.
모든 작업이 완료되면 핸들러가 초기화됩니다. 핸들러는 트랜잭션 동기화, 블록 다운로드 등 모든 P2P 네트워크 요청에 대한 처리 항목입니다. 이는 이더리움이 분산된 운영을 달성하기 위한 핵심 구성 요소입니다. 이 모든 과정이 완료되면 eth/68, snap 등 devp2p 기반으로 구현된 일부 하위 프로토콜이 Node 컨테이너에 등록됩니다. 마지막으로, 이더리움은 노드 컨테이너에 라이프사이클로 등록되고, 이더리움 초기화가 완료됩니다.
마지막으로, Engine API의 초기화는 비교적 간단합니다. Node.js에 Engine API를 등록하기만 하면 됩니다. 이 시점에서 노드 초기화가 완료되었습니다.
노드 시작
노드 초기화를 완료한 후에는 노드를 시작해야 합니다. 노드를 시작하는 과정은 비교적 간단합니다. 등록된 RPC 서비스와 라이프사이클을 모두 시작하기만 하면 전체 노드가 외부 세계에 서비스를 제공할 수 있습니다.
06\요약
이더리움의 실행 계층 구현을 깊이 이해하기 전에, 이더리움에 대한 전체적인 이해가 필요합니다. 이더리움은 전체적으로 거래 중심의 상태 머신으로 볼 수 있습니다. 실행 계층은 트랜잭션과 상태 변경의 실행을 담당하고, 합의 계층은 실행 계층이 블록을 생성하고, 트랜잭션 순서를 결정하고, 블록에 투표하고, 블록을 최종적으로 만드는 것을 포함하여 실행 계층이 실행되도록 하는 역할을 합니다. 이 상태 머신은 분산되어 있으므로 P2P 네트워크를 통해 다른 노드와 통신하여 상태 데이터의 일관성을 공동으로 유지해야 합니다.
실행 계층은 거래 순서를 결정하는 역할을 하지 않고, 거래 실행과 거래 실행 후 상태 변경 기록만을 담당합니다. 여기에는 두 가지 형태의 기록이 있습니다. 하나는 모든 상태 변화를 블록 형태로 기록하는 것이고, 다른 하나는 현재 상태를 데이터베이스에 기록하는 것입니다. 동시에 실행 계층은 거래의 진입점이기도 하며, 거래 풀은 아직 블록으로 패키징되지 않은 거래를 저장하는 데 사용됩니다. 다른 노드가 블록, 상태 및 거래 데이터를 얻어야 하는 경우 실행 계층은 이 정보를 P2P 네트워크를 통해 전송합니다.
실행 계층에는 컴퓨팅, 스토리지, 네트워크라는 세 가지 핵심 모듈이 있습니다. 계산은 EVM 구현에 대응하고, 저장소는 ethdb 구현에 대응하며, 네트워크는 devp2p 구현에 대응합니다. 이러한 전반적인 이해를 바탕으로, 특정 세부 사항에 얽매이지 않고 각 하위 모듈을 더 깊이 이해할 수 있습니다.
07\참고
[1]https://ethereum.org/zh/what-is-ethereum/
[2]https://epf.wiki/#/wiki/프로토콜/아키텍처
[3]https://clientdiversity.org/#분배
[4]https://github.com/ethereum/devp2p
[5]https://github.com/ethereum/실행-사양
[6]https://github.com/ethereum/consensus-specs
·끝·
목차 | 레이
편집 및 서식 | 환환
디자인 | 데이지