diff --git a/core/types/block.go b/core/types/block.go index 2bce2bb77bf..05c74b6329a 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -1095,25 +1095,38 @@ func NewBlockFromNetwork(header *Header, body *Body) *Block { // 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 //nolint + cpy := Header{} // note: do not copy hash atomic.Pointer + cpy.ParentHash = h.ParentHash + cpy.UncleHash = h.UncleHash + cpy.Coinbase = h.Coinbase + cpy.Root = h.Root + cpy.TxHash = h.TxHash + cpy.ReceiptHash = h.ReceiptHash + cpy.Bloom = h.Bloom if cpy.Difficulty = new(big.Int); h.Difficulty != nil { cpy.Difficulty.Set(h.Difficulty) } if cpy.Number = new(big.Int); h.Number != nil { cpy.Number.Set(h.Number) } - if h.BaseFee != nil { - cpy.BaseFee = new(big.Int) - cpy.BaseFee.Set(h.BaseFee) - } - if len(h.Extra) > 0 { + cpy.GasLimit = h.GasLimit + cpy.GasUsed = h.GasUsed + cpy.Time = h.Time + if h.Extra != nil { cpy.Extra = make([]byte, len(h.Extra)) copy(cpy.Extra, h.Extra) } - if len(h.AuRaSeal) > 0 { + cpy.MixDigest = h.MixDigest + cpy.Nonce = h.Nonce + cpy.AuRaStep = h.AuRaStep + if h.AuRaSeal != nil { cpy.AuRaSeal = make([]byte, len(h.AuRaSeal)) copy(cpy.AuRaSeal, h.AuRaSeal) } + if h.BaseFee != nil { + cpy.BaseFee = new(big.Int) + cpy.BaseFee.Set(h.BaseFee) + } if h.WithdrawalsHash != nil { cpy.WithdrawalsHash = new(libcommon.Hash) cpy.WithdrawalsHash.SetBytes(h.WithdrawalsHash.Bytes()) @@ -1134,6 +1147,15 @@ func CopyHeader(h *Header) *Header { cpy.RequestsHash = new(libcommon.Hash) cpy.RequestsHash.SetBytes(h.RequestsHash.Bytes()) } + cpy.Verkle = h.Verkle + if h.VerkleProof != nil { + cpy.VerkleProof = make([]byte, len(h.VerkleProof)) + copy(cpy.VerkleProof, h.VerkleProof) + } + if h.VerkleKeyVals != nil { + cpy.VerkleKeyVals = make([]verkle.KeyValuePair, len(h.VerkleKeyVals)) + copy(cpy.VerkleKeyVals, h.VerkleKeyVals) + } cpy.mutable = h.mutable if hash := h.hash.Load(); hash != nil { hashCopy := *hash diff --git a/core/types/block_test.go b/core/types/block_test.go index 996e6716b3d..d10522b8a12 100644 --- a/core/types/block_test.go +++ b/core/types/block_test.go @@ -606,3 +606,15 @@ func TestCopyTxs(t *testing.T) { copies := CopyTxs(txs) assert.Equal(t, txs, copies) } + +func TestCopyHeader(t *testing.T) { + // if this test fails when adding a new attribute to the Header struct + // please update the copy function to include logic for the new attribute + const runCount = 1000 + tr := NewTRand() + for range make([]byte, runCount) { + h1 := tr.RandHeaderReflectAllFields() + h2 := CopyHeader(h1) + require.Equal(t, h1, h2) + } +} diff --git a/core/types/encdec_test.go b/core/types/encdec_test.go index 84630ca2ed4..6d4933fb8f4 100644 --- a/core/types/encdec_test.go +++ b/core/types/encdec_test.go @@ -25,6 +25,7 @@ import ( "testing" "time" + "github.com/gballet/go-verkle" "github.com/holiman/uint256" libcommon "github.com/erigontech/erigon-lib/common" @@ -77,10 +78,29 @@ func (tr *TRand) RandHash() libcommon.Hash { return libcommon.Hash(tr.RandBytes(32)) } +func (tr *TRand) RandBoolean() bool { + return tr.rnd.Intn(2) == 0 +} + func (tr *TRand) RandBloom() Bloom { return Bloom(tr.RandBytes(BloomByteLength)) } +func (tr *TRand) RandVerkleKeyValuePairs(count int) []verkle.KeyValuePair { + res := make([]verkle.KeyValuePair, count) + for i := 0; i < count; i++ { + res[i] = tr.RandVerkleKeyValuePair() + } + return res +} + +func (tr *TRand) RandVerkleKeyValuePair() verkle.KeyValuePair { + return verkle.KeyValuePair{ + Key: tr.RandBytes(tr.RandIntInRange(1, 32)), + Value: tr.RandBytes(tr.RandIntInRange(1, 32)), + } +} + func (tr *TRand) RandWithdrawal() *Withdrawal { return &Withdrawal{ Index: tr.rnd.Uint64(), @@ -117,6 +137,60 @@ func (tr *TRand) RandHeader() *Header { } } +func (tr *TRand) RandHeaderReflectAllFields(skipFields ...string) *Header { + skipSet := make(map[string]struct{}, len(skipFields)) + for _, field := range skipFields { + skipSet[field] = struct{}{} + } + + emptyUint64 := uint64(0) + h := &Header{} + // note unexported fields are skipped in reflection auto-assign as they are not assignable + h.mutable = tr.RandBoolean() + headerValue := reflect.ValueOf(h) + headerElem := headerValue.Elem() + numField := headerElem.Type().NumField() + for i := 0; i < numField; i++ { + field := headerElem.Field(i) + if !field.CanSet() { + continue + } + + if _, skip := skipSet[headerElem.Type().Field(i).Name]; skip { + continue + } + + switch field.Type() { + case reflect.TypeOf(libcommon.Hash{}): + field.Set(reflect.ValueOf(tr.RandHash())) + case reflect.TypeOf(&libcommon.Hash{}): + randHash := tr.RandHash() + field.Set(reflect.ValueOf(&randHash)) + case reflect.TypeOf(libcommon.Address{}): + field.Set(reflect.ValueOf(tr.RandAddress())) + case reflect.TypeOf(Bloom{}): + field.Set(reflect.ValueOf(tr.RandBloom())) + case reflect.TypeOf(BlockNonce{}): + field.Set(reflect.ValueOf(BlockNonce(tr.RandBytes(8)))) + case reflect.TypeOf(&big.Int{}): + field.Set(reflect.ValueOf(tr.RandBig())) + case reflect.TypeOf(uint64(0)): + field.Set(reflect.ValueOf(*tr.RandUint64())) + case reflect.TypeOf(&emptyUint64): + field.Set(reflect.ValueOf(tr.RandUint64())) + case reflect.TypeOf([]byte{}): + field.Set(reflect.ValueOf(tr.RandBytes(tr.RandIntInRange(128, 1024)))) + case reflect.TypeOf(false): + field.Set(reflect.ValueOf(tr.RandBoolean())) + case reflect.TypeOf([]verkle.KeyValuePair{}): + field.Set(reflect.ValueOf(tr.RandVerkleKeyValuePairs(tr.RandIntInRange(1, 3)))) + default: + panic(fmt.Sprintf("don't know how to generate rand value for Header field type %v - please add handler", field.Type())) + } + } + return h +} + func (tr *TRand) RandAccessTuple() AccessTuple { n := tr.RandIntInRange(1, 5) sk := make([]libcommon.Hash, n)