mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
wip: add 'stateless' command for stateless block execution
This commit is contained in:
parent
6039362bee
commit
adcc2bc9da
6 changed files with 265 additions and 2 deletions
102
cmd/stateless/main.go
Normal file
102
cmd/stateless/main.go
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/console/prompt"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/debug"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
"go.uber.org/automaxprocs/maxprocs"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
BlockWitnessFlag = &cli.StringFlag{
|
||||||
|
Name: "block-witness",
|
||||||
|
Usage: "foo bar",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var app = flags.NewApp("stateless block executor")
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Initialize the CLI app and start Geth
|
||||||
|
app.Action = stateless
|
||||||
|
app.Copyright = "Copyright 2013-2023 The go-ethereum Authors"
|
||||||
|
|
||||||
|
app.Flags = []cli.Flag{
|
||||||
|
BlockWitnessFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Before = func(ctx *cli.Context) error {
|
||||||
|
maxprocs.Set() // Automatically set GOMAXPROCS to match Linux container CPU quota.
|
||||||
|
if err := debug.Setup(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
app.After = func(ctx *cli.Context) error {
|
||||||
|
debug.Exit()
|
||||||
|
prompt.Stdin.Close() // Resets terminal mode.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stateless(ctx *cli.Context) error {
|
||||||
|
var vmConfig vm.Config
|
||||||
|
|
||||||
|
blockWitnessPath := ctx.String(BlockWitnessFlag.Name)
|
||||||
|
if blockWitnessPath == "" {
|
||||||
|
panic("block witness required")
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(blockWitnessPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var b []byte
|
||||||
|
f.Read(b)
|
||||||
|
block, witness, err := state.DecodeWitnessRLP(b)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryDb := witness.PopulateMemoryDB()
|
||||||
|
db, err := state.New(witness.Root(), state.NewDatabase(memoryDb), nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
chainConfig := params.MainnetChainConfig
|
||||||
|
engine, err := ethconfig.CreateConsensusEngine(chainConfig, memoryDb)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
validator := core.NewBlockValidator(chainConfig, nil, engine)
|
||||||
|
processor := core.NewStateProcessor(chainConfig, nil, engine)
|
||||||
|
|
||||||
|
receipts, logs, usedGas, err := processor.ProcessStateless(witness, block, db, vmConfig)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = logs
|
||||||
|
|
||||||
|
if err := validator.ValidateState(block, db, receipts, usedGas); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := app.Run(os.Args); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1852,6 +1852,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
} else {
|
} else {
|
||||||
status, err = bc.writeBlockAndSetHead(block, receipts, logs, statedb, false)
|
status, err = bc.writeBlockAndSetHead(block, receipts, logs, statedb, false)
|
||||||
}
|
}
|
||||||
|
statedb.GetWitness().Dump()
|
||||||
state.DumpBlockWithWitnessToFile(statedb.GetWitness(), block)
|
state.DumpBlockWithWitnessToFile(statedb.GetWitness(), block)
|
||||||
|
|
||||||
followupInterrupt.Store(true)
|
followupInterrupt.Store(true)
|
||||||
|
|
|
||||||
37
core/evm.go
37
core/evm.go
|
|
@ -17,6 +17,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -36,6 +37,42 @@ type ChainContext interface {
|
||||||
GetHeader(common.Hash, uint64) *types.Header
|
GetHeader(common.Hash, uint64) *types.Header
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewStatelessEVMBlockContext(witness *state.Witness, header *types.Header, engine consensus.Engine) vm.BlockContext {
|
||||||
|
getBlockHash := func(num uint64) common.Hash {
|
||||||
|
return witness.GetBlockHash(num)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
baseFee *big.Int
|
||||||
|
blobBaseFee *big.Int
|
||||||
|
random *common.Hash
|
||||||
|
)
|
||||||
|
|
||||||
|
beneficiary, _ := engine.Author(header) // Ignore error, we're past header validation
|
||||||
|
|
||||||
|
if header.BaseFee != nil {
|
||||||
|
baseFee = new(big.Int).Set(header.BaseFee)
|
||||||
|
}
|
||||||
|
if header.ExcessBlobGas != nil {
|
||||||
|
blobBaseFee = eip4844.CalcBlobFee(*header.ExcessBlobGas)
|
||||||
|
}
|
||||||
|
if header.Difficulty.Cmp(common.Big0) == 0 {
|
||||||
|
random = &header.MixDigest
|
||||||
|
}
|
||||||
|
return vm.BlockContext{
|
||||||
|
CanTransfer: CanTransfer,
|
||||||
|
Transfer: Transfer,
|
||||||
|
GetHash: getBlockHash,
|
||||||
|
Coinbase: beneficiary,
|
||||||
|
BlockNumber: new(big.Int).Set(header.Number),
|
||||||
|
Time: header.Time,
|
||||||
|
Difficulty: new(big.Int).Set(header.Difficulty),
|
||||||
|
BaseFee: baseFee,
|
||||||
|
BlobBaseFee: blobBaseFee,
|
||||||
|
GasLimit: header.GasLimit,
|
||||||
|
Random: random,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewEVMBlockContext creates a new context for use in the EVM.
|
// NewEVMBlockContext creates a new context for use in the EVM.
|
||||||
func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address) vm.BlockContext {
|
func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address) vm.BlockContext {
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
@ -16,10 +18,58 @@ type Witness struct {
|
||||||
lists map[common.Hash]map[string][]byte
|
lists map[common.Hash]map[string][]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Witness) GetBlockHash(num uint64) common.Hash {
|
||||||
|
return w.blockHashes[num]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Witness) Root() common.Hash {
|
||||||
|
return w.root
|
||||||
|
}
|
||||||
|
|
||||||
|
type encodedWitness struct {
|
||||||
|
block types.Block
|
||||||
|
root common.Hash
|
||||||
|
owners []common.Hash
|
||||||
|
allPaths [][]string
|
||||||
|
allNodes [][][]byte
|
||||||
|
blockNums []uint64
|
||||||
|
blockHashes []common.Hash
|
||||||
|
codes []Code
|
||||||
|
codeHashes []common.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *encodedWitness) ToWitness() *Witness {
|
||||||
|
var res Witness
|
||||||
|
res.root = e.root
|
||||||
|
for i := 0; i < len(e.codes); i++ {
|
||||||
|
res.codes[e.codeHashes[i]] = e.codes[i]
|
||||||
|
}
|
||||||
|
for i, owner := range e.owners {
|
||||||
|
pathMap := make(map[string][]byte)
|
||||||
|
for j := 0; j < len(e.allPaths[i]); j++ {
|
||||||
|
pathMap[e.allPaths[i][j]] = e.allNodes[i][j]
|
||||||
|
}
|
||||||
|
res.lists[owner] = pathMap
|
||||||
|
}
|
||||||
|
for i, blockNum := range e.blockNums {
|
||||||
|
res.blockHashes[blockNum] = e.blockHashes[i]
|
||||||
|
}
|
||||||
|
return &res
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeWitnessRLP(b []byte) (*types.Block, *Witness, error) {
|
||||||
|
var res encodedWitness
|
||||||
|
if err := rlp.DecodeBytes(b, &res); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return &res.block, res.ToWitness(), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Witness) EncodeRLP(b *types.Block) []byte {
|
func (w *Witness) EncodeRLP(b *types.Block) []byte {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
eb := rlp.NewEncoderBuffer(buf)
|
eb := rlp.NewEncoderBuffer(buf)
|
||||||
|
|
||||||
|
var root common.Hash
|
||||||
var owners []common.Hash
|
var owners []common.Hash
|
||||||
var allPaths [][]string
|
var allPaths [][]string
|
||||||
var allNodes [][][]byte
|
var allNodes [][][]byte
|
||||||
|
|
@ -52,6 +102,9 @@ func (w *Witness) EncodeRLP(b *types.Block) []byte {
|
||||||
}
|
}
|
||||||
l := eb.List()
|
l := eb.List()
|
||||||
b.EncodeRLP(eb)
|
b.EncodeRLP(eb)
|
||||||
|
if err := rlp.Encode(eb, root); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
if err := rlp.Encode(eb, owners); err != nil {
|
if err := rlp.Encode(eb, owners); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -107,6 +160,30 @@ func (w Witness) Copy() Witness {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Witness) Dump() {
|
||||||
|
for owner, al := range w.lists {
|
||||||
|
fmt.Printf("owner %x:\n", owner)
|
||||||
|
for path, node := range al {
|
||||||
|
fmt.Printf("%x: %x\n", []byte(path), node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Witness) PopulateMemoryDB() ethdb.Database {
|
||||||
|
db := rawdb.NewMemoryDatabase()
|
||||||
|
for codeHash, code := range w.codes {
|
||||||
|
rawdb.WriteCode(db, codeHash, code)
|
||||||
|
}
|
||||||
|
|
||||||
|
for owner, owned := range w.lists {
|
||||||
|
for path, node := range owned {
|
||||||
|
rawdb.WriteTrieNode(db, owner, []byte(path), common.Hash{}, node, rawdb.PathScheme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
func NewWitness() *Witness {
|
func NewWitness() *Witness {
|
||||||
return &Witness{
|
return &Witness{
|
||||||
make(map[uint64]common.Hash),
|
make(map[uint64]common.Hash),
|
||||||
|
|
@ -117,7 +194,7 @@ func NewWitness() *Witness {
|
||||||
}
|
}
|
||||||
func DumpBlockWithWitnessToFile(w *Witness, b *types.Block) {
|
func DumpBlockWithWitnessToFile(w *Witness, b *types.Block) {
|
||||||
enc := w.EncodeRLP(b)
|
enc := w.EncodeRLP(b)
|
||||||
path := "/datadrive/"
|
path, _ := os.Getwd() //"/datadrive/"
|
||||||
err := os.MkdirAll(fmt.Sprintf("%s/block-dump", path), 0755)
|
err := os.MkdirAll(fmt.Sprintf("%s/block-dump", path), 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("shite2")
|
panic("shite2")
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,53 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
return receipts, allLogs, *usedGas, nil
|
return receipts, allLogs, *usedGas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *StateProcessor) ProcessStateless(witness *state.Witness, block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
|
||||||
|
var (
|
||||||
|
receipts types.Receipts
|
||||||
|
usedGas = new(uint64)
|
||||||
|
header = block.Header()
|
||||||
|
blockHash = block.Hash()
|
||||||
|
blockNumber = block.Number()
|
||||||
|
allLogs []*types.Log
|
||||||
|
gp = new(GasPool).AddGas(block.GasLimit())
|
||||||
|
)
|
||||||
|
// Mutate the block and state according to any hard-fork specs
|
||||||
|
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
|
||||||
|
misc.ApplyDAOHardFork(statedb)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
context = NewStatelessEVMBlockContext(witness, header, p.bc.Engine())
|
||||||
|
vmenv = vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg)
|
||||||
|
signer = types.MakeSigner(p.config, header.Number, header.Time)
|
||||||
|
)
|
||||||
|
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||||
|
ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
|
||||||
|
}
|
||||||
|
// Iterate over and process the individual transactions
|
||||||
|
for i, tx := range block.Transactions() {
|
||||||
|
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||||
|
}
|
||||||
|
statedb.SetTxContext(tx.Hash(), i)
|
||||||
|
receipt, err := applyTransaction(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||||
|
}
|
||||||
|
receipts = append(receipts, receipt)
|
||||||
|
allLogs = append(allLogs, receipt.Logs...)
|
||||||
|
}
|
||||||
|
// Fail if Shanghai not enabled and len(withdrawals) is non-zero.
|
||||||
|
withdrawals := block.Withdrawals()
|
||||||
|
if len(withdrawals) > 0 && !p.config.IsShanghai(block.Number(), block.Time()) {
|
||||||
|
return nil, nil, 0, errors.New("withdrawals before shanghai")
|
||||||
|
}
|
||||||
|
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||||
|
p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), withdrawals)
|
||||||
|
|
||||||
|
return receipts, allLogs, *usedGas, nil
|
||||||
|
}
|
||||||
|
|
||||||
func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) {
|
func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) {
|
||||||
// Create a new context to be used in the EVM environment.
|
// Create a new context to be used in the EVM environment.
|
||||||
txContext := NewEVMTxContext(msg)
|
txContext := NewEVMTxContext(msg)
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue