From 48ca188157809a32bc213d598b8cfc91eba6aa0a Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 7 May 2024 20:02:22 +0200 Subject: [PATCH] generate empty blocks for gaps --- internal/ethapi/simulate.go | 73 ++++++++++++++++++++++++------- internal/ethapi/simulate_test.go | 74 ++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 16 deletions(-) create mode 100644 internal/ethapi/simulate_test.go diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index 44d2d05314..60680998ef 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -34,7 +34,6 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" @@ -147,7 +146,13 @@ func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]simBloc // Make sure the context is cancelled when the call has completed // this makes sure resources are cleaned up. defer cancel() - headers, err := makeHeaders(sim.b.ChainConfig(), blocks, sim.base) + + var err error + blocks, err = sim.sanitizeBlockOrder(blocks) + if err != nil { + return nil, err + } + headers, err := sim.makeHeaders(blocks) if err != nil { return nil, err } @@ -392,27 +397,63 @@ func (sim *simulator) activePrecompiles(ctx context.Context, base *types.Header) return vm.ActivePrecompiledContracts(rules).Copy() } -func makeHeaders(config *params.ChainConfig, blocks []simBlock, base *types.Header) ([]*types.Header, error) { +// 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) { + var ( + res = make([]simBlock, 0, len(blocks)) + base = sim.base + prevNumber = base.Number + ) + for _, block := range blocks { + if block.BlockOverrides == nil { + block.BlockOverrides = new(BlockOverrides) + } + if block.BlockOverrides.Number == nil { + n := new(big.Int).Add(prevNumber, big.NewInt(1)) + block.BlockOverrides.Number = (*hexutil.Big)(n) + } + diff := new(big.Int).Sub(block.BlockOverrides.Number.ToInt(), prevNumber) + if diff.Cmp(common.Big0) <= 0 { + return nil, &invalidBlockNumberError{fmt.Sprintf("block numbers must be in order: %d <= %d", block.BlockOverrides.Number.ToInt().Uint64(), prevNumber)} + } + if total := new(big.Int).Sub(block.BlockOverrides.Number.ToInt(), base.Number); total.Cmp(big.NewInt(maxSimulateBlocks)) > 0 { + return nil, &clientLimitExceededError{message: "too many blocks"} + } + if diff.Cmp(big.NewInt(1)) > 0 { + // Fill the gap with empty blocks. + gap := new(big.Int).Sub(diff, big.NewInt(1)) + // 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)}} + res = append(res, b) + } + } + // Only append block after filling a potential gap. + prevNumber = block.BlockOverrides.Number.ToInt() + res = append(res, block) + } + return res, nil +} + +// makeHeaders makes header object with preliminary fields based on a simulated block. +// Some fields have to be filled post-execution. +// It assumes blocks are in order and numbers have been validated. +func (sim *simulator) makeHeaders(blocks []simBlock) ([]*types.Header, error) { res := make([]*types.Header, len(blocks)) var ( - prevNumber = base.Number.Uint64() + config = sim.b.ChainConfig() + base = sim.base prevTimestamp = base.Time header = base ) for bi, block := range blocks { - overrides := new(BlockOverrides) - if block.BlockOverrides != nil { - overrides = block.BlockOverrides + if block.BlockOverrides == nil || block.BlockOverrides.Number == nil { + return nil, errors.New("empty block number") } - // Sanitize block number and timestamp - if overrides.Number == nil { - n := new(big.Int).Add(big.NewInt(int64(prevNumber)), big.NewInt(1)) - overrides.Number = (*hexutil.Big)(n) - } else if overrides.Number.ToInt().Uint64() <= prevNumber { - return nil, &invalidBlockNumberError{fmt.Sprintf("block numbers must be in order: %d <= %d", overrides.Number.ToInt().Uint64(), prevNumber)} - } - prevNumber = overrides.Number.ToInt().Uint64() - + overrides := block.BlockOverrides if overrides.Time == nil { t := prevTimestamp + 12 overrides.Time = (*hexutil.Uint64)(&t) diff --git a/internal/ethapi/simulate_test.go b/internal/ethapi/simulate_test.go new file mode 100644 index 0000000000..34cbb3ba38 --- /dev/null +++ b/internal/ethapi/simulate_test.go @@ -0,0 +1,74 @@ +package ethapi + +import ( + "fmt" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" +) + +func TestSimulateSanitizeBlockOrder(t *testing.T) { + for i, tc := range []struct { + baseNumber int + blocks []simBlock + expectedLen int + err string + }{ + { + baseNumber: 10, + blocks: []simBlock{{}, {}, {}}, + expectedLen: 3, + }, + { + baseNumber: 10, + blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(13)}}, {}}, + expectedLen: 4, + }, + { + baseNumber: 10, + blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(11)}}, {BlockOverrides: &BlockOverrides{Number: newInt(14)}}, {}}, + expectedLen: 5, + }, + { + baseNumber: 10, + blocks: []simBlock{{BlockOverrides: &BlockOverrides{Number: newInt(13)}}, {BlockOverrides: &BlockOverrides{Number: newInt(12)}}}, + err: "block numbers must be in order: 12 <= 13", + }, + } { + sim := &simulator{base: &types.Header{Number: big.NewInt(int64(tc.baseNumber))}} + res, err := sim.sanitizeBlockOrder(tc.blocks) + if err != nil { + if err.Error() == tc.err { + continue + } else { + t.Fatalf("testcase %d: error mismatch. Want '%s', have '%s'", i, tc.err, err.Error()) + } + } + 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)) + } + for bi, b := range res { + if b.BlockOverrides == nil { + t.Fatalf("testcase %d: block overrides nil", i) + } + if b.BlockOverrides.Number == nil { + t.Fatalf("testcase %d: block number not set", i) + } + want := tc.baseNumber + bi + 1 + have := b.BlockOverrides.Number.ToInt().Uint64() + if uint64(want) != have { + t.Errorf("testcase %d: block number mismatch. Want %d, have %d", i, want, have) + } + } + } +} + +func newInt(n int64) *hexutil.Big { + return (*hexutil.Big)(big.NewInt(n)) +}