From 6b9396e2251c2c3b9d97f3b070da20e7ebf28308 Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Fri, 24 Mar 2023 10:57:20 +0100 Subject: [PATCH 1/9] Add constant PoW header fields for ETH compat. --- core/types/block.go | 38 ++++++++++++++++++++++++++++++++++++++ internal/ethapi/api.go | 9 +++++++++ 2 files changed, 47 insertions(+) diff --git a/core/types/block.go b/core/types/block.go index ba39db5434..2669bcdc13 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -18,6 +18,7 @@ package types import ( + "encoding/binary" "encoding/json" "fmt" "io" @@ -35,24 +36,55 @@ import ( var ( EmptyRootHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + emptyUncleHash = rlpHash([]*Header(nil)) // 1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347 EmptyRandomness = Randomness{} EmptyEpochSnarkData = EpochSnarkData{} ) +// A BlockNonce is a 64-bit hash which proves (combined with the +// mix-hash) that a sufficient amount of computation has been carried +// out on a block. +type BlockNonce [8]byte + +// EncodeNonce converts the given integer to a block nonce. +func EncodeNonce(i uint64) BlockNonce { + var n BlockNonce + binary.BigEndian.PutUint64(n[:], i) + return n +} + +// Uint64 returns the integer value of a block nonce. +func (n BlockNonce) Uint64() uint64 { + return binary.BigEndian.Uint64(n[:]) +} + +// MarshalText encodes n as a hex string with 0x prefix. +func (n BlockNonce) MarshalText() ([]byte, error) { + return hexutil.Bytes(n[:]).MarshalText() +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (n *BlockNonce) UnmarshalText(input []byte) error { + return hexutil.UnmarshalFixedText("BlockNonce", input, n[:]) +} + //go:generate gencodec -type Header -field-override headerMarshaling -out gen_header_json.go // Header represents a block header in the Ethereum blockchain. type Header struct { ParentHash common.Hash `json:"parentHash" gencodec:"required"` + UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` Coinbase common.Address `json:"miner" gencodec:"required"` Root common.Hash `json:"stateRoot" gencodec:"required"` TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` Bloom Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *big.Int `json:"difficulty" gencodec:"required"` Number *big.Int `json:"number" gencodec:"required"` GasUsed uint64 `json:"gasUsed" gencodec:"required"` Time uint64 `json:"timestamp" gencodec:"required"` Extra []byte `json:"extraData" gencodec:"required"` + Nonce BlockNonce `json:"nonce"` // Used to cache deserialized istanbul extra data extraLock sync.Mutex @@ -271,6 +303,8 @@ func NewBlock(header *Header, txs []*Transaction, receipts []*Receipt, randomnes b.randomness = &EmptyRandomness } + b.header.UncleHash = emptyUncleHash + return b } @@ -286,15 +320,19 @@ func NewBlockWithHeader(header *Header) *Block { func CopyHeader(h *Header) *Header { cpy := Header{ ParentHash: h.ParentHash, + UncleHash: h.UncleHash, Coinbase: h.Coinbase, Root: h.Root, TxHash: h.TxHash, ReceiptHash: h.ReceiptHash, Bloom: h.Bloom, + Difficulty: h.Difficulty, Number: new(big.Int), GasLimit: h.GasLimit, GasUsed: h.GasUsed, Time: h.Time, + MixDigest: h.MixDigest, + Nonce: h.Nonce, } if h.Number != nil { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index d54b319bc7..933c0ffd05 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1149,6 +1149,15 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} { "transactionsRoot": head.TxHash, "receiptsRoot": head.ReceiptHash, } + // Former proof-of-work fields, now constants, see https://eips.ethereum.org/EIPS/eip-3675#block-structure + // Set after GFork + if head.Difficulty != nil { + result["difficulty"] = (*hexutil.Big)(head.Difficulty) + result["nonce"] = head.Nonce + result["sha3Uncles"] = head.UncleHash + result["uncles"] = []interface{}{} + result["mixHash"] = head.MixDigest + } return result } From 0270d12f5d4fc7a06ac23238811a3c8958283657 Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Tue, 28 Mar 2023 12:16:43 +0200 Subject: [PATCH 2/9] Update block header marshaling --- core/types/block.go | 23 ++++++++++++----------- core/types/gen_header_json.go | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/core/types/block.go b/core/types/block.go index 2669bcdc13..9518b85fa3 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -73,18 +73,15 @@ func (n *BlockNonce) UnmarshalText(input []byte) error { // Header represents a block header in the Ethereum blockchain. type Header struct { ParentHash common.Hash `json:"parentHash" gencodec:"required"` - UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` Coinbase common.Address `json:"miner" gencodec:"required"` Root common.Hash `json:"stateRoot" gencodec:"required"` TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` Bloom Bloom `json:"logsBloom" gencodec:"required"` - Difficulty *big.Int `json:"difficulty" gencodec:"required"` Number *big.Int `json:"number" gencodec:"required"` GasUsed uint64 `json:"gasUsed" gencodec:"required"` Time uint64 `json:"timestamp" gencodec:"required"` Extra []byte `json:"extraData" gencodec:"required"` - Nonce BlockNonce `json:"nonce"` // Used to cache deserialized istanbul extra data extraLock sync.Mutex @@ -92,16 +89,22 @@ type Header struct { extraError error GasLimit uint64 `json:"gasLimit" rlp:"optional"` + // Proof-of-work fields for Eth compatibility + Difficulty *big.Int `json:"difficulty" rlp:"optional"` + Nonce BlockNonce `json:"nonce" rlp:"optional"` + UncleHash common.Hash `json:"sha3Uncles" rlp:"optional"` + MixDigest common.Hash `json:"mixHash" rlp:"optional"` } // field type overrides for gencodec type headerMarshaling struct { - Number *hexutil.Big - GasLimit hexutil.Uint64 - GasUsed hexutil.Uint64 - Time hexutil.Uint64 - Extra hexutil.Bytes - Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON + Number *hexutil.Big + GasLimit hexutil.Uint64 + GasUsed hexutil.Uint64 + Time hexutil.Uint64 + Extra hexutil.Bytes + Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON + Difficulty *hexutil.Big } // Hash returns the block hash of the header, which is simply the keccak256 hash of its @@ -303,8 +306,6 @@ func NewBlock(header *Header, txs []*Transaction, receipts []*Receipt, randomnes b.randomness = &EmptyRandomness } - b.header.UncleHash = emptyUncleHash - return b } diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go index fbdc0cf284..9fa3c9b80a 100644 --- a/core/types/gen_header_json.go +++ b/core/types/gen_header_json.go @@ -27,6 +27,10 @@ func (h Header) MarshalJSON() ([]byte, error) { Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` Extra hexutil.Bytes `json:"extraData" gencodec:"required"` GasLimit hexutil.Uint64 `json:"gasLimit" rlp:"optional"` + Difficulty *hexutil.Big `json:"difficulty" rlp:"optional"` + Nonce BlockNonce `json:"nonce" rlp:"optional"` + UncleHash common.Hash `json:"sha3Uncles" rlp:"optional"` + MixDigest common.Hash `json:"mixHash" rlp:"optional"` Hash common.Hash `json:"hash"` } var enc Header @@ -41,6 +45,10 @@ func (h Header) MarshalJSON() ([]byte, error) { enc.Time = hexutil.Uint64(h.Time) enc.Extra = h.Extra enc.GasLimit = hexutil.Uint64(h.GasLimit) + enc.Difficulty = (*hexutil.Big)(h.Difficulty) + enc.Nonce = h.Nonce + enc.UncleHash = h.UncleHash + enc.MixDigest = h.MixDigest enc.Hash = h.Hash() return json.Marshal(&enc) } @@ -59,6 +67,10 @@ func (h *Header) UnmarshalJSON(input []byte) error { Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` GasLimit *hexutil.Uint64 `json:"gasLimit" rlp:"optional"` + Difficulty *hexutil.Big `json:"difficulty" rlp:"optional"` + Nonce *BlockNonce `json:"nonce" rlp:"optional"` + UncleHash *common.Hash `json:"sha3Uncles" rlp:"optional"` + MixDigest *common.Hash `json:"mixHash" rlp:"optional"` } var dec Header if err := json.Unmarshal(input, &dec); err != nil { @@ -107,5 +119,17 @@ func (h *Header) UnmarshalJSON(input []byte) error { if dec.GasLimit != nil { h.GasLimit = uint64(*dec.GasLimit) } + if dec.Difficulty != nil { + h.Difficulty = (*big.Int)(dec.Difficulty) + } + if dec.Nonce != nil { + h.Nonce = *dec.Nonce + } + if dec.UncleHash != nil { + h.UncleHash = *dec.UncleHash + } + if dec.MixDigest != nil { + h.MixDigest = *dec.MixDigest + } return nil } From bb1fe4ae92de43e75e7ff2143105f6b4a97dfeac Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Tue, 28 Mar 2023 12:17:21 +0200 Subject: [PATCH 3/9] Improve test error messages --- ethclient/ethclient_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index f09f743062..79b80c40e2 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -396,7 +396,7 @@ func testGetBlock(t *testing.T, client *rpc.Client) { // Get current block number blockNumber, err := ec.BlockNumber(context.Background()) if err != nil { - t.Fatalf("unexpected error: %v", err) + t.Fatalf("BlockNumber: %v", err) } if blockNumber != 1 { t.Fatalf("BlockNumber returned wrong number: %d", blockNumber) @@ -404,7 +404,7 @@ func testGetBlock(t *testing.T, client *rpc.Client) { // Get current block by number block, err := ec.BlockByNumber(context.Background(), new(big.Int).SetUint64(blockNumber)) if err != nil { - t.Fatalf("unexpected error: %v", err) + t.Fatalf("BlockByNumber: %v", err) } if block.NumberU64() != blockNumber { t.Fatalf("BlockByNumber returned wrong block: want %d got %d", blockNumber, block.NumberU64()) @@ -412,7 +412,7 @@ func testGetBlock(t *testing.T, client *rpc.Client) { // Get current block by hash blockH, err := ec.BlockByHash(context.Background(), block.Hash()) if err != nil { - t.Fatalf("unexpected error: %v", err) + t.Fatalf("BlockByHash: %v", err) } if block.Hash() != blockH.Hash() { t.Fatalf("BlockByHash returned wrong block: want %v got %v", block.Hash().Hex(), blockH.Hash().Hex()) @@ -420,7 +420,7 @@ func testGetBlock(t *testing.T, client *rpc.Client) { // Get header by number header, err := ec.HeaderByNumber(context.Background(), new(big.Int).SetUint64(blockNumber)) if err != nil { - t.Fatalf("unexpected error: %v", err) + t.Fatalf("HeaderByNumber: %v", err) } if block.Header().Hash() != header.Hash() { t.Fatalf("HeaderByNumber returned wrong header: want %v got %v", block.Header().Hash().Hex(), header.Hash().Hex()) @@ -428,7 +428,7 @@ func testGetBlock(t *testing.T, client *rpc.Client) { // Get header by hash headerH, err := ec.HeaderByHash(context.Background(), block.Hash()) if err != nil { - t.Fatalf("unexpected error: %v", err) + t.Fatalf("HeaderByHash: %v", err) } if block.Header().Hash() != headerH.Hash() { t.Fatalf("HeaderByHash returned wrong header: want %v got %v", block.Header().Hash().Hex(), headerH.Hash().Hex()) From 4d019e1586950d5449edf11ce4cb2d20f650d78b Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Tue, 6 Jun 2023 11:56:03 +0200 Subject: [PATCH 4/9] Fix header comparison in test --- ethclient/ethclient_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index 79b80c40e2..601610e1a1 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -308,8 +308,11 @@ func testHeader(t *testing.T, chain []*types.Block, client *rpc.Client) { if got != nil && got.Number != nil && got.Number.Sign() == 0 { got.Number = big.NewInt(0) // hack to make DeepEqual work } + if got != nil && got.Difficulty != nil && got.Difficulty.Sign() == 0 { + got.Difficulty = big.NewInt(0) // hack to make DeepEqual work + } if !reflect.DeepEqual(got, tt.want) { - t.Fatalf("HeaderByNumber(%v)\n = %v\nwant %v", tt.block, got, tt.want) + t.Fatalf("HeaderByNumber(%v)\n = %+v\nwant %+v", tt.block, got, tt.want) } }) } From dbb30510760e0d6fee89e4b8ca52cbc697f8ebc7 Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Tue, 6 Jun 2023 16:02:25 +0200 Subject: [PATCH 5/9] Set constant header fields after GFork --- core/types/block.go | 3 ++- miner/block.go | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/core/types/block.go b/core/types/block.go index 9518b85fa3..616753fa96 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -36,9 +36,10 @@ import ( var ( EmptyRootHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") - emptyUncleHash = rlpHash([]*Header(nil)) // 1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347 + EmptyUncleHash = rlpHash([]*Header(nil)) // 1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347 EmptyRandomness = Randomness{} EmptyEpochSnarkData = EpochSnarkData{} + EmptyMixDigest = common.HexToHash("0000000000000000000000000000000000000000000000000000000000000000") ) // A BlockNonce is a 64-bit hash which proves (combined with the diff --git a/miner/block.go b/miner/block.go index cf01eeb44f..ae7c3e52aa 100644 --- a/miner/block.go +++ b/miner/block.go @@ -113,6 +113,10 @@ func prepareBlock(w *worker) (*blockState, error) { b.gasPool = new(core.GasPool).AddGas(b.gasLimit) if w.chainConfig.IsGFork(header.Number) { header.GasLimit = b.gasLimit + header.Difficulty = big.NewInt(0) + header.Nonce = types.EncodeNonce(0) + header.UncleHash = types.EmptyUncleHash + header.MixDigest = types.EmptyMixDigest } // Play our part in generating the random beacon. From 04776feb62a82502ab85c09f0a79cb7ffc01f405 Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Tue, 6 Jun 2023 16:25:20 +0200 Subject: [PATCH 6/9] Test presence of fields in RPC --- e2e_test/e2e_test.go | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index cfb227f5be..83dac1c83c 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -601,10 +601,10 @@ func TestEthersJSCompatibilityDisable(t *testing.T) { err = network[0].WsClient.GetRPCClient().CallContext(ctx, &result, "eth_getBlockByNumber", "latest", true) require.NoError(t, err) - _, ok := result["gasLimit"] - assert.True(t, ok, "gasLimit field should be present on RPC block") - _, ok = result["baseFeePerGas"] - assert.True(t, ok, "baseFeePerGas field should be present on RPC block") + for _, field := range []string{"gasLimit", "baseFeePerGas", "sha3Uncles", "uncles", "nonce", "mixHash", "difficulty"} { + _, ok := result[field] + assert.Truef(t, ok, "%s field should be present on RPC block after GFork", field) + } // Turn off compatibility and check fields are not present ec.RPCEthCompatibility = false @@ -621,7 +621,7 @@ func TestEthersJSCompatibilityDisable(t *testing.T) { // After GFork, gasLimit should be returned directly from the header, even if // RPCEthCompatibility is off, since it is now part of the header hash. - _, ok = result["gasLimit"] + _, ok := result["gasLimit"] assert.True(t, ok, "gasLimit field must be present on RPC block after GFork") _, ok = result["baseFeePerGas"] assert.False(t, ok, "baseFeePerGas field must be present on RPC block") @@ -648,10 +648,14 @@ func TestEthersJSCompatibilityDisableBeforeGFork(t *testing.T) { err = network[0].WsClient.GetRPCClient().CallContext(ctx, &result, "eth_getBlockByNumber", "latest", true) require.NoError(t, err) - _, ok := result["gasLimit"] - assert.True(t, ok, "gasLimit field should be present on RPC block") - _, ok = result["baseFeePerGas"] - assert.True(t, ok, "baseFeePerGas field should be present on RPC block") + for _, field := range []string{"gasLimit", "baseFeePerGas", "difficulty"} { + _, ok := result[field] + assert.Truef(t, ok, "%s field should be present on RPC block before GFork", field) + } + for _, field := range []string{"sha3Uncles", "uncles", "nonce", "mixHash"} { + _, ok := result[field] + assert.Falsef(t, ok, "%s field should not be present on RPC block before GFork", field) + } // Turn off compatibility and check fields are not present ec.RPCEthCompatibility = false @@ -666,8 +670,8 @@ func TestEthersJSCompatibilityDisableBeforeGFork(t *testing.T) { err = network[0].WsClient.GetRPCClient().CallContext(ctx, &result, "eth_getBlockByNumber", "latest", true) require.NoError(t, err) - _, ok = result["gasLimit"] - assert.False(t, ok, "gasLimit field should not be present on RPC block before GFork") - _, ok = result["baseFeePerGas"] - assert.False(t, ok, "baseFeePerGas field should not be present on RPC block") + for _, field := range []string{"gasLimit", "baseFeePerGas", "sha3Uncles", "uncles", "nonce", "mixHash", "difficulty"} { + _, ok := result[field] + assert.Falsef(t, ok, "%s field should not be present on RPC block before GFork", field) + } } From 545fabf79d350a76109b846a4bf270d5df9c1a7b Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Wed, 7 Jun 2023 11:01:11 +0200 Subject: [PATCH 7/9] Add nil check to test I've seen the following flaky error in CI: ``` panic: runtime error: invalid memory address or nil pointer dereference /opt/hostedtoolcache/go/1.17.5/x64/src/testing/testing.go:1209 +0x24e /opt/hostedtoolcache/go/1.17.5/x64/src/testing/testing.go:1212 +0x218 /opt/hostedtoolcache/go/1.17.5/x64/src/runtime/panic.go:1038 +0x215 /runner/_work/celo-blockchain/celo-blockchain/core/types/block.go:487 +0x30 /runner/_work/celo-blockchain/celo-blockchain/e2e_test/e2e_test.go:573 +0x56a /opt/hostedtoolcache/go/1.17.5/x64/src/testing/testing.go:1259 +0x102 /opt/hostedtoolcache/go/1.17.5/x64/src/testing/testing.go:1306 +0x35a ``` I don't think it is caused by this PR, adding a nil check to make the error case clearer can't hurt. --- e2e_test/e2e_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index 83dac1c83c..4be6c2ad08 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -568,6 +568,7 @@ func TestEthersJSCompatibility(t *testing.T) { err = network[0].Tracker.AwaitBlock(ctx, num+1) require.NoError(t, err) block := network[0].Tracker.GetProcessedBlock(num) + require.NotNil(t, block) // Prune state err = pruneStateOfBlock(ctx, network[0], block.Hash()) From 847592ce0cb2223b5fd22f186feac917bfa3fcb3 Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Wed, 7 Jun 2023 09:01:24 +0200 Subject: [PATCH 8/9] Update monorepo_commit Required change: * Use constant header fields in hash if present --- monorepo_commit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monorepo_commit b/monorepo_commit index 7d0ae641ec..bf06d94c8b 100644 --- a/monorepo_commit +++ b/monorepo_commit @@ -1 +1 @@ -d216e7599f499adec2e0200e599f601ea5424cde +a70a729f9785ad0130d8605493b008210cb22326 From 19616c89628b0650122da8201af8881a63b1aa09 Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Wed, 7 Jun 2023 15:48:33 +0200 Subject: [PATCH 9/9] Add getters for constant header fields to `Block` We don't use them anywhere, but bringing them back reduces the differences to upstream geth. --- core/types/block.go | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/core/types/block.go b/core/types/block.go index 616753fa96..674a492616 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -385,17 +385,23 @@ func (b *Block) Transaction(hash common.Hash) *Transaction { } func (b *Block) Number() *big.Int { return new(big.Int).Set(b.header.Number) } +func (b *Block) GasLimit() uint64 { return b.header.GasLimit } func (b *Block) GasUsed() uint64 { return b.header.GasUsed } +func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) } func (b *Block) Time() uint64 { return b.header.Time } func (b *Block) TotalDifficulty() *big.Int { return new(big.Int).Add(b.header.Number, big.NewInt(1)) } -func (b *Block) NumberU64() uint64 { return b.header.Number.Uint64() } -func (b *Block) Bloom() Bloom { return b.header.Bloom } -func (b *Block) Coinbase() common.Address { return b.header.Coinbase } -func (b *Block) Root() common.Hash { return b.header.Root } -func (b *Block) ParentHash() common.Hash { return b.header.ParentHash } -func (b *Block) TxHash() common.Hash { return b.header.TxHash } -func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash } -func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Extra) } + +func (b *Block) NumberU64() uint64 { return b.header.Number.Uint64() } +func (b *Block) MixDigest() common.Hash { return b.header.MixDigest } +func (b *Block) Nonce() uint64 { return binary.BigEndian.Uint64(b.header.Nonce[:]) } +func (b *Block) Bloom() Bloom { return b.header.Bloom } +func (b *Block) Coinbase() common.Address { return b.header.Coinbase } +func (b *Block) Root() common.Hash { return b.header.Root } +func (b *Block) ParentHash() common.Hash { return b.header.ParentHash } +func (b *Block) TxHash() common.Hash { return b.header.TxHash } +func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash } +func (b *Block) UncleHash() common.Hash { return b.header.UncleHash } +func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Extra) } func (b *Block) Header() *Header { return CopyHeader(b.header) } func (b *Block) MutableHeader() *Header { return b.header }