This commit is contained in:
Sina M 2025-02-18 21:39:21 +00:00 committed by GitHub
commit 15cf9f0bc9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 122 additions and 10 deletions

View File

@ -775,7 +775,7 @@ func (api *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockN
//
// Note, this function doesn't make any changes in the state/blockchain and is
// useful to execute and retrieve values.
func (api *BlockChainAPI) SimulateV1(ctx context.Context, opts simOpts, blockNrOrHash *rpc.BlockNumberOrHash) ([]map[string]interface{}, error) {
func (api *BlockChainAPI) SimulateV1(ctx context.Context, opts simOpts, blockNrOrHash *rpc.BlockNumberOrHash) ([]*simBlockResult, error) {
if len(opts.BlockStateCalls) == 0 {
return nil, &invalidParamsError{message: "empty input"}
} else if len(opts.BlockStateCalls) > maxSimulateBlocks {

View File

@ -24,6 +24,7 @@ import (
"encoding/json"
"errors"
"fmt"
"math"
"math/big"
"os"
"path/filepath"
@ -2309,6 +2310,101 @@ func TestSimulateV1(t *testing.T) {
}
}
func TestSimulateV1ChainLinkage(t *testing.T) {
var (
acc = newTestAccount()
sender = acc.addr
contractAddr = common.Address{0xaa, 0xaa}
recipient = common.Address{0xbb, 0xbb}
gspec = &core.Genesis{
Config: params.MergedTestChainConfig,
Alloc: types.GenesisAlloc{
sender: {Balance: big.NewInt(params.Ether)},
contractAddr: {Code: common.Hex2Bytes("5f35405f8114600f575f5260205ff35b5f80fd")},
},
}
signer = types.LatestSigner(params.MergedTestChainConfig)
)
backend := newTestBackend(t, 1, gspec, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) {
tx := types.MustSignNewTx(acc.key, signer, &types.LegacyTx{
Nonce: uint64(i),
GasPrice: b.BaseFee(),
Gas: params.TxGas,
To: &recipient,
Value: big.NewInt(500),
})
b.AddTx(tx)
})
ctx := context.Background()
stateDB, baseHeader, err := backend.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber))
if err != nil {
t.Fatalf("failed to get state and header: %v", err)
}
sim := &simulator{
b: backend,
state: stateDB,
base: baseHeader,
chainConfig: backend.ChainConfig(),
gp: new(core.GasPool).AddGas(math.MaxUint64),
traceTransfers: false,
validate: false,
fullTx: false,
}
var (
call1 = TransactionArgs{
From: &sender,
To: &recipient,
Value: (*hexutil.Big)(big.NewInt(1000)),
}
call2 = TransactionArgs{
From: &sender,
To: &recipient,
Value: (*hexutil.Big)(big.NewInt(2000)),
}
call3a = TransactionArgs{
From: &sender,
To: &contractAddr,
Input: uint256ToBytes(uint256.NewInt(baseHeader.Number.Uint64() + 1)),
Gas: newUint64(1000000),
}
call3b = TransactionArgs{
From: &sender,
To: &contractAddr,
Input: uint256ToBytes(uint256.NewInt(baseHeader.Number.Uint64() + 2)),
Gas: newUint64(1000000),
}
blocks = []simBlock{
{Calls: []TransactionArgs{call1}},
{Calls: []TransactionArgs{call2}},
{Calls: []TransactionArgs{call3a, call3b}},
}
)
results, err := sim.execute(ctx, blocks)
if err != nil {
t.Fatalf("simulation execution failed: %v", err)
}
require.Equal(t, 3, len(results), "expected 3 simulated blocks")
// Check linkages of simulated blocks:
// Verify that block2's parent hash equals block1's hash.
block1 := results[0].Block
block2 := results[1].Block
block3 := results[2].Block
require.Equal(t, block1.ParentHash(), baseHeader.Hash(), "parent hash of block1 should equal hash of base block")
require.Equal(t, block1.Hash(), block2.Header().ParentHash, "parent hash of block2 should equal hash of block1")
require.Equal(t, block2.Hash(), block3.Header().ParentHash, "parent hash of block3 should equal hash of block2")
// In block3, two calls were executed to our contract.
// The first call in block3 should return the blockhash for block1 (i.e. block1.Hash()),
// whereas the second call should return the blockhash for block2 (i.e. block2.Hash()).
require.Equal(t, block1.Hash().Bytes(), []byte(results[2].Calls[0].ReturnValue), "returned blockhash for block1 does not match")
require.Equal(t, block2.Hash().Bytes(), []byte(results[2].Calls[1].ReturnValue), "returned blockhash for block2 does not match")
}
func TestSignTransaction(t *testing.T) {
t.Parallel()
// Initialize test accounts

View File

@ -73,6 +73,20 @@ func (r *simCallResult) MarshalJSON() ([]byte, error) {
return json.Marshal((*callResultAlias)(r))
}
// simBlockResult is the result of a simulated block.
type simBlockResult struct {
fullTx bool
chainConfig *params.ChainConfig
Block *types.Block
Calls []simCallResult
}
func (r *simBlockResult) MarshalJSON() ([]byte, error) {
blockData := RPCMarshalBlock(r.Block, true, r.fullTx, r.chainConfig)
blockData["calls"] = r.Calls
return json.Marshal(blockData)
}
// simOpts are the inputs to eth_simulateV1.
type simOpts struct {
BlockStateCalls []simBlock
@ -95,7 +109,7 @@ type simulator struct {
}
// execute runs the simulation of a series of blocks.
func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]map[string]interface{}, error) {
func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]*simBlockResult, error) {
if err := ctx.Err(); err != nil {
return nil, err
}
@ -123,19 +137,21 @@ func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]map[str
return nil, err
}
var (
results = make([]map[string]interface{}, len(blocks))
parent = sim.base
results = make([]*simBlockResult, len(blocks))
// prevHeaders is the header for all previously simulated blocks.
// It is "filled" compared to the barebone header available prior to execution.
// It will be used for serving the BLOCKHASH opcode.
prevHeaders = make([]*types.Header, 0, len(blocks))
parent = sim.base
)
for bi, block := range blocks {
result, callResults, err := sim.processBlock(ctx, &block, headers[bi], parent, headers[:bi], timeout)
result, callResults, err := sim.processBlock(ctx, &block, headers[bi], parent, prevHeaders, timeout)
if err != nil {
return nil, err
}
enc := RPCMarshalBlock(result, true, sim.fullTx, sim.chainConfig)
enc["calls"] = callResults
results[bi] = enc
parent = headers[bi]
results[bi] = &simBlockResult{fullTx: sim.fullTx, chainConfig: sim.chainConfig, Block: result, Calls: callResults}
parent = result.Header()
prevHeaders = append(prevHeaders, parent)
}
return results, nil
}