Merge a376f4b93e
into ef00a6e9a2
This commit is contained in:
commit
390fa7586c
|
@ -24,8 +24,10 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
"github.com/ethereum/go-ethereum/core/stateless"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
|
@ -443,3 +445,73 @@ func (api *DebugAPI) GetTrieFlushInterval() (string, error) {
|
||||||
}
|
}
|
||||||
return api.eth.blockchain.GetTrieFlushInterval().String(), nil
|
return api.eth.blockchain.GetTrieFlushInterval().String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *DebugAPI) ExecutionWitness(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*ExecutionWitness, error) {
|
||||||
|
block, err := api.eth.APIBackend.BlockByNumberOrHash(ctx, blockNrOrHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to retrieve block: %w", err)
|
||||||
|
}
|
||||||
|
if block == nil {
|
||||||
|
return nil, fmt.Errorf("block not found: %s", blockNrOrHash.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
witness, err := generateWitness(api.eth.blockchain, block)
|
||||||
|
return ToExecutionWitness(witness), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateWitness(blockchain *core.BlockChain, block *types.Block) (*stateless.Witness, error) {
|
||||||
|
witness, err := stateless.NewWitness(block.Header(), blockchain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create witness: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
parentHeader := witness.Headers[0]
|
||||||
|
statedb, err := blockchain.StateAt(parentHeader.Root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to retrieve parent state: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
statedb.StartPrefetcher("debug_execution_witness", witness)
|
||||||
|
defer statedb.StopPrefetcher()
|
||||||
|
|
||||||
|
res, err := blockchain.Processor().Process(block, statedb, *blockchain.GetVMConfig())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to process block %d: %w", block.Number(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := blockchain.Validator().ValidateState(block, statedb, res, false); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to validate block %d: %w", block.Number(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return witness, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecutionWitness is a witness json encoding for transferring across the network.
|
||||||
|
// In the future, we'll probably consider using the extWitness format instead for less overhead if performance becomes an issue.
|
||||||
|
// Currently using this format for ease of reading, parsing and compatibility across clients.
|
||||||
|
type ExecutionWitness struct {
|
||||||
|
Headers []*types.Header `json:"headers"`
|
||||||
|
Codes map[string]string `json:"codes"`
|
||||||
|
State map[string]string `json:"state"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func transformMap(in map[string]struct{}) map[string]string {
|
||||||
|
out := make(map[string]string, len(in))
|
||||||
|
for item := range in {
|
||||||
|
bytes := []byte(item)
|
||||||
|
key := crypto.Keccak256Hash(bytes).Hex()
|
||||||
|
out[key] = hexutil.Encode(bytes)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToExecutionWitness converts a witness to an execution witness format that is compatible with reth.
|
||||||
|
// keccak(node) => node
|
||||||
|
// keccak(bytecodes) => bytecodes
|
||||||
|
func ToExecutionWitness(w *stateless.Witness) *ExecutionWitness {
|
||||||
|
return &ExecutionWitness{
|
||||||
|
Headers: w.Headers,
|
||||||
|
Codes: transformMap(w.Codes),
|
||||||
|
State: transformMap(w.State),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package eth
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -26,13 +27,18 @@ import (
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
var dumper = spew.ConfigState{Indent: " "}
|
var dumper = spew.ConfigState{Indent: " "}
|
||||||
|
@ -224,3 +230,57 @@ func TestStorageRangeAt(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExecutionWitness(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var (
|
||||||
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
gspec = &core.Genesis{
|
||||||
|
Config: params.TestChainConfig,
|
||||||
|
Alloc: types.GenesisAlloc{testAddr: {Balance: big.NewInt(1000000000000000)}},
|
||||||
|
}
|
||||||
|
chain, _ = core.NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
|
signer = types.LatestSigner(gspec.Config)
|
||||||
|
)
|
||||||
|
// Create a database pre-initialize with a genesis block
|
||||||
|
|
||||||
|
blockNum := 10
|
||||||
|
_, bs, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), blockNum, func(i int, g *core.BlockGen) {
|
||||||
|
if i == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// add more transactions to the block
|
||||||
|
for j := 0; j < 10; j++ {
|
||||||
|
tx, err := types.SignNewTx(testKey, signer, &types.DynamicFeeTx{
|
||||||
|
ChainID: gspec.Config.ChainID,
|
||||||
|
Nonce: uint64((i-1)*10 + j),
|
||||||
|
GasTipCap: common.Big0,
|
||||||
|
GasFeeCap: g.PrevBlock(0).BaseFee(),
|
||||||
|
Gas: 50000,
|
||||||
|
To: &common.Address{0xaa},
|
||||||
|
Value: big.NewInt(int64(i)),
|
||||||
|
Data: nil,
|
||||||
|
AccessList: nil,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error creating tx: %v", err)
|
||||||
|
}
|
||||||
|
g.AddTx(tx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if _, err := chain.InsertChain(bs); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
block := chain.GetBlockByNumber(uint64(blockNum - 1))
|
||||||
|
require.NotNil(t, block)
|
||||||
|
|
||||||
|
witness, err := generateWitness(chain, block)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, _, err = core.ExecuteStateless(params.TestChainConfig, block, witness)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue