1. 交易的签名
  2. 理解收据receipt
  3. 理解区块
  4. 理解交易
  5. blockchain核心
  6. 布隆过滤器原理
  7. forkId 解读
  8. oracle 原理和实现
  9. 交易池分析
  10. TxList 解读
  11. MPT树
  12. 区块同步
  13. geth源码学习——介绍
  14. How Geth starts its server

When starting up, the Geth client begins by parsing the command line parameters. This process kicks off with the geth function: func geth(ctx *cli.Context) error at cmd/geth/main.go:326 where the geth prepares the metric if enabled. Subsequently, the makeFullNode function takes over. It reads the provided configuration file or command line flags. As a result, it returns two key components: the protocol stack and the backend API.

It’s worth noting the importance of the Backend API. It establishes a myriad of interfaces, facilitating interaction with nodes and handling external RPC call limits.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// Backend interface provides the common API services (that are provided by
// both full and light clients) with access to necessary functions.
type Backend interface {
// General Ethereum API
SyncProgress() ethereum.SyncProgress

SuggestGasTipCap(ctx context.Context) (*big.Int, error)
FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error)
ChainDb() ethdb.Database
AccountManager() *accounts.Manager
ExtRPCEnabled() bool
RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
RPCEVMTimeout() time.Duration // global timeout for eth_call over rpc: DoS protection
RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs
UnprotectedAllowed() bool // allows only for EIP155 transactions.

// Blockchain API
SetHead(number uint64)
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error)
CurrentHeader() *types.Header
CurrentBlock() *types.Header
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error)
StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error)
StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error)
PendingBlockAndReceipts() (*types.Block, types.Receipts)
GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
GetTd(ctx context.Context, hash common.Hash) *big.Int
GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) (*vm.EVM, func() error)
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription

// Transaction pool API
SendTx(ctx context.Context, signedTx *types.Transaction) error
GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
GetPoolTransactions() (types.Transactions, error)
GetPoolTransaction(txHash common.Hash) *types.Transaction
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
Stats() (pending int, queued int)
TxPoolContent() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction)
TxPoolContentFrom(addr common.Address) ([]*types.Transaction, []*types.Transaction)
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription

ChainConfig() *params.ChainConfig
Engine() consensus.Engine

// This is copied from filters.Backend
// eth/filters needs to be initialized from this backend type, so methods needed by
// it must also be included here.
GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error)
GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error)
SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription
SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription
SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription
BloomStatus() (uint64, uint64)
ServiceFilter(ctx context.Context, session *bloombits.MatcherSession)
}

For instance, the SyncProgress() method captures the node’s status as it synchronizes with the Ethereum network.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
type SyncProgress struct {
StartingBlock uint64 // Block number where sync began
CurrentBlock uint64 // Current block number where sync is at
HighestBlock uint64 // Highest alleged block number in the chain

// "fast sync" fields. These used to be sent by geth, but are no longer used
// since version v1.10.
PulledStates uint64 // Number of state trie entries already downloaded
KnownStates uint64 // Total number of state trie entries known about

// "snap sync" fields.
SyncedAccounts uint64 // Number of accounts downloaded
SyncedAccountBytes uint64 // Number of account trie bytes persisted to disk
SyncedBytecodes uint64 // Number of bytecodes downloaded
SyncedBytecodeBytes uint64 // Number of bytecode bytes downloaded
SyncedStorage uint64 // Number of storage slots downloaded
SyncedStorageBytes uint64 // Number of storage trie bytes persisted to disk

HealedTrienodes uint64 // Number of state trie nodes downloaded
HealedTrienodeBytes uint64 // Number of state trie bytes persisted to disk
HealedBytecodes uint64 // Number of bytecodes downloaded
HealedBytecodeBytes uint64 // Number of bytecodes persisted to disk

HealingTrienodes uint64 // Number of state trie nodes pending
HealingBytecode uint64 // Number of bytecodes pending
}

Regarding the protocol stack, it’s represented by the Node structure, which comprises numerous fields. Among these, the stop field plays a pivotal role as it signals the termination of the server.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Node is a container on which services can be registered.
type Node struct {
eventmux *event.TypeMux
config *Config
accman *accounts.Manager
log log.Logger
keyDir string // key store directory
keyDirTemp bool // If true, key directory will be removed by Stop
dirLock *flock.Flock // prevents concurrent use of instance directory
stop chan struct{} // Channel to wait for termination notifications
server *p2p.Server // Currently running P2P networking layer
startStopLock sync.Mutex // Start/Stop are protected by an additional lock
state int // Tracks state of node lifecycle

lock sync.Mutex
lifecycles []Lifecycle // All registered backends, services, and auxiliary services that have a lifecycle
rpcAPIs []rpc.API // List of APIs currently provided by the node
http *httpServer //
ws *httpServer //
httpAuth *httpServer //
wsAuth *httpServer //
ipc *ipcServer // Stores information about the ipc http server
inprocHandler *rpc.Server // In-process RPC request handler to process the API requests

databases map[*closeTrackingDB]struct{} // All open databases
}

Once all configurations are in place and prior to the server’s actual launch, you should observe the following output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
INFO [09-13|12:46:39.391] Starting Geth on Ethereum mainnet...
INFO [09-13|12:46:39.392] Bumping default cache on mainnet provided=1024 updated=4096
INFO [09-13|12:46:39.396] Maximum peer count ETH=50 LES=0 total=50
INFO [09-13|12:46:39.400] Smartcard socket not found, disabling err="stat /run/pcscd/pcscd.comm: no such file or directory"
INFO [09-13|12:46:39.408] Set global gas cap cap=50,000,000
INFO [09-13|12:46:39.408] Initializing the KZG library backend=gokzg
INFO [09-13|12:46:39.611] Allocated trie memory caches clean=614.00MiB dirty=1024.00MiB
INFO [09-13|12:46:39.611] Using pebble as the backing database
INFO [09-13|12:46:39.611] Allocated cache and file handles database=/home/username/.ethereum/geth/chaindata cache=2.00GiB handles=32767
INFO [09-13|12:46:39.677] Opened ancient database database=/home/username/.ethereum/geth/chaindata/ancient/chain readonly=false
INFO [09-13|12:46:39.681] Initialising Ethereum protocol network=1 dbversion=8
INFO [09-13|12:46:39.681]
INFO [09-13|12:46:39.681] ---------------------------------------------------------------------------------------------------------------------------------------------------------
INFO [09-13|12:46:39.683] Chain ID: 1 (mainnet)
INFO [09-13|12:46:39.683] Consensus: Beacon (proof-of-stake), merged from Ethash (proof-of-work)
INFO [09-13|12:46:39.683]
INFO [09-13|12:46:39.683] Pre-Merge hard forks (block based):
INFO [09-13|12:46:39.683] - Homestead: #1150000 (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/homestead.md)
INFO [09-13|12:46:39.683] - DAO Fork: #1920000 (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/dao-fork.md)
INFO [09-13|12:46:39.683] - Tangerine Whistle (EIP 150): #2463000 (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/tangerine-whistle.md)
INFO [09-13|12:46:39.683] - Spurious Dragon/1 (EIP 155): #2675000 (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/spurious-dragon.md)
INFO [09-13|12:46:39.683] - Spurious Dragon/2 (EIP 158): #2675000 (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/spurious-dragon.md)
INFO [09-13|12:46:39.683] - Byzantium: #4370000 (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/byzantium.md)
INFO [09-13|12:46:39.683] - Constantinople: #7280000 (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/constantinople.md)
INFO [09-13|12:46:39.683] - Petersburg: #7280000 (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/petersburg.md)
INFO [09-13|12:46:39.683] - Istanbul: #9069000 (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/istanbul.md)
INFO [09-13|12:46:39.683] - Muir Glacier: #9200000 (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/muir-glacier.md)
INFO [09-13|12:46:39.683] - Berlin: #12244000 (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/berlin.md)
INFO [09-13|12:46:39.683] - London: #12965000 (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/london.md)
INFO [09-13|12:46:39.683] - Arrow Glacier: #13773000 (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/arrow-glacier.md)
INFO [09-13|12:46:39.683] - Gray Glacier: #15050000 (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/gray-glacier.md)
INFO [09-13|12:46:39.683]
INFO [09-13|12:46:39.683] Merge configurationd:
INFO [09-13|12:46:39.683] - Hard-fork specification: https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/paris.md
INFO [09-13|12:46:39.683] - Network known to be merged: true
INFO [09-13|12:46:39.683] - Total terminal difficulty: 58750000000000000000000
INFO [09-13|12:46:39.683]
INFO [09-13|12:46:39.683] Post-Merge hard forks (timestamp based):
INFO [09-13|12:46:39.684] - Shanghai: @1681338455 (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md)
INFO [09-13|12:46:39.684]
INFO [09-13|12:46:39.684] ---------------------------------------------------------------------------------------------------------------------------------------------------------
INFO [09-13|12:46:39.684]
INFO [09-13|12:46:39.686] Loaded most recent local block number=0 hash=d4e567..cb8fa3 td=17,179,869,184 age=54y5mo3w
WARN [09-13|12:46:39.688] Loaded snapshot journal diffs=missing
INFO [09-13|12:46:39.688] Resuming state snapshot generation root=d7f897..0f0544 accounts=0 slots=0 storage=0.00B dangling=0 elapsed="34.181µs"
INFO [09-13|12:46:39.688] Loaded local transaction journal transactions=0 dropped=0
INFO [09-13|12:46:39.689] Regenerated local transaction journal transactions=0 accounts=0
INFO [09-13|12:46:39.729] Chain post-merge, sync via beacon client
INFO [09-13|12:46:39.730] Gasprice oracle is ignoring threshold set threshold=2
WARN [09-13|12:46:39.738] Unclean shutdown detected booted=2023-09-12T15:07:35+0000 age=21h39m4s
WARN [09-13|12:46:39.738] Unclean shutdown detected booted=2023-09-13T08:02:05+0000 age=4h44m34s
WARN [09-13|12:46:39.739] Engine API enabled protocol=eth

Following this, the startNode function executes, paving the way for StartNode, which is responsible for actually initiating the server. The method stack.Start() found in cmd/utils/cmd.go:72 will launch the node. Once the server is up and running, the Geth client keeps tabs on the disk space and monitors system signals to determine when to terminate the server.

Within the Start() method, the line err := n.openEndpoints() is responsible to open networking and RPC endpoint. Meanwhile, the n.server.Start() method, located at node/node.go:269 initiates the p2p discovery and handshake processes. Concurrently, the line err := n.startRPC() located at node/node.go:273 brings the RPC endpoint to life. The output should resemble the following:

1
2
3
4
5
6
7
8
9
INFO [09-13|13:56:00.514] Starting peer-to-peer node               instance=Geth/v1.12.1-unstable-60ce4e8d-20230810/linux-amd64/go1.20.6
INFO [09-13|13:56:04.989] New local node record seq=1,694,613,193,948 id=72f98ebcedb92640 ip=127.0.0.1 udp=35555 tcp=35555
INFO [09-13|13:56:05.083] Started P2P networking self=enode://9fc6b61a19d99e8a78ae8f8dbd12f27d208ce9b01dd52177fbfee1812a37467b3c3bab49c181b26b240f52bf1ac8afe64915f9b89465a540a74fdcb5e37f2abd@127.0.0.1:35555
INFO [09-13|13:56:05.325] Generating state snapshot root=d7f897..0f0544 at=9a53a5..ad4ba5 accounts=5375 slots=0 storage=247.62KiB dangling=0 elapsed=8.003s eta=28.97s
INFO [09-13|13:56:10.187] IPC endpoint opened url=/home/username/.ethereum/geth.ipc
INFO [09-13|13:56:10.200] Loaded JWT secret file path=/home/username/.ethereum/geth/jwtsecret crc32=0x3392b636
INFO [09-13|13:56:11.116] WebSocket enabled url=ws://127.0.0.1:8553
INFO [09-13|13:56:11.121] HTTP server started endpoint=127.0.0.1:8553 auth=true prefix= cors=localhost vhosts=localhost

Once the p2p network layer has been executed successfully, the crucial Ethereum protocol initiates and remains active. Two essential components stand out:

  • txPool: This manages transactions according to a specified strategy.
  • blockchain: This encompasses the EVM (Ethereum Virtual Machine) and upholds the chain structure of the blockchain.

The rationale for defining the subprotocol as a slice of lifecycle is that Geth also supports other protocols layered over the Devp2p protocol.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Ethereum implements the Ethereum full node service.
type Ethereum struct {
config *ethconfig.Config

// Handlers
txPool *txpool.TxPool

blockchain *core.BlockChain
handler *handler
ethDialCandidates enode.Iterator
snapDialCandidates enode.Iterator
merger *consensus.Merger

// DB interfaces
chainDb ethdb.Database // Block chain database

eventMux *event.TypeMux
engine consensus.Engine
accountManager *accounts.Manager

bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests
bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports
closeBloomHandler chan struct{}

APIBackend *EthAPIBackend

miner *miner.Miner
gasPrice *big.Int
etherbase common.Address

networkID uint64
netRPCService *ethapi.NetAPI

p2pServer *p2p.Server

lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase)

shutdownTracker *shutdowncheck.ShutdownTracker // Tracks if and when the node has shutdown ungracefully
}

To summarize, the steps for starting a Geth client are as follows:

  1. Command Line Parsing: The parser breaks down flags, subcommands, or the configuration file into a context.
  2. Process Context: The command line context is processed, establishing memory cache allowances and, if enabled, metrics that monitor the runtime environment.
  3. Protocol Stack Assembly: Based on the provided configuration, the protocol stack is assembled into a node struct. Concurrently, interaction methods are incorporated into ethapi.Backend.
  4. Node Initialization:
    1. P2P Network Layer: Initiates the p2p network layer for node discovery and facilitates basic pre-subprotocol (devp2p) message exchange.
    2. RPC Endpoint: This step starts the RPC endpoint.
    3. Subprotocols Activation: All the specified subprotocols in the lifecycles of the local node are initiated.
    4. Monitoring: Upon successful initialization, the system begins monitoring disk space and awaits system signals for termination.