Merge 746755e415
into dab746b3ef
This commit is contained in:
commit
15cf9f0bc9
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue