Avoid redundant Block.Header() deep-copy (#3050)

* Add separate Block.Nonce() and Block.NonceU64()

* Add Block.Seal()

* Avoid redundant Block.Header() deep-copy

* Add warning comment for Block.Header()
This commit is contained in:
TBC Dev 2021-11-29 16:52:36 +08:00 committed by GitHub
parent 8104a90993
commit 57d641b6f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 76 additions and 56 deletions

View File

@ -3679,8 +3679,8 @@ func scanReceipts(chaindata string, block uint64) error {
fix := true
if chainConfig.IsByzantium(blockNum) {
receiptSha := types.DeriveSha(receipts1)
if receiptSha != block.Header().ReceiptHash {
fmt.Printf("(retrace) mismatched receipt headers for block %d: %x, %x\n", block.NumberU64(), receiptSha, block.Header().ReceiptHash)
if receiptSha != block.ReceiptHash() {
fmt.Printf("(retrace) mismatched receipt headers for block %d: %x, %x\n", block.NumberU64(), receiptSha, block.ReceiptHash())
fix = false
}
}

View File

@ -303,9 +303,9 @@ func syncBySmallSteps(db kv.RwDB, miningConfig params.MiningConfig, ctx context.
panic(err)
}
if miner.MiningConfig.Enabled && nextBlock != nil && nextBlock.Header().Coinbase != (common.Address{}) {
miner.MiningConfig.Etherbase = nextBlock.Header().Coinbase
miner.MiningConfig.ExtraData = nextBlock.Header().Extra
if miner.MiningConfig.Enabled && nextBlock != nil && nextBlock.Coinbase() != (common.Address{}) {
miner.MiningConfig.Etherbase = nextBlock.Coinbase()
miner.MiningConfig.ExtraData = nextBlock.Extra()
miningStages.MockExecFunc(stages.MiningCreateBlock, func(firstCycle bool, badBlockUnwind bool, s *stagedsync.StageState, u stagedsync.Unwinder, tx kv.RwTx) error {
err = stagedsync.SpawnMiningCreateBlockStage(s, tx,
stagedsync.StageMiningCreateBlockCfg(db, miner, *chainConfig, engine, nil, nil, tmpDir),
@ -314,10 +314,10 @@ func syncBySmallSteps(db kv.RwDB, miningConfig params.MiningConfig, ctx context.
return err
}
miner.MiningBlock.Uncles = nextBlock.Uncles()
miner.MiningBlock.Header.Time = nextBlock.Header().Time
miner.MiningBlock.Header.GasLimit = nextBlock.Header().GasLimit
miner.MiningBlock.Header.Difficulty = nextBlock.Header().Difficulty
miner.MiningBlock.Header.Nonce = nextBlock.Header().Nonce
miner.MiningBlock.Header.Time = nextBlock.Time()
miner.MiningBlock.Header.GasLimit = nextBlock.GasLimit()
miner.MiningBlock.Header.Difficulty = nextBlock.Difficulty()
miner.MiningBlock.Header.Nonce = nextBlock.Nonce()
miner.MiningBlock.LocalTxs = types.NewTransactionsFixedOrder(nextBlock.Transactions())
miner.MiningBlock.RemoteTxs = types.NewTransactionsFixedOrder(nil)
//debugprint.Headers(miningWorld.Block.Header, nextBlock.Header())
@ -390,16 +390,15 @@ func checkChanges(expectedAccountChanges map[uint64]*changeset.ChangeSet, tx kv.
}
func checkMinedBlock(b1, b2 *types.Block, chainConfig *params.ChainConfig) {
h1 := b1.Header()
h2 := b2.Header()
if h1.Root != h2.Root ||
(chainConfig.IsByzantium(b1.NumberU64()) && h1.ReceiptHash != h2.ReceiptHash) ||
h1.TxHash != h2.TxHash ||
h1.ParentHash != h2.ParentHash ||
h1.UncleHash != h2.UncleHash ||
h1.GasUsed != h2.GasUsed ||
!bytes.Equal(h1.Extra, h2.Extra) {
debugprint.Headers(h1, h2)
if b1.Root() != b2.Root() ||
(chainConfig.IsByzantium(b1.NumberU64()) && b1.ReceiptHash() != b2.ReceiptHash()) ||
b1.TxHash() != b2.TxHash() ||
b1.ParentHash() != b2.ParentHash() ||
b1.UncleHash() != b2.UncleHash() ||
b1.GasUsed() != b2.GasUsed() ||
!bytes.Equal(b1.Extra(), b2.Extra()) { // TODO: Extra() doesn't need to be a copy for a read-only compare
// Header()'s deep-copy doesn't matter here since it will panic anyway
debugprint.Headers(b1.Header(), b2.Header())
panic("blocks are not same")
}
}

View File

@ -147,7 +147,7 @@ func CheckChangeSets(genesis *core.Genesis, logger log.Logger, blockNum uint64,
if writeReceipts {
if chainConfig.IsByzantium(block.Number().Uint64()) {
receiptSha := types.DeriveSha(receipts)
if receiptSha != block.Header().ReceiptHash {
if receiptSha != block.ReceiptHash() {
return fmt.Errorf("mismatched receipt headers for block %d", block.NumberU64())
}
}

View File

@ -557,7 +557,7 @@ func OpcodeTracer(genesis *core.Genesis, blockNum uint64, chaindata string, numB
}
if chainConfig.IsByzantium(block.Number().Uint64()) {
receiptSha := types.DeriveSha(receipts)
if receiptSha != block.Header().ReceiptHash {
if receiptSha != block.ReceiptHash() {
return fmt.Errorf("mismatched receipt headers for block %d", block.NumberU64())
}
}

View File

@ -293,20 +293,20 @@ func TestStaleSubmission(t *testing.T) {
}
select {
case res := <-results:
if res.Header().Nonce != fakeNonce {
t.Errorf("case %d block nonce mismatch, want %x, get %x", id+1, fakeNonce, res.Header().Nonce)
if res.Nonce() != fakeNonce {
t.Errorf("case %d block nonce mismatch, want %x, get %x", id+1, fakeNonce, res.Nonce())
}
if res.Header().MixDigest != fakeDigest {
t.Errorf("case %d block digest mismatch, want %x, get %x", id+1, fakeDigest, res.Header().MixDigest)
if res.MixDigest() != fakeDigest {
t.Errorf("case %d block digest mismatch, want %x, get %x", id+1, fakeDigest, res.MixDigest())
}
if res.Header().Difficulty.Uint64() != c.headers[c.submitIndex].Difficulty.Uint64() {
t.Errorf("case %d block difficulty mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Difficulty, res.Header().Difficulty)
if res.Difficulty().Uint64() != c.headers[c.submitIndex].Difficulty.Uint64() {
t.Errorf("case %d block difficulty mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Difficulty, res.Difficulty())
}
if res.Header().Number.Uint64() != c.headers[c.submitIndex].Number.Uint64() {
t.Errorf("case %d block number mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Number.Uint64(), res.Header().Number.Uint64())
if res.Number().Uint64() != c.headers[c.submitIndex].Number.Uint64() {
t.Errorf("case %d block number mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Number.Uint64(), res.Number().Uint64())
}
if res.Header().ParentHash != c.headers[c.submitIndex].ParentHash {
t.Errorf("case %d block parent hash mismatch, want %s, get %s", id+1, c.headers[c.submitIndex].ParentHash.Hex(), res.Header().ParentHash.Hex())
if res.ParentHash() != c.headers[c.submitIndex].ParentHash {
t.Errorf("case %d block parent hash mismatch, want %s, get %s", id+1, c.headers[c.submitIndex].ParentHash.Hex(), res.ParentHash().Hex())
}
case <-time.NewTimer(time.Second).C:
t.Errorf("case %d fetch ethash result timeout", id+1)

View File

@ -152,7 +152,7 @@ func ExecuteBlockEphemerally(
if chainConfig.IsByzantium(header.Number.Uint64()) && !vmConfig.NoReceipts {
receiptSha := types.DeriveSha(receipts)
if receiptSha != block.Header().ReceiptHash {
if receiptSha != block.ReceiptHash() {
return nil, fmt.Errorf("mismatched receipt headers for block %d", block.NumberU64())
}
}

View File

@ -185,12 +185,21 @@ func (b *BlockGen) PrevBlock(index int) *types.Block {
// tied to chain length directly.
func (b *BlockGen) OffsetTime(seconds int64) {
b.header.Time += uint64(seconds)
if b.header.Time <= b.parent.Header().Time {
parent := b.parent
if b.header.Time <= parent.Time() {
panic("block time out of range")
}
chainreader := &FakeChainReader{Cfg: b.config}
parent := b.parent.Header()
b.header.Difficulty = b.engine.CalcDifficulty(chainreader, b.header.Time, parent.Time, parent.Difficulty, parent.Number.Uint64(), parent.Hash(), parent.UncleHash, parent.Seal)
b.header.Difficulty = b.engine.CalcDifficulty(
chainreader,
b.header.Time,
parent.Time(),
parent.Difficulty(),
parent.NumberU64(),
parent.Hash(),
parent.UncleHash(),
parent.Seal(),
)
}
func (b *BlockGen) GetHeader() *types.Header {
@ -402,7 +411,7 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.I
parent.Number().Uint64(),
parent.Hash(),
parent.UncleHash(),
parent.Header().Seal,
parent.Seal(),
),
GasLimit: CalcGasLimit(parent.GasUsed(), parent.GasLimit(), parent.GasLimit(), parent.GasLimit()),
Number: new(big.Int).Add(parent.Number(), common.Big1),

View File

@ -154,7 +154,7 @@ func TestBlockStorage(t *testing.T) {
}
if entry := ReadHeader(tx, block.Hash(), block.NumberU64()); entry == nil {
t.Fatalf("Stored header not found")
} else if entry.Hash() != block.Header().Hash() {
} else if entry.Hash() != block.Hash() {
t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, block.Header())
}
if entry := ReadBodyWithTransactions(tx, block.Hash(), block.NumberU64()); entry == nil {
@ -186,8 +186,10 @@ func TestPartialBlockStorage(t *testing.T) {
TxHash: types.EmptyRootHash,
ReceiptHash: types.EmptyRootHash,
})
header := block.Header() // Not identical to struct literal above, due to other fields
// Store a header and check that it's not recognized as a block
WriteHeader(tx, block.Header())
WriteHeader(tx, header)
if entry := ReadBlock(tx, block.Hash(), block.NumberU64()); entry != nil {
t.Fatalf("Non existent block returned: %v", entry)
}
@ -203,7 +205,7 @@ func TestPartialBlockStorage(t *testing.T) {
DeleteBody(tx, block.Hash(), block.NumberU64())
// Store a header and a body separately and check reassembly
WriteHeader(tx, block.Header())
WriteHeader(tx, header)
if err := WriteBody(tx, block.Hash(), block.NumberU64(), block.Body()); err != nil {
t.Fatal(err)
}

View File

@ -629,6 +629,17 @@ func (h *Header) EmptyReceipts() bool {
return h.ReceiptHash == EmptyRootHash
}
func (h *Header) copySeal() []rlp.RawValue {
seal := h.Seal
if len(seal) > 0 {
seal = make([]rlp.RawValue, len(seal))
for i, s := range h.Seal {
seal[i] = common.CopyBytes(s)
}
}
return seal
}
// Body is a simple (mutable, non-safe) data container for storing and moving
// a block's data contents (transactions and uncles) together.
type Body struct {
@ -1016,12 +1027,7 @@ func CopyHeader(h *Header) *Header {
cpy.Extra = make([]byte, len(h.Extra))
copy(cpy.Extra, h.Extra)
}
if len(h.Seal) > 0 {
cpy.Seal = make([]rlp.RawValue, len(h.Seal))
for i := range h.Seal {
cpy.Seal[i] = common.CopyBytes(h.Seal[i])
}
}
cpy.Seal = h.copySeal()
return &cpy
}
@ -1203,7 +1209,8 @@ func (b *Block) Time() uint64 { return b.header.Time }
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) Nonce() BlockNonce { return b.header.Nonce }
func (b *Block) NonceU64() uint64 { return b.header.Nonce.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 }
@ -1218,7 +1225,9 @@ func (b *Block) BaseFee() *big.Int {
}
return new(big.Int).Set(b.header.BaseFee)
}
func (b *Block) Seal() (seal []rlp.RawValue) { return b.header.copySeal() }
// Header returns a deep-copy of the entire block header using CopyHeader()
func (b *Block) Header() *Header { return CopyHeader(b.header) }
// Body returns the non-header content of the block.

View File

@ -52,7 +52,7 @@ func TestBlockEncoding(t *testing.T) {
check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498"))
check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
check("Hash", block.Hash(), common.HexToHash("0a5843ac1cb04865017cb35a57b50b07084e5fcee39b5acadade33149f4fff9e"))
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
check("Nonce", block.NonceU64(), uint64(0xa13a5a8c8f2bb1c4))
check("Time", block.Time(), uint64(1426516743))
check("Size", block.Size(), common.StorageSize(len(blockEnc)))
@ -89,7 +89,7 @@ func TestEIP1559BlockEncoding(t *testing.T) {
check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498"))
check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
check("Hash", block.Hash(), common.HexToHash("c7252048cd273fe0dac09650027d07f0e3da4ee0675ebbb26627cea92729c372"))
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
check("Nonce", block.NonceU64(), uint64(0xa13a5a8c8f2bb1c4))
check("Time", block.Time(), uint64(1426516743))
check("Size", block.Size(), common.StorageSize(len(blockEnc)))
check("BaseFee", block.BaseFee(), new(big.Int).SetUint64(params.InitialBaseFee))
@ -154,7 +154,7 @@ func TestEIP2718BlockEncoding(t *testing.T) {
check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1"))
check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498"))
check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017"))
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4))
check("Nonce", block.NonceU64(), uint64(0xa13a5a8c8f2bb1c4))
check("Time", block.Time(), uint64(1426516743))
check("Size", block.Size(), common.StorageSize(len(blockEnc)))

View File

@ -55,7 +55,7 @@ func SpawnMiningFinishStage(s *StageState, tx kv.RwTx, cfg MiningFinishCfg, quit
//prev = sealHash
// Tests may set pre-calculated nonce
if block.Header().Nonce.Uint64() != 0 {
if block.NonceU64() != 0 {
cfg.miningState.MiningResultCh <- block
return nil
}

View File

@ -123,8 +123,8 @@ var ReceiptRepair = Migration{
fix := true
if chainConfig.IsByzantium(block.Number().Uint64()) {
receiptSha := types.DeriveSha(receipts1)
if receiptSha != block.Header().ReceiptHash {
fmt.Printf("(retrace) mismatched receipt headers for block %d: %x, %x\n", block.NumberU64(), receiptSha, block.Header().ReceiptHash)
if receiptSha != block.ReceiptHash() {
fmt.Printf("(retrace) mismatched receipt headers for block %d: %x, %x\n", block.NumberU64(), receiptSha, block.ReceiptHash())
fix = false
}
}

View File

@ -307,7 +307,7 @@ func (t *BlockTest) validateImportedHeaders(tx kv.Tx, validBlocks []btBlock) err
if err := validateHeader(bmap[b.Hash()].BlockHeader, b.Header()); err != nil {
return fmt.Errorf("imported block header validation failed: %w", err)
}
b, _ = rawdb.ReadBlockByHash(tx, b.Header().ParentHash)
b, _ = rawdb.ReadBlockByHash(tx, b.ParentHash())
}
return nil
}

View File

@ -1024,11 +1024,12 @@ func (hd *HeaderDownload) Fetching() bool {
}
func (hd *HeaderDownload) AddMinedBlock(block *types.Block) error {
header := block.Header()
buf := bytes.NewBuffer(nil)
if err := block.Header().EncodeRLP(buf); err != nil {
if err := header.EncodeRLP(buf); err != nil {
return err
}
segments, _, err := hd.SingleHeaderAsSegment(buf.Bytes(), block.Header())
segments, _, err := hd.SingleHeaderAsSegment(buf.Bytes(), header)
if err != nil {
return err
}

View File

@ -54,7 +54,7 @@ func ComputeTxEnv(ctx context.Context, block *types.Block, cfg *params.ChainConf
statedb.Prepare(tx.Hash(), blockHash, idx)
// Assemble the transaction call message and return if the requested offset
msg, _ := tx.AsMessage(*signer, block.Header().BaseFee)
msg, _ := tx.AsMessage(*signer, block.BaseFee())
TxContext := core.NewEVMTxContext(msg)
if idx == int(txIndex) {
return msg, BlockContext, TxContext, statedb, reader, nil