generate empty blocks for gaps

This commit is contained in:
Sina Mahmoodi 2024-05-07 20:02:22 +02:00
parent 8698221fae
commit 48ca188157
2 changed files with 131 additions and 16 deletions

View file

@ -34,7 +34,6 @@ import (
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie" "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 // Make sure the context is cancelled when the call has completed
// this makes sure resources are cleaned up. // this makes sure resources are cleaned up.
defer cancel() 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 { if err != nil {
return nil, err return nil, err
} }
@ -392,27 +397,63 @@ func (sim *simulator) activePrecompiles(ctx context.Context, base *types.Header)
return vm.ActivePrecompiledContracts(rules).Copy() 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)) res := make([]*types.Header, len(blocks))
var ( var (
prevNumber = base.Number.Uint64() config = sim.b.ChainConfig()
base = sim.base
prevTimestamp = base.Time prevTimestamp = base.Time
header = base header = base
) )
for bi, block := range blocks { for bi, block := range blocks {
overrides := new(BlockOverrides) if block.BlockOverrides == nil || block.BlockOverrides.Number == nil {
if block.BlockOverrides != nil { return nil, errors.New("empty block number")
overrides = block.BlockOverrides
} }
// Sanitize block number and timestamp overrides := block.BlockOverrides
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()
if overrides.Time == nil { if overrides.Time == nil {
t := prevTimestamp + 12 t := prevTimestamp + 12
overrides.Time = (*hexutil.Uint64)(&t) overrides.Time = (*hexutil.Uint64)(&t)

View file

@ -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))
}