diff --git a/core/types/block.go b/core/types/block.go index 1b5c7fc8f9..4452f4bb21 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -170,23 +170,7 @@ type Body struct { Withdrawals []*Withdrawal `rlp:"optional"` } -// Block represents an Ethereum block. -// -// Note the Block type tries to be 'immutable', and contains certain caches that rely -// on that. The rules around block immutability are as follows: -// -// - We copy all data when the block is constructed. This makes references held inside -// the block independent of whatever value was passed in. -// -// - We copy all header data on access. This is because any change to the header would mess -// up the cached hash and size values in the block. Calling code is expected to take -// advantage of this to avoid over-allocating! -// -// - When new body data is attached to the block, a shallow copy of the block is returned. -// This ensures block modifications are race-free. -// -// - We do not copy body data on access because it does not affect the caches, and also -// because it would be too expensive. +// Block represents an entire block in the Ethereum blockchain. type Block struct { header *Header uncles []*Header @@ -211,8 +195,9 @@ type extblock struct { Withdrawals []*Withdrawal `rlp:"optional"` } -// NewBlock creates a new block. The input data is copied, changes to header and to the -// field values will not affect the block. +// NewBlock creates a new block. The input data is copied, +// changes to header and to the field values will not affect the +// block. // // The values of TxHash, UncleHash, ReceiptHash and Bloom in header // are ignored and set to values derived from the given txs, uncles @@ -249,11 +234,13 @@ func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []* return b } -// NewBlockWithWithdrawals creates a new block with withdrawals. The input data is copied, -// changes to header and to the field values will not affect the block. +// NewBlockWithWithdrawals creates a new block with withdrawals. The input data +// is copied, changes to header and to the field values will not +// affect the block. // -// The values of TxHash, UncleHash, ReceiptHash and Bloom in header are ignored and set to -// values derived from the given txs, uncles and receipts. +// The values of TxHash, UncleHash, ReceiptHash and Bloom in header +// are ignored and set to values derived from the given txs, uncles +// and receipts. func NewBlockWithWithdrawals(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, withdrawals []*Withdrawal, hasher TrieHasher) *Block { b := NewBlock(header, txs, uncles, receipts, hasher) @@ -269,7 +256,15 @@ func NewBlockWithWithdrawals(header *Header, txs []*Transaction, uncles []*Heade return b.WithWithdrawals(withdrawals) } -// CopyHeader creates a deep copy of a block header. +// NewBlockWithHeader creates a block with the given header data. The +// header data is copied, changes to header and to the field values +// will not affect the block. +func NewBlockWithHeader(header *Header) *Block { + return &Block{header: CopyHeader(header)} +} + +// CopyHeader creates a deep copy of a block header to prevent side effects from +// modifying a header variable. func CopyHeader(h *Header) *Header { cpy := *h if cpy.Difficulty = new(big.Int); h.Difficulty != nil { @@ -300,7 +295,7 @@ func CopyHeader(h *Header) *Header { return &cpy } -// DecodeRLP decodes a block from RLP. +// DecodeRLP decodes the Ethereum func (b *Block) DecodeRLP(s *rlp.Stream) error { var eb extblock _, size, _ := s.Kind() @@ -312,9 +307,9 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error { return nil } -// EncodeRLP serializes a block as RLP. +// EncodeRLP serializes b into the Ethereum RLP block format. func (b *Block) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, &extblock{ + return rlp.Encode(w, extblock{ Header: b.header, Txs: b.transactions, Uncles: b.uncles, @@ -322,18 +317,10 @@ func (b *Block) EncodeRLP(w io.Writer) error { }) } -// Body returns the non-header content of the block. -// Note the returned data is not an independent copy. -func (b *Block) Body() *Body { - return &Body{b.transactions, b.uncles, b.withdrawals} -} - -// Accessors for body data. These do not return a copy because the content -// of the body slices does not affect the cached hash/size in block. +// TODO: copies func (b *Block) Uncles() []*Header { return b.uncles } func (b *Block) Transactions() Transactions { return b.transactions } -func (b *Block) Withdrawals() Withdrawals { return b.withdrawals } func (b *Block) Transaction(hash common.Hash) *Transaction { for _, transaction := range b.transactions { @@ -344,13 +331,6 @@ func (b *Block) Transaction(hash common.Hash) *Transaction { return nil } -// Header returns the block header (as a copy). -func (b *Block) Header() *Header { - return CopyHeader(b.header) -} - -// Header value accessors. These do copy! - 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 } @@ -376,6 +356,10 @@ func (b *Block) BaseFee() *big.Int { return new(big.Int).Set(b.header.BaseFee) } +func (b *Block) Withdrawals() Withdrawals { + return b.withdrawals +} + func (b *Block) ExcessBlobGas() *uint64 { var excessBlobGas *uint64 if b.header.ExcessBlobGas != nil { @@ -394,6 +378,11 @@ func (b *Block) BlobGasUsed() *uint64 { return blobGasUsed } +func (b *Block) Header() *Header { return CopyHeader(b.header) } + +// Body returns the non-header content of the block. +func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles, b.withdrawals} } + // Size returns the true RLP encoded storage size of the block, either by encoding // and returning it, or returning a previously cached value. func (b *Block) Size() uint64 { @@ -426,31 +415,25 @@ func CalcUncleHash(uncles []*Header) common.Hash { return rlpHash(uncles) } -// NewBlockWithHeader creates a block with the given header data. The -// header data is copied, changes to header and to the field values -// will not affect the block. -func NewBlockWithHeader(header *Header) *Block { - return &Block{header: CopyHeader(header)} -} - // WithSeal returns a new block with the data from b but the header replaced with // the sealed one. func (b *Block) WithSeal(header *Header) *Block { + cpy := *header + return &Block{ - header: CopyHeader(header), + header: &cpy, transactions: b.transactions, uncles: b.uncles, withdrawals: b.withdrawals, } } -// WithBody returns a copy of the block with the given transaction and uncle contents. +// WithBody returns a new block with the given transaction and uncle contents. func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block { block := &Block{ - header: b.header, + header: CopyHeader(b.header), transactions: make([]*Transaction, len(transactions)), uncles: make([]*Header, len(uncles)), - withdrawals: b.withdrawals, } copy(block.transactions, transactions) for i := range uncles { @@ -459,18 +442,13 @@ func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block { return block } -// WithWithdrawals returns a copy of the block containing the given withdrawals. +// WithWithdrawals sets the withdrawal contents of a block, does not return a new block. func (b *Block) WithWithdrawals(withdrawals []*Withdrawal) *Block { - block := &Block{ - header: b.header, - transactions: b.transactions, - uncles: b.uncles, - } if withdrawals != nil { - block.withdrawals = make([]*Withdrawal, len(withdrawals)) - copy(block.withdrawals, withdrawals) + b.withdrawals = make([]*Withdrawal, len(withdrawals)) + copy(b.withdrawals, withdrawals) } - return block + return b } // Hash returns the keccak256 hash of b's header.