Geth 소스 코드 시리즈: Geth 전체 아키텍처
저자: Ray, LXDAO
본 문서는 실행 계층 클라이언트 Geth의 설계 아키텍처와 Geth 노드의 시작 프로세스를 연구합니다. Geth 코드 업데이트 속도가 매우 빠르므로 이후에 보게 될 코드는 다를 수 있지만, 전체적인 설계는 대체로 일관되며 새로운 코드는 동일한 사고방식으로 읽을 수 있습니다.
소스 코드 버전: https://github.com/ethereum/go-ethereum/commit/c8a9a9c0917dd57d077a79044e65dbbdd421458b
1. 이더리움 클라이언트
이더리움은 The Merge 업그레이드 이전에 단 하나의 클라이언트만 존재했으며, 이 클라이언트는 거래의 실행을 담당하고 블록체인의 합의도 책임졌습니다. 블록체인이 일정한 순서로 새로운 블록을 생성하도록 보장합니다. The Merge 업그레이드 이후, 이더리움 클라이언트는 실행 계층과 합의 계층으로 나뉘었습니다. 실행 계층은 거래의 실행, 상태 및 데이터의 유지 관리를 담당하고, 합의 계층은 합의 기능의 구현을 책임집니다. 실행 계층과 합의 계층은 API를 통해 통신합니다. 실행 계층과 합의 계층은 각자의 규범이 있으며, 클라이언트는 서로 다른 언어로 구현할 수 있지만 해당 규범을 준수해야 합니다. Geth는 실행 계층 클라이언트의 한 구현입니다. 현재 주류의 실행 계층 및 합의 계층 클라이언트는 다음과 같은 구현이 있습니다:
실행 계층
- Geth: 이더리움 재단이 직접 지원하는 팀이 유지 관리하며, Go 언어로 개발되어 공인된 가장 안정적이고 검증된 클라이언트입니다.
- Nethermind: Nethermind 팀이 개발 및 유지 관리하며, C# 언어로 개발되었습니다. 초기에는 이더리움 재단과 Gitcoin 커뮤니티의 지원을 받았습니다.
- Besu: 처음에 ConsenSys의 PegaSys 팀이 개발하였고, 현재는 Hyperledger 커뮤니티 프로젝트로, Java 언어로 개발되었습니다.
- Erigon: Erigon 팀이 개발 및 유지 관리하며, 이더리움 재단과 BNB 체인의 지원을 받습니다. 2017년에 Geth에서 포크되어 동기화 속도와 디스크 효율성을 향상시키는 것을 목표로 합니다.
- Reth: Paradigm이 주도하여 개발하며, 개발 언어는 Rust로, 모듈화와 높은 성능을 강조합니다. 현재 성숙기에 접어들어 생산 환경에서 사용할 수 있습니다.
합의 계층
- Prysm: Prysmatic Labs가 유지 관리하며, 이더리움 최초의 합의 계층 클라이언트 중 하나로 Go 언어로 개발되었습니다. 사용성과 보안에 중점을 두며, 초기에는 이더리움 재단의 지원을 받았습니다.
- Lighthouse: Sigma Prime 팀이 유지 관리하며, Rust 언어로 개발되었습니다. 고성능과 기업급 보안을 강조하며, 고부하 상황에 적합합니다.
- Teku: 초기에는 ConsenSys의 PegaSys 팀이 개발하였고, 이후 Hyperledger Besu 커뮤니티의 일부가 되었습니다. Java 언어로 개발되었습니다.
- Nimbus: Status Network 팀이 개발 및 유지 관리하며, Nim 언어로 개발되었습니다. 자원이 제한된 장치(예: 휴대폰, IoT 장치)를 위해 최적화되어 있으며, 임베디드 시스템에서 경량화된 실행을 목표로 합니다.
2. 실행 계층 개요
이더리움 실행 계층은 거래에 의해 구동되는 상태 기계로 볼 수 있으며, 실행 계층의 가장 기본적인 기능은 EVM을 통해 거래를 실행하여 상태 데이터를 업데이트하는 것입니다. 거래 실행 외에도 블록 및 상태 데이터를 저장하고 검증하며, p2p 네트워크를 운영하고 거래 풀을 유지하는 등의 기능이 있습니다.
거래는 사용자(또는 프로그램)가 이더리움 실행 계층 규범에 정의된 형식으로 생성하며, 사용자는 거래에 서명해야 합니다. 거래가 합법적이라면(Nonce가 연속적이고, 서명이 올바르며, 가스 요금이 충분하고, 비즈니스 로직이 올바르다면), 거래는 결국 EVM에 의해 실행되어 이더리움 네트워크의 상태를 업데이트합니다. 여기서 상태는 데이터 구조, 데이터 및 데이터베이스의 집합을 의미하며, 외부 계좌 주소, 계약 주소, 주소 잔액 및 코드와 데이터를 포함합니다.
실행 계층은 거래를 실행하고 거래 실행 후의 상태를 유지하는 책임이 있으며, 합의 계층은 어떤 거래를 실행할지를 선택하는 책임이 있습니다. EVM은 이 상태 기계의 상태 전환 함수로, 함수의 입력은 여러 곳에서 올 수 있으며, 합의 계층에서 제공하는 최신 블록 정보에서 올 수도 있고, p2p 네트워크에서 다운로드한 블록에서 올 수도 있습니다.
합의 계층과 실행 계층은 Engine API를 통해 통신하며, 이는 실행 계층과 합의 계층 간의 유일한 통신 방법입니다. 합의 계층이 블록 생성 권한을 얻으면 Engine API를 통해 실행 계층에 새로운 블록을 생성하도록 요청하며, 블록 생성 권한을 얻지 못하면 최신 블록을 동기화하여 실행 계층이 검증하고 실행하도록 하여 전체 이더리움 네트워크와 합의를 유지합니다.
실행 계층은 논리적으로 6개의 부분으로 나눌 수 있습니다:
- EVM: 거래를 실행하며, 거래 실행은 상태 수를 수정하는 유일한 방법입니다.
- 저장소: 상태 및 블록 등의 데이터 저장을 담당합니다.
- 거래 풀: 사용자가 제출한 거래를 임시로 저장하며, p2p 네트워크를 통해 다른 노드 간에 전파됩니다.
- p2p 네트워크: 노드 발견, 거래 동기화, 블록 다운로드 등의 기능을 수행합니다.
- RPC 서비스: 노드에 대한 접근 능력을 제공하며, 사용자가 노드에 거래를 전송하고, 합의 계층과 실행 계층 간의 상호작용을 포함합니다.
- BlockChain: 이더리움의 블록체인 데이터를 관리합니다.
아래 그림은 실행 계층의 주요 프로세스와 각 부분의 기능을 보여줍니다:

실행 계층(여기서는 Full Node만 논의합니다)에는 세 가지 핵심 프로세스가 있습니다:
- 새로 이더리움에 가입한 노드는 p2p 네트워크를 통해 다른 노드로부터 블록 및 상태 데이터를 동기화해야 합니다. Full Sync의 경우, 창세 블록부터 시작하여 블록을 하나씩 다운로드하고, 블록을 검증하며 EVM을 통해 상태 데이터베이스를 재구성합니다. Snap Sync의 경우, 모든 블록 검증 과정을 건너뛰고 최신 체크포인트의 상태 데이터와 이후 블록 데이터를 직접 다운로드합니다.
- 이미 최신 상태로 동기화된 노드는 Engine API를 통해 합의 계층에서 현재 최신으로 생성된 블록을 지속적으로 가져오고, 블록을 검증한 후 EVM을 통해 블록 내 모든 거래를 실행하여 상태 데이터베이스를 업데이트하고 블록을 로컬 체인에 기록합니다.
- 이미 최신 상태로 동기화되어 있고 합의 계층이 블록 생성 권한을 얻은 노드는 Engine API를 통해 실행 계층에 최신 블록을 생성하도록 요청하며, 실행 계층은 거래 풀에서 거래를 가져와 실행한 후 블록으로 조립하여 Engine API를 통해 합의 계층에 전달하며, 합의 계층은 블록을 합의 계층 p2p 네트워크에 브로드캐스트합니다.
3. 소스 코드 구조
go-ethereum의 코드 구조는 매우 방대하지만, 그 중 많은 코드는 보조 코드와 단위 테스트에 해당합니다. Geth 소스 코드를 연구할 때는 프로토콜의 핵심 구현에만 집중하면 됩니다. 각 모듈의 기능은 다음과 같습니다. core, eth, ethdb, node, p2p, rlp, trie & triedb 등의 모듈에 특히 주목해야 합니다:
- accounts: 이더리움 계정을 관리하며, 공개 및 개인 키 쌍 생성, 서명 검증, 주소 파생 등을 포함합니다.
- beacon: 이더리움 신호 체인(Beacon Chain)과의 상호 작용 로직을 처리하며, 지분 증명(PoS) 합의의 병합(The Merge) 후 기능을 지원합니다.
- build: 빌드 스크립트 및 컴파일 구성(예: Dockerfile, 크로스 플랫폼 컴파일 지원)을 포함합니다.
- cmd: 명령줄 도구의 진입점으로 여러 하위 명령을 포함합니다.
- common: 바이트 처리, 주소 형식 변환, 수학 함수와 같은 일반 유틸리티 클래스입니다.
- consensus: 합의 엔진을 정의하며, 이전의 작업 증명(Ethash) 및 단일 노드 지분 증명(Clique)과 Beacon 엔진 등을 포함합니다.
- console: 대화형 JavaScript 콘솔을 제공하여 사용자가 명령줄을 통해 이더리움 노드와 직접 상호작용할 수 있도록 합니다(예: Web3 API 호출, 계정 관리, 블록체인 데이터 조회).
- core: 블록체인의 핵심 로직을 처리하며, 블록/거래의 생애 주기 관리, 상태 기계, 가스 계산 등을 포함합니다.
- crypto: 암호화 알고리즘 구현을 포함하며, 타원 곡선(secp256k1), 해시(Keccak-256), 서명 검증 등을 포함합니다.
- docs: 문서(예: 설계 규범, API 설명)를 포함합니다.
- eth: 이더리움 네트워크 프로토콜의 완전한 구현으로, 노드 서비스, 블록 동기화(예: 빠른 동기화, 아카이브 모드), 거래 브로드캐스트 등을 포함합니다.
- ethclient: 이더리움 클라이언트 라이브러리를 구현하며, JSON-RPC 인터페이스를 캡슐화하여 Go 개발자가 이더리움 노드와 상호작용할 수 있도록 합니다(예: 블록 조회, 거래 전송, 계약 배포).
- ethdb: 데이터베이스 추상화 계층으로, LevelDB, Pebble, 메모리 데이터베이스 등을 지원하며 블록체인 데이터를 저장합니다(블록, 상태, 거래).
- ethstats: 노드의 실행 상태를 수집하고 통계 서비스에 보고하여 네트워크 건강 상태를 모니터링합니다.
- event: 이벤트 구독 및 게시 메커니즘을 구현하여 노드 내부 모듈 간의 비동기 통신을 지원합니다(예: 새 블록 도착, 거래 풀 업데이트).
- graphql: GraphQL 인터페이스를 제공하여 복잡한 쿼리를 지원합니다(일부 JSON-RPC 기능을 대체).
- internal: 내부 도구 또는 외부 접근을 제한하는 코드입니다.
- log: 로그 시스템으로, 로그 출력의 수준을 지원하며, 컨텍스트 로그 기록을 포함합니다.
- metrics: 성능 지표 수집(Prometheus 지원).
- miner: 채굴 관련 로직으로, 새로운 블록을 생성하고 거래를 패키징합니다(PoW 시나리오에서).
- node: 노드 서비스 관리로, p2p, RPC, 데이터베이스 등의 모듈의 시작 및 구성을 통합합니다.
- p2p: 점대점 네트워크 프로토콜 구현으로, 노드 발견, 데이터 전송, 암호화 통신을 지원합니다.
- params: 이더리움 네트워크 매개변수(메인넷, 테스트넷, 창세 블록 구성)를 정의합니다.
- rlp: 이더리움 전용 데이터 직렬화 프로토콜 RLP(Recursive Length Prefix)를 구현하여 블록, 거래 등의 데이터 구조를 인코딩/디코딩합니다.
- rpc: JSON-RPC 및 IPC 인터페이스를 구현하여 외부 프로그램이 노드와 상호작용할 수 있도록 합니다.
- signer: 거래 서명 관리(하드웨어 지갑 통합).
- tests: 통합 테스트 및 상태 테스트로, 프로토콜 호환성을 검증합니다.
- trie & triedb: 머클 패트리샤 트리(Merkle Patricia Trie)를 구현하여 계정 상태 및 계약 저장소를 효율적으로 저장하고 관리합니다.
4. 실행 계층 모듈 분할
외부에서 Geth 노드에 접근하는 방법은 두 가지가 있습니다. 하나는 RPC를 통해, 다른 하나는 Console을 통해입니다. RPC는 외부 사용자에게 개방하는 데 적합하며, Console은 노드 관리자가 사용하는 데 적합합니다. 그러나 RPC든 Console이든 내부에서 이미 캡슐화된 기능을 사용하며, 이러한 기능은 계층화된 방식으로 구축됩니다.
가장 바깥쪽은 API로 외부에서 노드의 다양한 기능에 접근하는 데 사용되며, Engine API는 실행 계층과 합의 계층 간의 통신을 위해 사용됩니다. Eth API는 외부 사용자 또는 프로그램이 거래를 전송하고 블록 정보를 가져오는 데 사용되며, Net API는 p2p 네트워크의 상태를 가져오는 데 사용됩니다. 예를 들어 사용자가 API를 통해 거래를 전송하면, 이 거래는 결국 거래 풀에 제출되어 관리됩니다. 또 다른 예로 사용자가 블록 데이터를 가져오려면 데이터베이스의 기능을 호출하여 해당 블록을 가져와야 합니다.
API의 다음 계층은 핵심 기능의 구현으로, 거래 풀, 거래 패키징, 블록 생성, 블록 및 상태의 동기화 등을 포함합니다. 이러한 기능은 더 낮은 수준의 기능에 의존해야 하며, 예를 들어 거래 풀, 블록 및 상태의 동기화는 p2p 네트워크의 기능에 의존합니다. 블록 생성 및 다른 노드에서 동기화된 블록은 검증을 거쳐야 로컬 데이터베이스에 기록될 수 있으며, 이는 EVM 및 데이터 저장 기능에 의존해야 합니다.

실행 계층 핵심 데이터 구조
Ethereum
eth/backend.go의 Ethereum 구조체는 이더리움 프로토콜의 추상화로, 이더리움의 주요 구성 요소를 포함합니다. 그러나 EVM은 예외로, 거래를 처리할 때마다 인스턴스화되며 전체 노드 초기화와 함께 필요하지 않습니다. 아래의 Ethereum은 이 구조체를 의미합니다:
type Ethereum struct {
// 이더리움 구성, 체인 구성 포함
config *ethconfig.Config
// 거래 풀, 사용자의 거래가 제출된 후 먼저 거래 풀에 도착
txPool *txpool.TxPool
// 로컬 거래를 추적하고 관리하는 데 사용
localTxTracker *locals.TxTracker
// 블록체인 구조
blockchain *core.BlockChain
// 이더리움 노드의 네트워크 계층 핵심 구성 요소로, 다른 노드와의 모든 통신을 처리하며, 블록 동기화, 거래 브로드캐스트 및 수신, 피어 노드 연결 관리 등을 포함
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 서비스를 제공하여 RPC를 통해 네트워크 상태를 조회할 수 있도록 함
netRPCService *ethapi.NetAPI
// P2P 네트워크 연결 관리, 노드 발견 및 연결 수립을 처리하며 기본 네트워크 전송 기능 제공
p2pServer *p2p.Server
// 가변 필드의 동시 접근 보호
lock sync.RWMutex
// 노드가 정상적으로 종료되었는지 추적하며, 비정상 종료 후 복구를 돕기 위해 사용
shutdownTracker *shutdowncheck.ShutdownTracker
}
Node
node/node.go의 Node는 또 다른 핵심 데이터 구조로, 다양한 서비스의 실행을 관리하고 조정하는 컨테이너 역할을 합니다. 아래 구조에서 lifecycles 필드에 주목해야 하며, Lifecycle은 내부 기능의 생애 주기를 관리하는 데 사용됩니다. 예를 들어 위의 Ethereum 추상화는 Node에 의존하여 시작해야 하며, lifecycles에 등록되어야 합니다. 이렇게 하면 구체적인 기능과 노드의 추상화를 분리하여 전체 아키텍처의 확장성을 높일 수 있습니다. 이 Node는 devp2p의 Node와 구별되어야 합니다.
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 네트워크 인스턴스
server *p2p.Server
startStopLock sync.Mutex
// 노드 생애 주기 상태 추적(초기화, 실행 중, 종료됨)
state int
lock sync.Mutex
// 모든 등록된 백엔드, 서비스 및 보조 서비스
lifecycles []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
devp2p
이더리움은 본질적으로 분산 시스템이며, 각 노드는 p2p 네트워크를 통해 다른 노드와 연결됩니다. 이더리움의 p2p 네트워크 프로토콜 구현이 바로 devp2p입니다.
devp2p는 두 가지 핵심 기능을 가지고 있습니다. 하나는 노드 발견으로, 노드가 네트워크에 접속할 때 다른 노드와 연결할 수 있도록 합니다. 또 다른 하나는 데이터 전송 서비스로, 다른 노드와 연결된 후 데이터를 교환할 수 있습니다.
p2p/enode/node.go의 Node 구조체는 p2p 네트워크에서 하나의 노드를 나타내며, enr.Record 구조체는 노드의 상세 정보 키-값 쌍을 저장합니다. 여기에는 신원 정보(노드 신원에 사용되는 서명 알고리즘, 공개 키), 네트워크 정보(IP 주소, 포트 번호), 지원하는 프로토콜 정보(예: eth/68 및 snap 프로토콜) 및 기타 사용자 정의 정보가 포함됩니다. 이 정보는 RLP 방식으로 인코딩되며, 구체적인 규범은 eip-778에 정의되어 있습니다:
type Node struct {
// 노드 기록, 노드의 다양한 속성을 포함
r enr.Record
// 노드의 고유 식별자, 32바이트 길이
id ID// hostname은 노드의 DNS 이름을 추적
hostname string// 노드의 IP 주소
ip netip.Addr
// UDP 포트
udp uint16
// TCP 포트
tcp uint16
}// enr.Record
type Record struct {
// 시퀀스 번호
seq uint64
// 서명
signature []byte
// RLP 인코딩된 기록
raw []byte
// 모든 키-값 쌍의 정렬된 목록
pairs []pair
}
p2p/discover/table.go의 Table 구조체는 devp2p에서 노드 발견 프로토콜을 구현하는 핵심 데이터 구조로, Kademlia와 유사한 분산 해시 테이블을 구현하여 네트워크의 노드 정보를 유지하고 관리합니다.
type Table struct {
mutex sync.Mutex
// 거리별로 알려진 노드를 인덱싱
buckets [nBuckets]*bucket
// 부트스트랩 노드
nursery []*enode.Node
rand reseedingRandom
ips netutil.DistinctNetSet
revalidation 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)
}
ethdb
ethdb는 이더리움 데이터 저장의 추상화를 완료하며, 통합된 저장소 인터페이스를 제공합니다. 하위의 구체적인 데이터베이스는 leveldb일 수도 있고, pebble 또는 다른 데이터베이스일 수도 있습니다. 인터페이스 수준에서 통일성을 유지하기만 하면 많은 확장이 가능합니다.
일부 데이터(예: 블록 데이터)는 ethdb 인터페이스를 통해 하위 데이터베이스에 직접 읽기 및 쓰기가 가능하며, 다른 데이터 저장 인터페이스는 ethdb를 기반으로 구축됩니다. 예를 들어 데이터베이스의 많은 부분은 상태 데이터이며, 이러한 데이터는 MPT 구조로 조직됩니다. Geth에서 해당 구현은 trie이며, 노드가 실행되는 과정에서 trie 데이터는 많은 중간 상태를 생성합니다. 이러한 데이터는 직접 ethdb를 통해 읽기 및 쓰기가 불가능하며, triedb를 통해 이러한 데이터와 중간 상태를 관리한 후 최종적으로 ethdb를 통해 영구 저장됩니다.
ethdb/database.go에서 하위 데이터베이스의 읽기 및 쓰기 능력을 정의하는 인터페이스가 정의되어 있지만, 구체적인 구현은 포함되어 있지 않으며, 구체적인 구현은 서로 다른 데이터베이스가 직접 구현합니다. 예를 들어 leveldb 또는 pebble 데이터베이스입니다. Database에서는 두 개의 데이터 읽기 및 쓰기 인터페이스를 정의하며, KeyValueStore 인터페이스는 최신 블록, 상태 등과 같은 활성화되고 자주 변경될 수 있는 데이터를 저장하는 데 사용됩니다. AncientStore는 역사적 블록 데이터를 처리하는 데 사용되며, 이러한 데이터는 한 번 기록되면 거의 변경되지 않습니다.
// 데이터베이스의 최상위 인터페이스
type Database interface {
KeyValueStore
AncientStore
}
// KV 데이터의 읽기 및 쓰기 인터페이스
type KeyValueStore interface {
KeyValueReader
KeyValueWriter
KeyValueStater
KeyValueRangeDeleter
Batcher
Iteratee
Compacter
io.Closer
}
// 오래된 데이터의 읽기 및 쓰기 인터페이스
type AncientStore interface {
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의 Contract 구조체는 계약 호출의 구체적인 매개변수를 캡슐화하며, 호출자, 계약 코드, 입력 등을 포함하고, core/vm/opcodes.go에서는 현재 모든 작업 코드를 정의합니다:
// EVM
type EVM struct {
// 블록 컨텍스트, 블록 관련 정보 포함
Context BlockContext
// 거래 컨텍스트, 거래 관련 정보 포함
TxContext
// 상태 데이터베이스, 계정 상태에 접근하고 수정하는 데 사용
StateDB StateDB
// 현재 호출 깊이
depth int
// 체인 구성 매개변수
chainConfig *params.ChainConfig
chainRules params.Rules
// EVM 구성
Config Config
// 바이트코드 해석기
interpreter *EVMInterpreter
// 실행 중단 플래그
abort atomic.Bool
callGasTemp uint64
// 사전 컴파일 계약 매핑
precompiles map[common.Address]PrecompiledContract
jumpDests map[common.Hash]bitvec
}type EVMInterpreter struct {
// 소속 EVM 인스턴스를 가리킴
evm *EVM
// 작업 코드 점프 테이블
table *JumpTable
// Keccak256 해시 인스턴스, 작업 코드 간에 공유
hasher crypto.KeccakState
// Keccak256 해시 결과 버퍼
hasherBuf common.Hash
// 읽기 전용 모드 여부, 읽기 전용 모드에서는 상태 수정 불가
readOnly bool
// 마지막 CALL의 반환 데이터, 후속 재사용을 위해
returnData []byte
}type Contract struct {
// 호출자 주소
caller common.Address
// 계약 주소
address common.Addressjumpdests map[common.Hash]bitvec
analysis bitvec
// 계약 바이트코드
Code []byte
// 코드 해시
CodeHash common.Hash
// 호출 입력
Input []byte
// 계약 배포 여부
IsDeployment bool
// 시스템 호출 여부
IsSystemCall bool
// 사용 가능한 가스 양
Gas uint64
// 호출에 첨부된 ETH 양
value *uint256.Int
}
기타 모듈 구현
실행 계층의 기능은 계층화된 방식으로 구현되며, 다른 모듈과 기능은 이 세 가지 핵심 구성 요소를 기반으로 구축됩니다. 여기서는 몇 가지 핵심 모듈을 소개합니다.
eth/protocols 아래에는 현재 이더리움의 p2p 네트워크 하위 프로토콜의 구현이 있습니다. eth/68 및 snap 하위 프로토콜이 있으며, 이 하위 프로토콜들은 devp2p 위에 구축되었습니다.
eth/68은 이더리움의 핵심 프로토콜로, 프로토콜 이름은 eth이며, 68은 버전 번호입니다. 이 프로토콜을 기반으로 거래 풀(TxPool), 블록 동기화(Downloader) 및 거래 동기화(Fetcher) 등의 기능이 구현되었습니다. snap 프로토콜은 새로운 노드가 네트워크에 가입할 때 블록 및 상태 데이터를 빠르게 동기화하는 데 사용되며, 새로운 노드의 시작 시간을 크게 줄일 수 있습니다.
ethdb는 하위 데이터베이스의 읽기 및 쓰기 능력을 제공하며, 이더리움 프로토콜에는 많은 복잡한 데이터 구조가 있기 때문에 직접 ethdb를 통해 이러한 데이터 관리를 구현할 수 없습니다. 따라서 ethdb 위에 rawdb 및 statedb를 구현하여 각각 블록 및 상태 데이터를 관리합니다.
EVM은 모든 주요 프로세스를 관통하며, 블록 생성이나 블록 검증 등 모든 과정에서 거래를 실행하는 데 필요합니다.
5. Geth 노드 시작 프로세스
Geth의 시작은 두 단계로 나뉘며, 첫 번째 단계는 노드가 시작하는 데 필요한 구성 요소와 자원을 초기화하는 것입니다. 두 번째 단계에서는 노드를 공식적으로 시작하고 외부 서비스에 제공합니다.
노드 초기화
Geth 노드를 시작할 때는 다음과 같은 코드가 포함됩니다:

각 모듈의 초기화는 다음과 같습니다:
- cmd/geth/main.go: geth 노드 시작 진입점
- cmd/geth/config.go(makeFullNode): 구성 로드 및 노드 초기화
- node/node.go: 이더리움 노드의 핵심 컨테이너 초기화
- node.rpcstack.go: RPC 모듈 초기화
- accounts.manager.go: accountManager 초기화
- eth/backend.go: Ethereum 인스턴스 초기화
- node/node.go OpenDatabaseWithFreezer: chaindb 초기화
- eth/ethconfig/config.go: 합의 엔진 인스턴스 초기화(여기서 합의 엔진은 실제로 합의에 참여하지 않고, 합의 계층의 결과를 검증하며, 검증자의 출금 요청을 처리합니다)
- core/blockchain.go: 블록체인 초기화
- core/filterMaps.go: filtermaps 초기화
- core/txpool/blobpool/blobpool.go: blob 거래 풀 초기화
- core/txpool/legacypool/legacypool.go: 일반 거래 풀 초기화
- core/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): Filter RPC API 등록
- cmd/utils/flags.go(RegisterGraphQLService): GraphQL RPC API 등록(구성된 경우)
- cmd/utils/flags.go(RegisterEthStatsService): EthStats RPC API 등록(구성된 경우)
- eth/catalyst/api.go: Engine API 등록
노드 초기화는 cmd/geth/config.go의 makeFullNode에서 완료되며, 다음 세 가지 모듈을 초기화하는 데 중점을 둡니다.
첫 번째 단계에서는 node/node.go의 Node 구조체를 초기화하여 전체 노드 컨테이너를 생성하며, 모든 기능은 이 컨테이너 내에서 실행됩니다. 두 번째 단계에서는 Ethereum 구조체를 초기화하며, 이더리움의 다양한 핵심 기능 구현이 포함됩니다. Ethereum은 Node에 등록되어야 하며, 세 번째 단계는 Engine API를 Node에 등록하는 것입니다.
Node 초기화는 Node 인스턴스를 생성하고 p2p 서버, 계정 관리 및 HTTP와 같은 외부에 노출되는 프로토콜 포트를 초기화합니다.

Ethereum 초기화는 훨씬 복잡하며, 대부분의 핵심 기능이 여기에서 초기화됩니다. 먼저 ethdb를 초기화하고 저장소에서 체인 구성을 로드한 후 합의 엔진을 생성합니다. 여기서 합의 엔진은 합의 작업을 수행하지 않고, 합의 계층의 결과를 검증합니다. 합의 계층에서 출금 요청이 발생하면 여기에서 실제 출금 작업이 수행됩니다. 그런 다음 블록체인 구조와 거래 풀을 초기화합니다.
이 모든 작업이 완료되면 핸들러를 초기화합니다. 핸들러는 모든 p2p 네트워크 요청을 처리하는 진입점으로, 거래 동기화, 블록 다운로드 등을 포함하며, 이더리움이 분산 실행을 구현하는 핵심 구성 요소입니다. 이러한 모든 작업이 완료되면 devp2p 기반으로 구현된 하위 프로토콜(예: eth/68, snap 등)을 Node 컨테이너에 등록하고, 마지막으로 Ethereum을 Node 컨테이너에 생애 주기로 등록하여 Ethereum 초기화가 완료됩니다.

마지막으로 Engine API의 초기화는 상대적으로 간단하며, Engine API를 Node에 등록하는 것뿐입니다. 여기까지 노드 초기화가 완료되었습니다.
노드 시작
노드 초기화가 완료된 후, 노드를 시작해야 합니다. 노드 시작 프로세스는 상대적으로 간단하며, 이미 등록된 RPC 서비스와 생애 주기를 모두 시작하면 전체 노드가 외부에 서비스를 제공할 수 있습니다.
6. 요약
이더리움 실행 계층의 구현을 깊이 이해하기 전에 이더리움에 대한 전체적인 인식을 가져야 합니다. 이더리움을 거래에 의해 구동되는 상태 기계로 볼 수 있으며, 실행 계층은 거래의 실행과 상태의 변화를 담당하고, 합의 계층은 실행 계층의 작동을 유도하며, 실행 계층이 블록을 생성하도록 하고, 거래의 순서를 결정하며, 블록에 투표하고, 블록이 최종성을 얻도록 합니다. 이 상태 기계는 분산되어 있으므로 p2p 네트워크를 통해 다른 노드와 통신하여 상태 데이터의 일관성을 공동으로 유지해야 합니다.
실행 계층은 거래의 순서를 결정하지 않고, 거래를 실행하고 거래 실행 후의 상태 변화를 기록하는 역할을 합니다. 여기서 기록은 두 가지 형태가 있으며, 하나는 블록의 형태로 모든 상태 변화를 기록하는 것이고, 다른 하나는 데이터베이스에 현재 상태를 기록하는 것입니다. 동시에 실행 계층은 거래의 진입점으로, 거래 풀을 통해 아직 블록에 패키징되지 않은 거래를 저장합니다. 다른 노드가 블록, 상태 및 거래 데이터를 가져와야 할 경우, 실행 계층은 p2p 네트워크를 통해 이러한 정보를 전송합니다.
실행 계층에는 세 가지 핵심 모듈이 있습니다: 계산, 저장소 및 네트워크. 계산은 EVM의 구현에 해당하며, 저장소는 ethdb의 구현에 해당하고, 네트워크는 devp2p의 구현에 해당합니다. 이러한 전체적인 인식을 바탕으로 각 하위 모듈을 깊이 이해할 수 있으며, 구체적인 세부 사항에 빠지지 않을 수 있습니다.
참조
[1]https://ethereum.org/zh/what-is-ethereum/
[2]https://epf.wiki/#/wiki/protocol/architecture
[3]https://clientdiversity.org/#distribution
[4]https://github.com/ethereum/devp2p














