diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 14b7a824ef..330ee5f295 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1095,7 +1095,7 @@ func (o *BlockOverrides) Apply(blockCtx *vm.BlockContext) { // MakeHeader returns a new header object with the overridden // fields. -// Note: MakeHeader ignores blobGasPrice if set. That's because +// Note: MakeHeader ignores BlobBaseFee if set. That's because // header has no such field. func (o *BlockOverrides) MakeHeader(header *types.Header) *types.Header { if o == nil { diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 509989999b..268cc246bb 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -42,6 +42,9 @@ const ( // maxSimulateBlocks is the maximum number of blocks that can be simulated // in a single request. maxSimulateBlocks = 256 + + // timestampIncrement is the default increment between block timestamps. + timestampIncrement = 1 ) // simBlock is a batch of calls to be simulated sequentially. @@ -106,7 +109,7 @@ func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]map[str defer cancel() var err error - blocks, err = sim.sanitizeBlockOrder(blocks) + blocks, err = sim.sanitizeChain(blocks) if err != nil { return nil, err } @@ -287,14 +290,16 @@ func (sim *simulator) activePrecompiles(ctx context.Context, base *types.Header) return vm.ActivePrecompiledContracts(rules).Copy() } -// sanitizeBlockOrder iterates the blocks checking that block numbers -// are strictly increasing. When necessary it will generate empty blocks. -// It modifies the block's override object. -func (sim *simulator) sanitizeBlockOrder(blocks []simBlock) ([]simBlock, error) { +// sanitizeChain checks the chain integrity. Specifically it checks that +// block numbers and timestamp are strictly increasing, setting default values +// when necessary. Gaps in block numbers are filled with empty blocks. +// Note: It modifies the block's override object. +func (sim *simulator) sanitizeChain(blocks []simBlock) ([]simBlock, error) { var ( - res = make([]simBlock, 0, len(blocks)) - base = sim.base - prevNumber = base.Number + res = make([]simBlock, 0, len(blocks)) + base = sim.base + prevNumber = base.Number + prevTimestamp = base.Time ) for _, block := range blocks { if block.BlockOverrides == nil { @@ -317,12 +322,25 @@ func (sim *simulator) sanitizeBlockOrder(blocks []simBlock) ([]simBlock, error) // Assign block number to the empty blocks. for i := uint64(0); i < gap.Uint64(); i++ { n := new(big.Int).Add(prevNumber, big.NewInt(int64(i+1))) - b := simBlock{BlockOverrides: &BlockOverrides{Number: (*hexutil.Big)(n)}} + t := prevTimestamp + timestampIncrement + b := simBlock{BlockOverrides: &BlockOverrides{Number: (*hexutil.Big)(n), Time: (*hexutil.Uint64)(&t)}} + prevTimestamp = t res = append(res, b) } } // Only append block after filling a potential gap. prevNumber = block.BlockOverrides.Number.ToInt() + var t uint64 + if block.BlockOverrides.Time == nil { + t = prevTimestamp + timestampIncrement + block.BlockOverrides.Time = (*hexutil.Uint64)(&t) + } else { + t = uint64(*block.BlockOverrides.Time) + if t <= prevTimestamp { + return nil, &invalidBlockTimestampError{fmt.Sprintf("block timestamps must be in order: %d <= %d", t, prevTimestamp)} + } + } + prevTimestamp = t res = append(res, block) } return res, nil @@ -333,23 +351,15 @@ func (sim *simulator) sanitizeBlockOrder(blocks []simBlock) ([]simBlock, error) // It assumes blocks are in order and numbers have been validated. func (sim *simulator) makeHeaders(blocks []simBlock) ([]*types.Header, error) { var ( - res = make([]*types.Header, len(blocks)) - base = sim.base - prevTimestamp = base.Time - header = base + res = make([]*types.Header, len(blocks)) + base = sim.base + header = base ) for bi, block := range blocks { if block.BlockOverrides == nil || block.BlockOverrides.Number == nil { return nil, errors.New("empty block number") } overrides := block.BlockOverrides - if overrides.Time == nil { - t := prevTimestamp + 1 - overrides.Time = (*hexutil.Uint64)(&t) - } else if time := (*uint64)(overrides.Time); *time <= prevTimestamp { - return nil, &invalidBlockTimestampError{fmt.Sprintf("block timestamps must be in order: %d <= %d", *time, prevTimestamp)} - } - prevTimestamp = uint64(*overrides.Time) var withdrawalsHash *common.Hash if sim.chainConfig.IsShanghai(overrides.Number.ToInt(), (uint64)(*overrides.Time)) { diff --git a/internal/ethapi/simulate_test.go b/internal/ethapi/simulate_test.go index 34cbb3ba38..d80380a8e0 100644 --- a/internal/ethapi/simulate_test.go +++ b/internal/ethapi/simulate_test.go @@ -1,7 +1,6 @@ package ethapi import ( - "fmt" "math/big" "testing" @@ -10,35 +9,62 @@ import ( ) func TestSimulateSanitizeBlockOrder(t *testing.T) { + type result struct { + number uint64 + timestamp uint64 + } for i, tc := range []struct { - baseNumber int - blocks []simBlock - expectedLen int - err string + baseNumber int + baseTimestamp uint64 + blocks []simBlock + expected []result + err string }{ { - baseNumber: 10, - blocks: []simBlock{{}, {}, {}}, - expectedLen: 3, + baseNumber: 10, + baseTimestamp: 50, + blocks: []simBlock{{}, {}, {}}, + expected: []result{{number: 11, timestamp: 51}, {number: 12, timestamp: 52}, {number: 13, timestamp: 53}}, }, { - baseNumber: 10, - blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(13)}}, {}}, - expectedLen: 4, + baseNumber: 10, + baseTimestamp: 50, + blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(13), Time: newUint64(70)}}, {}}, + expected: []result{{number: 11, timestamp: 51}, {number: 12, timestamp: 52}, {number: 13, timestamp: 70}, {number: 14, timestamp: 71}}, }, { - baseNumber: 10, - blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(11)}}, {BlockOverrides: &BlockOverrides{Number: newInt(14)}}, {}}, - expectedLen: 5, + baseNumber: 10, + baseTimestamp: 50, + blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(11)}}, {BlockOverrides: &BlockOverrides{Number: newInt(14)}}, {}}, + expected: []result{{number: 11, timestamp: 51}, {number: 12, timestamp: 52}, {number: 13, timestamp: 53}, {number: 14, timestamp: 54}, {number: 15, timestamp: 55}}, }, { - baseNumber: 10, - blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(13)}}, {BlockOverrides: &BlockOverrides{Number: newInt(12)}}}, - err: "block numbers must be in order: 12 <= 13", + baseNumber: 10, + baseTimestamp: 50, + blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(13)}}, {BlockOverrides: &BlockOverrides{Number: newInt(12)}}}, + err: "block numbers must be in order: 12 <= 13", + }, + { + baseNumber: 10, + baseTimestamp: 50, + blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(13), Time: newUint64(52)}}}, + err: "block timestamps must be in order: 52 <= 52", + }, + { + baseNumber: 10, + baseTimestamp: 50, + blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(11), Time: newUint64(60)}}, {BlockOverrides: &BlockOverrides{Number: newInt(12), Time: newUint64(55)}}}, + err: "block timestamps must be in order: 55 <= 60", + }, + { + baseNumber: 10, + baseTimestamp: 50, + blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(11), Time: newUint64(60)}}, {BlockOverrides: &BlockOverrides{Number: newInt(13), Time: newUint64(61)}}}, + err: "block timestamps must be in order: 61 <= 61", }, } { - sim := &simulator{base: &types.Header{Number: big.NewInt(int64(tc.baseNumber))}} - res, err := sim.sanitizeBlockOrder(tc.blocks) + sim := &simulator{base: &types.Header{Number: big.NewInt(int64(tc.baseNumber)), Time: tc.baseTimestamp}} + res, err := sim.sanitizeChain(tc.blocks) if err != nil { if err.Error() == tc.err { continue @@ -49,9 +75,8 @@ func TestSimulateSanitizeBlockOrder(t *testing.T) { if err == nil && tc.err != "" { t.Fatalf("testcase %d: expected err", i) } - if len(res) != tc.expectedLen { - fmt.Printf("res: %v\n", res) - t.Errorf("testcase %d: mismatch number of blocks. Want %d, have %d", i, tc.expectedLen, len(res)) + if len(res) != len(tc.expected) { + t.Errorf("testcase %d: mismatch number of blocks. Want %d, have %d", i, len(tc.expected), len(res)) } for bi, b := range res { if b.BlockOverrides == nil { @@ -60,10 +85,15 @@ func TestSimulateSanitizeBlockOrder(t *testing.T) { if b.BlockOverrides.Number == nil { t.Fatalf("testcase %d: block number not set", i) } - want := tc.baseNumber + bi + 1 + if b.BlockOverrides.Time == nil { + t.Fatalf("testcase %d: block time not set", i) + } + if (uint64)(*b.BlockOverrides.Time) != tc.expected[bi].timestamp { + t.Errorf("testcase %d: block timestamp mismatch. Want %d, have %d", i, tc.expected[bi].timestamp, (uint64)(*b.BlockOverrides.Time)) + } have := b.BlockOverrides.Number.ToInt().Uint64() - if uint64(want) != have { - t.Errorf("testcase %d: block number mismatch. Want %d, have %d", i, want, have) + if have != tc.expected[bi].number { + t.Errorf("testcase %d: block number mismatch. Want %d, have %d", i, tc.expected[bi].number, have) } } }