geth源码解读--rpc 调用getBlock()解析

go-ethereum/internal/ethapi/api.goBlockByNumber 接收到请求,拿到参数 blockNrfullTx 然后调用 rpcOutputBlock 拿到tansaction的数据,如果fullTx 为True则返回transaction的详细数据,否则返回transaction的哈希列表 :

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// GetBlockByNumber returns the requested block. When blockNr is -1 the chain head is returned. When fullTx is true all
// transactions in the block are returned in full detail, otherwise only the transaction hash is returned.
func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
block, err := s.b.BlockByNumber(ctx, blockNr)
if block != nil {
response, err := s.rpcOutputBlock(block, true, fullTx)
if err == nil && blockNr == rpc.PendingBlockNumber {
// Pending blocks need to nil out a few fields
for _, field := range []string{"hash", "nonce", "miner"} {
response[field] = nil
}
}
return response, err
}
return nil, err
}

// GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full
// detail, otherwise only the transaction hash is returned.
func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) {
block, err := s.b.GetBlock(ctx, blockHash)
if block != nil {
return s.rpcOutputBlock(block, true, fullTx)
}
return nil, err
}

// rpcOutputBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
// transaction hashes.
func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) {
head := b.Header() // copies the header once
fields := map[string]interface{}{
"number": (*hexutil.Big)(head.Number),
"hash": b.Hash(),
"parentHash": head.ParentHash,
"nonce": head.Nonce,
"mixHash": head.MixDigest,
"sha3Uncles": head.UncleHash,
"logsBloom": head.Bloom,
"stateRoot": head.Root,
"miner": head.Coinbase,
"difficulty": (*hexutil.Big)(head.Difficulty),
"totalDifficulty": (*hexutil.Big)(s.b.GetTd(b.Hash())),
"extraData": hexutil.Bytes(head.Extra),
"size": hexutil.Uint64(b.Size()),
"gasLimit": hexutil.Uint64(head.GasLimit),
"gasUsed": hexutil.Uint64(head.GasUsed),
"timestamp": (*hexutil.Big)(head.Time),
"transactionsRoot": head.TxHash,
"receiptsRoot": head.ReceiptHash,
}

if inclTx {
formatTx := func(tx *types.Transaction) (interface{}, error) {
return tx.Hash(), nil
}

if fullTx {
formatTx = func(tx *types.Transaction) (interface{}, error) {
return newRPCTransactionFromBlockHash(b, tx.Hash()), nil
}
}

txs := b.Transactions()
transactions := make([]interface{}, len(txs))
var err error
for i, tx := range b.Transactions() {
if transactions[i], err = formatTx(tx); err != nil {
return nil, err
}
}
fields["transactions"] = transactions
}

uncles := b.Uncles()
uncleHashes := make([]common.Hash, len(uncles))
for i, uncle := range uncles {
uncleHashes[i] = uncle.Hash()
}
fields["uncles"] = uncleHashes

return fields, nil
}

// RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction
type RPCTransaction struct {
BlockHash common.Hash `json:"blockHash"`
BlockNumber *hexutil.Big `json:"blockNumber"`
From common.Address `json:"from"`
Gas hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
Hash common.Hash `json:"hash"`
Input hexutil.Bytes `json:"input"`
Nonce hexutil.Uint64 `json:"nonce"`
To *common.Address `json:"to"`
TransactionIndex hexutil.Uint `json:"transactionIndex"`
Value *hexutil.Big `json:"value"`
V *hexutil.Big `json:"v"`
R *hexutil.Big `json:"r"`
S *hexutil.Big `json:"s"`
}

执行示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
> eth.getBlock(46251)
{
difficulty: 1461115576915,
extraData: "0x476574682f76312e302e312f6c696e75782f676f312e342e32",
gasLimit: 22150,
gasUsed: 21000,
hash: "0xeecb25ce31fb7a212500443a31d463b663a2c5f65e7838c5364bb8110a0750f1",
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
miner: "0x47ff6576639c2e94762ea5443978d7681c0e78dc",
mixHash: "0xaaad7bf5c87f1b2fc08ed73a1519dae6832b458a112891cd7c5a0a86125a8321",
nonce: "0x4e164418632effd2",
number: 46251,
parentHash: "0x3c93a7ff6fb6f39e63456e9ea6fb069c7783f3c00b1012e36f0a8a02bdae604a",
receiptsRoot: "0x5add83c7805c02ab079871e3a2379b6a764d34e557d758ddce00b6a1bf5cc79f",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 652,
stateRoot: "0x7692ecfeac33453620701bad9eae23f89c01e845274f0e5adec78a5ea96f5083",
timestamp: 1438919990,
totalDifficulty: 42835839136947252,
transactions: ["0x304346de678ddca5b1311970e7e8d3f26a42d2924aaa2f2d73b8781a9a9a2c73"],
transactionsRoot: "0xf642520ac0ce7008292d2c835b8d2e6eb90822f8a68b8ae368b0774229371b5d",
uncles: []
}

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
> eth.getBlock(46251, true)
{
difficulty: 1461115576915,
extraData: "0x476574682f76312e302e312f6c696e75782f676f312e342e32",
gasLimit: 22150,
gasUsed: 21000,
hash: "0xeecb25ce31fb7a212500443a31d463b663a2c5f65e7838c5364bb8110a0750f1",
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
miner: "0x47ff6576639c2e94762ea5443978d7681c0e78dc",
mixHash: "0xaaad7bf5c87f1b2fc08ed73a1519dae6832b458a112891cd7c5a0a86125a8321",
nonce: "0x4e164418632effd2",
number: 46251,
parentHash: "0x3c93a7ff6fb6f39e63456e9ea6fb069c7783f3c00b1012e36f0a8a02bdae604a",
receiptsRoot: "0x5add83c7805c02ab079871e3a2379b6a764d34e557d758ddce00b6a1bf5cc79f",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 652,
stateRoot: "0x7692ecfeac33453620701bad9eae23f89c01e845274f0e5adec78a5ea96f5083",
timestamp: 1438919990,
totalDifficulty: 42835839136947252,
transactions: [{
blockHash: "0xeecb25ce31fb7a212500443a31d463b663a2c5f65e7838c5364bb8110a0750f1",
blockNumber: 46251,
from: "0xfd2605a2bf58fdbb90db1da55df61628b47f9e8c",
gas: 21000,
gasPrice: 88893746891,
hash: "0x304346de678ddca5b1311970e7e8d3f26a42d2924aaa2f2d73b8781a9a9a2c73",
input: "0x",
nonce: 1,
r: "0x26f3e5c541f526b549d9f003c8e7de442d3cf4bbf9c66934027672693a88275f",
s: "0x7bd40d54be85b58444d6f0a28f22591474a84380fc8a6a7515bda42b5ebb0e2b",
to: "0x073f70b5bfade6409e4951ef72bc8f4157677729",
transactionIndex: 0,
v: "0x1c",
value: 10000000000000000
}],
transactionsRoot: "0xf642520ac0ce7008292d2c835b8d2e6eb90822f8a68b8ae368b0774229371b5d",
uncles: []
}

go-ethereum/internal/ethapi/api.go => GetBlockByNumber 实际上是通过调用 go-ethereum/eth/api_backend.go 中的 BlockByNumber 来拿到区块数据的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func (b *EthAPIBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) {
// Pending block is only known by the miner
if blockNr == rpc.PendingBlockNumber {
block := b.eth.miner.PendingBlock()
return block, nil
}
// Otherwise resolve and return the block
if blockNr == rpc.LatestBlockNumber {
return b.eth.blockchain.CurrentBlock(), nil
}
return b.eth.blockchain.GetBlockByNumber(uint64(blockNr)), nil
}
func (b *EthAPIBackend) GetBlock(ctx context.Context, hash common.Hash) (*types.Block, error) {
return b.eth.blockchain.GetBlockByHash(hash), nil
}

go-ethereum/eth/api_backend.go 中的 BlockByNumber 又是接着调用 go-ethereum/core/blockchain.go GetBlockByNumber 来拿到区块数据,从源代码中也可以看到不管是 GetBlockByNumber 还是 GetBlockByHash 都是调用 GetBlock 来拿到区块的,并且调用GetBlock 之前还会先分别集齐Number和Hash 。
最后 GetBlock 通过 rawdbReadBlock 来读取区块,并且缓存区块到 blockCache 中:

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
// GetBlock retrieves a block from the database by hash and number,
// caching it if found.
func (bc *BlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
// Short circuit if the block's already in the cache, retrieve otherwise
if block, ok := bc.blockCache.Get(hash); ok {
return block.(*types.Block)
}
block := rawdb.ReadBlock(bc.db, hash, number)
if block == nil {
return nil
}
// Cache the found block for next time and return
bc.blockCache.Add(block.Hash(), block)
return block
}

// GetBlockByHash retrieves a block from the database by hash, caching it if found.
func (bc *BlockChain) GetBlockByHash(hash common.Hash) *types.Block {
number := bc.hc.GetBlockNumber(hash)
if number == nil {
return nil
}
return bc.GetBlock(hash, *number)
}

// GetBlockByNumber retrieves a block from the database by number, caching it
// (associated with its hash) if found.
func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block {
hash := rawdb.ReadCanonicalHash(bc.db, number)
if hash == (common.Hash{}) {
return nil
}
return bc.GetBlock(hash, number)
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// ReadBlock retrieves an entire block corresponding to the hash, assembling it
// back from the stored header and body. If either the header or body could not
// be retrieved nil is returned.
//
// Note, due to concurrent download of header and block body the header and thus
// canonical hash can be stored in the database but the body data not (yet).
func ReadBlock(db DatabaseReader, hash common.Hash, number uint64) *types.Block {
header := ReadHeader(db, hash, number)
if header == nil {
return nil
}
body := ReadBody(db, hash, number)
if body == nil {
return nil
}
return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles)
}