diff --git a/cmd/opera/launcher/export.go b/cmd/opera/launcher/export.go index 30b7bd48c..f8cc93c17 100644 --- a/cmd/opera/launcher/export.go +++ b/cmd/opera/launcher/export.go @@ -2,6 +2,7 @@ package launcher import ( "compress/gzip" + "errors" "io" "os" "strconv" @@ -138,7 +139,11 @@ func exportEvmKeys(ctx *cli.Context) error { keysDB := batched.Wrap(keysDB_) defer keysDB.Close() - it := gdb.EvmStore().EvmDb.NewIterator(nil, nil) + evmDb := gdb.EvmStore().EvmDb + if evmDb == nil { + return errors.New("only legacy MPT EVM keys export makes sense") + } + it := evmDb.NewIterator(nil, nil) // iterate only over MPT data it = mptAndPreimageIterator{it} defer it.Release() diff --git a/cmd/opera/launcher/genesiscmd.go b/cmd/opera/launcher/genesiscmd.go index 211126fc3..2276d2c24 100644 --- a/cmd/opera/launcher/genesiscmd.go +++ b/cmd/opera/launcher/genesiscmd.go @@ -376,7 +376,12 @@ func exportGenesis(ctx *cli.Context) error { if err != nil { return err } - it := gdb.EvmStore().EvmDb.NewIterator(nil, nil) + + evmDb := gdb.EvmStore().EvmDb + if evmDb == nil { + return errors.New("genesis should include legacy MPT EVM data only") + } + it := evmDb.NewIterator(nil, nil) if mode == "mpt" { // iterate only over MPT data it = mptIterator{it} diff --git a/cmd/opera/launcher/snapshotcmd.go b/cmd/opera/launcher/snapshotcmd.go index b20d2772c..ecabc9078 100644 --- a/cmd/opera/launcher/snapshotcmd.go +++ b/cmd/opera/launcher/snapshotcmd.go @@ -195,7 +195,13 @@ func pruneState(ctx *cli.Context) error { return err } } - pruner, err := evmpruner.NewPruner(gdb.EvmStore().EvmDb, genesisRoot, root, tmpDir, bloom) + + evmDb := gdb.EvmStore().EvmDb + if evmDb == nil { + return errors.New("only legacy MPT EVM pruning makes sense") + } + + pruner, err := evmpruner.NewPruner(evmDb, genesisRoot, root, tmpDir, bloom) if err != nil { log.Error("Failed to open snapshot tree", "err", err) return err @@ -269,6 +275,9 @@ func traverseState(ctx *cli.Context) error { return errors.New("failed to open snapshot tree: genesis is not written") } chaindb := gdb.EvmStore().EvmDb + if chaindb == nil { + return errors.New("only legacy MPT EVM nodes traversing makes sense") + } if ctx.NArg() > 1 { log.Error("Too many arguments given") @@ -359,6 +368,9 @@ func traverseRawState(ctx *cli.Context) error { return errors.New("failed to open snapshot tree: genesis is not written") } chaindb := gdb.EvmStore().EvmDb + if chaindb == nil { + return errors.New("only legacy MPT EVM nodes traversing makes sense") + } if ctx.NArg() > 1 { log.Error("Too many arguments given") diff --git a/ethapi/api.go b/ethapi/api.go index d0a7b5d8d..eeff6c69f 100644 --- a/ethapi/api.go +++ b/ethapi/api.go @@ -34,7 +34,6 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -710,8 +709,14 @@ type StorageResult struct { // GetProof returns the Merkle-proof for a given account and optionally some storage keys. func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*AccountResult, error) { - state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) - if state == nil || err != nil { + statedb, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + if statedb == nil || err != nil { + return nil, err + } + + state, ok := evmcore.IsMptStateDB(statedb) + if !ok { + err = errors.New("Proofs are not provided by current state db impl") return nil, err } @@ -912,7 +917,7 @@ type OverrideAccount struct { type StateOverride map[common.Address]OverrideAccount // Apply overrides the fields of specified accounts into the given state. -func (diff *StateOverride) Apply(state *state.StateDB) error { +func (diff *StateOverride) Apply(state evmcore.StateDB) error { if diff == nil { return nil } diff --git a/ethapi/backend.go b/ethapi/backend.go index d2e991497..06dffe9e8 100644 --- a/ethapi/backend.go +++ b/ethapi/backend.go @@ -26,7 +26,6 @@ import ( "github.com/Fantom-foundation/lachesis-base/inter/idx" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" @@ -69,12 +68,12 @@ type Backend interface { HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*evmcore.EvmHeader, error) HeaderByHash(ctx context.Context, hash common.Hash) (*evmcore.EvmHeader, error) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*evmcore.EvmBlock, error) - StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *evmcore.EvmHeader, error) + StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (evmcore.StateDB, *evmcore.EvmHeader, error) ResolveRpcBlockNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (idx.Block, error) BlockByHash(ctx context.Context, hash common.Hash) (*evmcore.EvmBlock, error) GetReceiptsByNumber(ctx context.Context, number rpc.BlockNumber) (types.Receipts, error) GetTd(hash common.Hash) *big.Int - GetEVM(ctx context.Context, msg evmcore.Message, state *state.StateDB, header *evmcore.EvmHeader, vmConfig *vm.Config) (*vm.EVM, func() error, error) + GetEVM(ctx context.Context, msg evmcore.Message, state evmcore.StateDB, header *evmcore.EvmHeader, vmConfig *vm.Config) (*vm.EVM, func() error, error) MinGasPrice() *big.Int MaxGasLimit() uint64 diff --git a/evmcore/apply_fake_genesis.go b/evmcore/apply_fake_genesis.go index 80a1e2d0a..6260b7014 100644 --- a/evmcore/apply_fake_genesis.go +++ b/evmcore/apply_fake_genesis.go @@ -24,7 +24,6 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" @@ -35,7 +34,7 @@ import ( var FakeGenesisTime = inter.Timestamp(1608600000 * time.Second) // ApplyFakeGenesis writes or updates the genesis block in db. -func ApplyFakeGenesis(statedb *state.StateDB, time inter.Timestamp, balances map[common.Address]*big.Int) (*EvmBlock, error) { +func ApplyFakeGenesis(statedb StateDB, time inter.Timestamp, balances map[common.Address]*big.Int) (*EvmBlock, error) { for acc, balance := range balances { statedb.SetBalance(acc, balance) } @@ -50,7 +49,7 @@ func ApplyFakeGenesis(statedb *state.StateDB, time inter.Timestamp, balances map return block, nil } -func flush(statedb *state.StateDB, clean bool) (root common.Hash, err error) { +func flush(statedb StateDB, clean bool) (root common.Hash, err error) { root, err = statedb.Commit(clean) if err != nil { return @@ -83,7 +82,7 @@ func genesisBlock(time inter.Timestamp, root common.Hash) *EvmBlock { } // MustApplyFakeGenesis writes the genesis block and state to db, panicking on error. -func MustApplyFakeGenesis(statedb *state.StateDB, time inter.Timestamp, balances map[common.Address]*big.Int) *EvmBlock { +func MustApplyFakeGenesis(statedb StateDB, time inter.Timestamp, balances map[common.Address]*big.Int) *EvmBlock { block, err := ApplyFakeGenesis(statedb, time, balances) if err != nil { log.Crit("ApplyFakeGenesis", "err", err) diff --git a/evmcore/bench_test.go b/evmcore/bench_test.go index b5952856d..db96743ad 100644 --- a/evmcore/bench_test.go +++ b/evmcore/bench_test.go @@ -23,10 +23,11 @@ import ( "os" "testing" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" @@ -145,13 +146,19 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { defer db.Close() } + stateAt := func(root common.Hash) StateDB { + st, err := state.New(root, state.NewDatabase(db), nil) + if err != nil { + panic(err) + } + + return ToStateDB(st) + } + // Generate a chain of b.N blocks using the supplied block // generator function. // state - statedb, err := state.New(common.Hash{}, state.NewDatabase(db), nil) - if err != nil { - b.Fatalf("cannot create statedb: %v", err) - } + statedb := newMemoryStateDB() genesisBlock := MustApplyFakeGenesis(statedb, FakeGenesisTime, map[common.Address]*big.Int{ benchRootAddr: benchRootFunds, }) @@ -162,5 +169,5 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { b.ReportAllocs() b.ResetTimer() - _, _, _ = GenerateChain(nil, genesisBlock, db, b.N, gen) + _, _, _ = GenerateChain(nil, genesisBlock, b.N, stateAt, gen) } diff --git a/evmcore/chain_makers.go b/evmcore/chain_makers.go index 5d34326d6..ff6f8b713 100644 --- a/evmcore/chain_makers.go +++ b/evmcore/chain_makers.go @@ -22,10 +22,8 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" "github.com/Fantom-foundation/go-opera/inter" @@ -39,7 +37,7 @@ type BlockGen struct { parent *EvmBlock chain []*EvmBlock header *EvmHeader - statedb *state.StateDB + statedb StateDB gasPool *GasPool txs []*types.Transaction @@ -100,7 +98,7 @@ func (b *BlockGen) AddTxWithChain(bc DummyChain, tx *types.Transaction) { b.statedb.Prepare(tx.Hash(), len(b.txs)) blockContext := NewEVMBlockContext(b.header, bc, nil) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, b.statedb, b.config, opera.DefaultVMConfig) - receipt, _, _, err := applyTransaction(msg, b.config, b.gasPool, b.statedb, b.header.Number, b.header.Hash, tx, &b.header.GasUsed, vmenv, func(log *types.Log, db *state.StateDB) {}) + receipt, _, _, err := applyTransaction(msg, b.config, b.gasPool, b.statedb, b.header.Number, b.header.Hash, tx, &b.header.GasUsed, vmenv, func(log *types.Log, db StateDB) {}) if err != nil { panic(err) } @@ -185,7 +183,11 @@ func (b *BlockGen) OffsetTime(seconds int64) { // Blocks created by GenerateChain do not contain valid proof of work // values. Inserting them into BlockChain requires use of FakePow or // a similar non-validating proof of work implementation. -func GenerateChain(config *params.ChainConfig, parent *EvmBlock, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*EvmBlock, []types.Receipts, DummyChain) { +func GenerateChain( + config *params.ChainConfig, parent *EvmBlock, n int, stateAt func(common.Hash) StateDB, gen func(int, *BlockGen), +) ( + []*EvmBlock, []types.Receipts, DummyChain, +) { if config == nil { config = params.AllEthashProtocolChanges } @@ -195,7 +197,7 @@ func GenerateChain(config *params.ChainConfig, parent *EvmBlock, db ethdb.Databa } blocks, receipts := make([]*EvmBlock, n), make([]types.Receipts, n) - genblock := func(i int, parent *EvmBlock, statedb *state.StateDB) (*EvmBlock, types.Receipts) { + genblock := func(i int, parent *EvmBlock, statedb StateDB) (*EvmBlock, types.Receipts) { b := &BlockGen{i: i, chain: blocks, parent: parent, statedb: statedb, config: config} b.header = makeHeader(parent, statedb) @@ -219,11 +221,10 @@ func GenerateChain(config *params.ChainConfig, parent *EvmBlock, db ethdb.Databa return block, b.receipts } + for i := 0; i < n; i++ { - statedb, err := state.New(parent.Root, state.NewDatabase(db), nil) - if err != nil { - panic(err) - } + statedb := stateAt(parent.Root) + block, receipt := genblock(i, parent, statedb) blocks[i] = block receipts[i] = receipt @@ -234,7 +235,7 @@ func GenerateChain(config *params.ChainConfig, parent *EvmBlock, db ethdb.Databa return blocks, receipts, chain } -func makeHeader(parent *EvmBlock, state *state.StateDB) *EvmHeader { +func makeHeader(parent *EvmBlock, state StateDB) *EvmHeader { var t inter.Timestamp if parent.Time == 0 { t = 10 @@ -252,27 +253,6 @@ func makeHeader(parent *EvmBlock, state *state.StateDB) *EvmHeader { return header } -// makeHeaderChain creates a deterministic chain of headers rooted at parent. -func makeHeaderChain(parent *EvmHeader, n int, db ethdb.Database, seed int) []*EvmHeader { - block := &EvmBlock{} - block.EvmHeader = *parent - - blocks := makeBlockChain(block, n, db, seed) - headers := make([]*EvmHeader, len(blocks)) - for i, block := range blocks { - headers[i] = block.Header() - } - return headers -} - -// makeBlockChain creates a deterministic chain of blocks rooted at parent. -func makeBlockChain(parent *EvmBlock, n int, db ethdb.Database, seed int) []*EvmBlock { - blocks, _, _ := GenerateChain(params.TestChainConfig, parent, db, n, func(i int, b *BlockGen) { - b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)}) - }) - return blocks -} - type fakeChainReader struct { config *params.ChainConfig genesis *EvmBlock diff --git a/evmcore/state_prefetcher.go b/evmcore/state_prefetcher.go index dac3d1e3c..3cdb3d21c 100644 --- a/evmcore/state_prefetcher.go +++ b/evmcore/state_prefetcher.go @@ -19,7 +19,6 @@ package evmcore import ( "sync/atomic" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" @@ -46,7 +45,7 @@ func newStatePrefetcher(config *params.ChainConfig, bc DummyChain) *statePrefetc // Prefetch processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb, but any changes are discarded. The // only goal is to pre-cache transaction signatures and state trie nodes. -func (p *statePrefetcher) Prefetch(block *EvmBlock, statedb *state.StateDB, cfg vm.Config, interrupt *uint32) { +func (p *statePrefetcher) Prefetch(block *EvmBlock, statedb StateDB, cfg vm.Config, interrupt *uint32) { var ( header = block.Header() gaspool = new(GasPool).AddGas(block.GasLimit) @@ -84,7 +83,7 @@ func (p *statePrefetcher) Prefetch(block *EvmBlock, statedb *state.StateDB, cfg // precacheTransaction attempts to apply a transaction to the given state database // and uses the input parameters for its environment. The goal is not to execute // the transaction successfully, rather to warm up touched data slots. -func precacheTransaction(msg types.Message, config *params.ChainConfig, gaspool *GasPool, statedb *state.StateDB, header *EvmHeader, evm *vm.EVM) error { +func precacheTransaction(msg types.Message, config *params.ChainConfig, gaspool *GasPool, statedb StateDB, header *EvmHeader, evm *vm.EVM) error { // Update the evm with the new transaction context. evm.Reset(NewEVMTxContext(msg), statedb) // Add addresses to access list if applicable diff --git a/evmcore/state_processor.go b/evmcore/state_processor.go index 931699588..be04283c0 100644 --- a/evmcore/state_processor.go +++ b/evmcore/state_processor.go @@ -21,7 +21,6 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -56,7 +55,7 @@ func NewStateProcessor(config *params.ChainConfig, bc DummyChain) *StateProcesso // returns the amount of gas that was used in the process. If any of the // transactions failed to execute due to insufficient gas it will return an error. func (p *StateProcessor) Process( - block *EvmBlock, statedb *state.StateDB, cfg vm.Config, usedGas *uint64, onNewLog func(*types.Log, *state.StateDB), + block *EvmBlock, statedb StateDB, cfg vm.Config, usedGas *uint64, onNewLog func(*types.Log, StateDB), ) ( receipts types.Receipts, allLogs []*types.Log, skipped []uint32, err error, ) { @@ -99,13 +98,13 @@ func applyTransaction( msg types.Message, config *params.ChainConfig, gp *GasPool, - statedb *state.StateDB, + statedb StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, - onNewLog func(*types.Log, *state.StateDB), + onNewLog func(*types.Log, StateDB), ) ( *types.Receipt, uint64, diff --git a/evmcore/state_transition.go b/evmcore/state_transition.go index 1fee3d1f4..f630b14f3 100644 --- a/evmcore/state_transition.go +++ b/evmcore/state_transition.go @@ -42,8 +42,10 @@ The state transitioning model does all the necessary work to work out a valid ne 3) Create a new state object if the recipient is \0*32 4) Value transfer == If contract creation == - 4a) Attempt to run transaction data - 4b) If valid, use result as code for the new state object + + 4a) Attempt to run transaction data + 4b) If valid, use result as code for the new state object + == end == 5) Run Script section 6) Derive new state root @@ -232,13 +234,13 @@ func (st *StateTransition) internal() bool { // TransitionDb will transition the state by applying the current message and // returning the evm execution result with following fields. // -// - used gas: -// total gas used (including gas being refunded) -// - returndata: -// the returned data from evm -// - concrete execution error: -// various **EVM** error which aborts the execution, -// e.g. ErrOutOfGas, ErrExecutionReverted +// - used gas: +// total gas used (including gas being refunded) +// - returndata: +// the returned data from evm +// - concrete execution error: +// various **EVM** error which aborts the execution, +// e.g. ErrOutOfGas, ErrExecutionReverted // // However if any consensus issue encountered, return the error directly with // nil evm execution result. diff --git a/evmcore/tx_noncer.go b/evmcore/tx_noncer.go index ef549ed76..9dab106f0 100644 --- a/evmcore/tx_noncer.go +++ b/evmcore/tx_noncer.go @@ -20,20 +20,19 @@ import ( "sync" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" ) // txNoncer is a tiny virtual state database to manage the executable nonces of // accounts in the pool, falling back to reading from a real state database if // an account is unknown. type txNoncer struct { - fallback *state.StateDB + fallback StateDB nonces map[common.Address]uint64 lock sync.Mutex } // newTxNoncer creates a new virtual state database to track the pool nonces. -func newTxNoncer(statedb *state.StateDB) *txNoncer { +func newTxNoncer(statedb StateDB) *txNoncer { return &txNoncer{ fallback: statedb.Copy(), nonces: make(map[common.Address]uint64), diff --git a/evmcore/tx_pool.go b/evmcore/tx_pool.go index 8f07673a2..fedc19cf8 100644 --- a/evmcore/tx_pool.go +++ b/evmcore/tx_pool.go @@ -28,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/consensus/misc" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" notify "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" @@ -137,7 +136,7 @@ const ( type StateReader interface { CurrentBlock() *EvmBlock GetBlock(hash common.Hash, number uint64) *EvmBlock - StateAt(root common.Hash) (*state.StateDB, error) + StateAt(root common.Hash) (StateDB, error) MinGasPrice() *big.Int EffectiveMinTip() *big.Int MaxGasLimit() uint64 @@ -240,9 +239,9 @@ type TxPool struct { eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions. eip1559 bool // Fork indicator whether we are using EIP-1559 type transactions. - currentState *state.StateDB // Current state in the blockchain head - pendingNonces *txNoncer // Pending state tracking virtual nonces - currentMaxGas uint64 // Current gas limit for transaction caps + currentState StateDB // Current state in the blockchain head + pendingNonces *txNoncer // Pending state tracking virtual nonces + currentMaxGas uint64 // Current gas limit for transaction caps locals *accountSet // Set of local transaction to exempt from eviction rules journal *txJournal // Journal of local transaction to back up to disk diff --git a/evmcore/tx_pool_test.go b/evmcore/tx_pool_test.go index 11532fe4b..6c8f64370 100644 --- a/evmcore/tx_pool_test.go +++ b/evmcore/tx_pool_test.go @@ -56,7 +56,7 @@ func init() { } type testBlockChain struct { - statedb *state.StateDB + statedb StateDB gasLimit uint64 chainHeadFeed *event.Feed } @@ -96,7 +96,7 @@ func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *EvmBlock { return bc.CurrentBlock() } -func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) { +func (bc *testBlockChain) StateAt(common.Hash) (StateDB, error) { return bc.statedb, nil } @@ -140,8 +140,13 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { return setupTxPoolWithConfig(params.TestChainConfig) } +func newMemoryStateDB() StateDB { + st, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + return ToStateDB(st) +} + func setupTxPoolWithConfig(config *params.ChainConfig) (*TxPool, *ecdsa.PrivateKey) { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb := newMemoryStateDB() blockchain := &testBlockChain{statedb, 10000000, new(event.Feed)} key, _ := crypto.GenerateKey() @@ -222,13 +227,13 @@ type testChain struct { // testChain.State() is used multiple times to reset the pending state. // when simulate is true it will create a state that indicates // that tx0 and tx1 are included in the chain. -func (c *testChain) State() (*state.StateDB, error) { +func (c *testChain) State() (StateDB, error) { // delay "state change" by one. The tx pool fetches the // state multiple times and by delaying it a bit we simulate // a state change between those fetches. stdb := c.statedb if *c.trigger { - c.statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + c.statedb = newMemoryStateDB() // simulate that the new head block included tx0 and tx1 c.statedb.SetNonce(c.address, 2) c.statedb.SetBalance(c.address, new(big.Int).SetUint64(params.Ether)) @@ -244,10 +249,10 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) { t.Parallel() var ( - key, _ = crypto.GenerateKey() - address = crypto.PubkeyToAddress(key.PublicKey) - statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - trigger = false + key, _ = crypto.GenerateKey() + address = crypto.PubkeyToAddress(key.PublicKey) + statedb = newMemoryStateDB() + trigger = false ) // setup pool with 2 transaction in it @@ -447,7 +452,7 @@ func TestTransactionChainFork(t *testing.T) { addr := crypto.PubkeyToAddress(key.PublicKey) resetState := func() { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb := newMemoryStateDB() statedb.AddBalance(addr, big.NewInt(100000000000000)) pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)} @@ -476,7 +481,7 @@ func TestTransactionDoubleNonce(t *testing.T) { addr := crypto.PubkeyToAddress(key.PublicKey) resetState := func() { - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb := newMemoryStateDB() statedb.AddBalance(addr, big.NewInt(100000000000000)) pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)} @@ -676,7 +681,7 @@ func TestTransactionPostponing(t *testing.T) { t.Parallel() // Create the pool to test the postponing with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb := newMemoryStateDB() blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain) @@ -889,7 +894,7 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) { t.Parallel() // Create the pool to test the limit enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb := newMemoryStateDB() blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} config := testTxPoolConfig @@ -981,7 +986,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) { evictionInterval = time.Millisecond * 100 // Create the pool to test the non-expiration enforcement - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb := newMemoryStateDB() blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} config := testTxPoolConfig @@ -1166,7 +1171,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) { t.Parallel() // Create the pool to test the limit enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb := newMemoryStateDB() blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} config := testTxPoolConfig @@ -1268,7 +1273,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) { t.Parallel() // Create the pool to test the limit enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb := newMemoryStateDB() blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} config := testTxPoolConfig @@ -1302,7 +1307,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) { t.Parallel() // Create the pool to test the limit enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb := newMemoryStateDB() blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} config := testTxPoolConfig @@ -1350,7 +1355,7 @@ func TestTransactionPoolRepricing(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb := newMemoryStateDB() blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain) @@ -1598,7 +1603,7 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb := newMemoryStateDB() blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} pool := NewTxPool(testTxPoolConfig, eip1559Config, blockchain) @@ -1671,7 +1676,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb := newMemoryStateDB() blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} config := testTxPoolConfig @@ -1777,7 +1782,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb := newMemoryStateDB() blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} config := testTxPoolConfig @@ -2009,7 +2014,7 @@ func TestTransactionDeduplication(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb := newMemoryStateDB() blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain) @@ -2075,7 +2080,7 @@ func TestTransactionReplacement(t *testing.T) { t.Parallel() // Create the pool to test the pricing enforcement with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb := newMemoryStateDB() blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain) @@ -2280,7 +2285,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) { os.Remove(journal) // Create the original pool to inject transaction into the journal - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb := newMemoryStateDB() blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} config := testTxPoolConfig @@ -2378,7 +2383,7 @@ func TestTransactionStatusCheck(t *testing.T) { t.Parallel() // Create the pool to test the status retrievals with - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb := newMemoryStateDB() blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain) diff --git a/evmcore/types.go b/evmcore/types.go index 9ab786ce7..26a0d0cd2 100644 --- a/evmcore/types.go +++ b/evmcore/types.go @@ -17,11 +17,61 @@ package evmcore import ( + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" ) +// StateDB is an EVM database for full state querying. +type StateDB interface { + vm.StateDB + + SetBalance(addr common.Address, amount *big.Int) + // Database retrieves the low level database supporting the lower level trie ops. + Database() state.Database + // Prepare sets the current transaction hash and index which are + // used when the EVM emits new state logs. + Prepare(thash common.Hash, ti int) + // TxIndex returns the current transaction index set by Prepare. + TxIndex() int + GetLogs(hash common.Hash, blockHash common.Hash) []*types.Log + // IntermediateRoot computes the current root hash of the state trie. + // It is called in between transactions to get the root hash that + // goes into transaction receipts. + IntermediateRoot(deleteEmptyObjects bool) common.Hash + // Finalise finalises the state by removing the s destructed objects and clears + // the journal as well as the refunds. Finalise, however, will not push any updates + // into the tries just yet. Only IntermediateRoot or Commit will do that. + Finalise(deleteEmptyObjects bool) + // Commit writes the state to the underlying in-memory trie database. + Commit(deleteEmptyObjects bool) (common.Hash, error) + + Error() error + Copy() StateDB + SetStorage(addr common.Address, storage map[common.Hash]common.Hash) + + Measurements() Measurements +} + +// Measurements gathered during execution for debugging purposes +type Measurements struct { + AccountReads time.Duration + AccountHashes time.Duration + AccountUpdates time.Duration + AccountCommits time.Duration + StorageReads time.Duration + StorageHashes time.Duration + StorageUpdates time.Duration + StorageCommits time.Duration + SnapshotAccountReads time.Duration + SnapshotStorageReads time.Duration + SnapshotCommits time.Duration +} + // Validator is an interface which defines the standard for block validation. It // is only responsible for validating block contents, as the header validation is // done by the specific consensus engines. @@ -31,7 +81,7 @@ type Validator interface { // ValidateState validates the given statedb and optionally the receipts and // gas used. - ValidateState(block *EvmBlock, state *state.StateDB, receipts types.Receipts, usedGas uint64) error + ValidateState(block *EvmBlock, state StateDB, receipts types.Receipts, usedGas uint64) error } // Prefetcher is an interface for pre-caching transaction signatures and state. @@ -39,7 +89,7 @@ type Prefetcher interface { // Prefetch processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb, but any changes are discarded. The // only goal is to pre-cache transaction signatures and state trie nodes. - Prefetch(block *EvmBlock, statedb *state.StateDB, cfg vm.Config, interrupt *uint32) + Prefetch(block *EvmBlock, statedb StateDB, cfg vm.Config, interrupt *uint32) } // Processor is an interface for processing blocks using a given initial state. @@ -47,5 +97,41 @@ type Processor interface { // Process processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb and applying any rewards to both // the processor (coinbase) and any included uncles. - Process(block *EvmBlock, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) + Process(block *EvmBlock, statedb StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) +} + +func ToStateDB(statedb *state.StateDB) StateDB { + return &stateWrapper{statedb} +} + +func IsMptStateDB(st StateDB) (statedb *state.StateDB, ok bool) { + if wrapper, is := st.(*stateWrapper); is { + statedb = wrapper.StateDB + ok = true + return + } + return +} + +// stateWrapper casts *state.StateDB to StateDB interface. +type stateWrapper struct { + *state.StateDB +} + +func (w *stateWrapper) Copy() StateDB { + statedb := w.StateDB.Copy() + return &stateWrapper{statedb} +} + +func (w *stateWrapper) Measurements() Measurements { + return Measurements{ + AccountReads: w.StateDB.AccountReads, + AccountHashes: w.StateDB.AccountHashes, + AccountUpdates: w.StateDB.AccountUpdates, + AccountCommits: w.StateDB.AccountCommits, + StorageReads: w.StateDB.StorageReads, + StorageHashes: w.StateDB.StorageHashes, + StorageUpdates: w.StateDB.StorageUpdates, + StorageCommits: w.StateDB.StorageCommits, + } } diff --git a/evmcore/types_test.go b/evmcore/types_test.go new file mode 100644 index 000000000..2e20da9e4 --- /dev/null +++ b/evmcore/types_test.go @@ -0,0 +1,25 @@ +package evmcore + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/stretchr/testify/require" +) + +func TestStateWrapper(t *testing.T) { + require := require.New(t) + + mpt, err := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + require.NoError(err) + require.NotNil(mpt) + + wrapped := ToStateDB(mpt) + require.NotNil(wrapped) + + unwrapped, ok := IsMptStateDB(wrapped) + require.True(ok) + require.NotNil(unwrapped) +} diff --git a/gossip/blockproc/drivermodule/driver_txs.go b/gossip/blockproc/drivermodule/driver_txs.go index 66380f10e..8a2814b28 100644 --- a/gossip/blockproc/drivermodule/driver_txs.go +++ b/gossip/blockproc/drivermodule/driver_txs.go @@ -7,10 +7,10 @@ import ( "github.com/Fantom-foundation/lachesis-base/inter/idx" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" + "github.com/Fantom-foundation/go-opera/evmcore" "github.com/Fantom-foundation/go-opera/gossip/blockproc" "github.com/Fantom-foundation/go-opera/inter" "github.com/Fantom-foundation/go-opera/inter/drivertype" @@ -32,7 +32,7 @@ func NewDriverTxListenerModule() *DriverTxListenerModule { return &DriverTxListenerModule{} } -func (m *DriverTxListenerModule) Start(block iblockproc.BlockCtx, bs iblockproc.BlockState, es iblockproc.EpochState, statedb *state.StateDB) blockproc.TxListener { +func (m *DriverTxListenerModule) Start(block iblockproc.BlockCtx, bs iblockproc.BlockState, es iblockproc.EpochState, statedb evmcore.StateDB) blockproc.TxListener { return &DriverTxListener{ block: block, es: es, @@ -45,7 +45,7 @@ type DriverTxListener struct { block iblockproc.BlockCtx es iblockproc.EpochState bs iblockproc.BlockState - statedb *state.StateDB + statedb evmcore.StateDB } type DriverTxTransactor struct{} @@ -60,7 +60,7 @@ func NewDriverTxPreTransactor() *DriverTxPreTransactor { return &DriverTxPreTransactor{} } -func InternalTxBuilder(statedb *state.StateDB) func(calldata []byte, addr common.Address) *types.Transaction { +func InternalTxBuilder(statedb evmcore.StateDB) func(calldata []byte, addr common.Address) *types.Transaction { nonce := uint64(math.MaxUint64) return func(calldata []byte, addr common.Address) *types.Transaction { if nonce == math.MaxUint64 { @@ -79,7 +79,7 @@ func maxBlockIdx(a, b idx.Block) idx.Block { return b } -func (p *DriverTxPreTransactor) PopInternalTxs(block iblockproc.BlockCtx, bs iblockproc.BlockState, es iblockproc.EpochState, sealing bool, statedb *state.StateDB) types.Transactions { +func (p *DriverTxPreTransactor) PopInternalTxs(block iblockproc.BlockCtx, bs iblockproc.BlockState, es iblockproc.EpochState, sealing bool, statedb evmcore.StateDB) types.Transactions { buildTx := InternalTxBuilder(statedb) internalTxs := make(types.Transactions, 0, 8) @@ -117,7 +117,7 @@ func (p *DriverTxPreTransactor) PopInternalTxs(block iblockproc.BlockCtx, bs ibl return internalTxs } -func (p *DriverTxTransactor) PopInternalTxs(_ iblockproc.BlockCtx, _ iblockproc.BlockState, es iblockproc.EpochState, sealing bool, statedb *state.StateDB) types.Transactions { +func (p *DriverTxTransactor) PopInternalTxs(_ iblockproc.BlockCtx, _ iblockproc.BlockState, es iblockproc.EpochState, sealing bool, statedb evmcore.StateDB) types.Transactions { buildTx := InternalTxBuilder(statedb) internalTxs := make(types.Transactions, 0, 1) // push data into Driver after epoch sealing diff --git a/gossip/blockproc/evmmodule/evm.go b/gossip/blockproc/evmmodule/evm.go index 39cb33a6d..d2a847ce6 100644 --- a/gossip/blockproc/evmmodule/evm.go +++ b/gossip/blockproc/evmmodule/evm.go @@ -5,7 +5,6 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -24,7 +23,7 @@ func New() *EVMModule { return &EVMModule{} } -func (p *EVMModule) Start(block iblockproc.BlockCtx, statedb *state.StateDB, reader evmcore.DummyChain, onNewLog func(*types.Log), net opera.Rules, evmCfg *params.ChainConfig) blockproc.EVMProcessor { +func (p *EVMModule) Start(block iblockproc.BlockCtx, statedb evmcore.StateDB, reader evmcore.DummyChain, onNewLog func(*types.Log), net opera.Rules, evmCfg *params.ChainConfig) blockproc.EVMProcessor { var prevBlockHash common.Hash if block.Idx != 0 { prevBlockHash = reader.GetHeader(common.Hash{}, uint64(block.Idx-1)).Hash @@ -44,7 +43,7 @@ func (p *EVMModule) Start(block iblockproc.BlockCtx, statedb *state.StateDB, rea type OperaEVMProcessor struct { block iblockproc.BlockCtx reader evmcore.DummyChain - statedb *state.StateDB + statedb evmcore.StateDB onNewLog func(*types.Log) net opera.Rules evmCfg *params.ChainConfig @@ -85,7 +84,7 @@ func (p *OperaEVMProcessor) Execute(txs types.Transactions) types.Receipts { // Process txs evmBlock := p.evmBlockWith(txs) - receipts, _, skipped, err := evmProcessor.Process(evmBlock, p.statedb, opera.DefaultVMConfig, &p.gasUsed, func(l *types.Log, _ *state.StateDB) { + receipts, _, skipped, err := evmProcessor.Process(evmBlock, p.statedb, opera.DefaultVMConfig, &p.gasUsed, func(l *types.Log, _ evmcore.StateDB) { // Note: l.Index is properly set before l.TxIndex += txsOffset p.onNewLog(l) diff --git a/gossip/blockproc/interface.go b/gossip/blockproc/interface.go index 6e99df010..920dd25b8 100644 --- a/gossip/blockproc/interface.go +++ b/gossip/blockproc/interface.go @@ -2,7 +2,6 @@ package blockproc import ( "github.com/Fantom-foundation/lachesis-base/inter/idx" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" @@ -20,11 +19,11 @@ type TxListener interface { } type TxListenerModule interface { - Start(block iblockproc.BlockCtx, bs iblockproc.BlockState, es iblockproc.EpochState, statedb *state.StateDB) TxListener + Start(block iblockproc.BlockCtx, bs iblockproc.BlockState, es iblockproc.EpochState, statedb evmcore.StateDB) TxListener } type TxTransactor interface { - PopInternalTxs(block iblockproc.BlockCtx, bs iblockproc.BlockState, es iblockproc.EpochState, sealing bool, statedb *state.StateDB) types.Transactions + PopInternalTxs(block iblockproc.BlockCtx, bs iblockproc.BlockState, es iblockproc.EpochState, sealing bool, statedb evmcore.StateDB) types.Transactions } type SealerProcessor interface { @@ -52,5 +51,5 @@ type EVMProcessor interface { } type EVM interface { - Start(block iblockproc.BlockCtx, statedb *state.StateDB, reader evmcore.DummyChain, onNewLog func(*types.Log), net opera.Rules, evmCfg *params.ChainConfig) EVMProcessor + Start(block iblockproc.BlockCtx, statedb evmcore.StateDB, reader evmcore.DummyChain, onNewLog func(*types.Log), net opera.Rules, evmCfg *params.ChainConfig) EVMProcessor } diff --git a/gossip/c_block_callbacks.go b/gossip/c_block_callbacks.go index 62ae6f2b1..a27cb5716 100644 --- a/gossip/c_block_callbacks.go +++ b/gossip/c_block_callbacks.go @@ -388,17 +388,18 @@ func consensusCallbackBeginBlockFn( updateLowestEpochToFill(es.Epoch, store) // Update the metrics touched during block processing - accountReadTimer.Update(statedb.AccountReads) - storageReadTimer.Update(statedb.StorageReads) - accountUpdateTimer.Update(statedb.AccountUpdates) - storageUpdateTimer.Update(statedb.StorageUpdates) - snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) - snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) - accountHashTimer.Update(statedb.AccountHashes) - storageHashTimer.Update(statedb.StorageHashes) - triehash := statedb.AccountHashes + statedb.StorageHashes - trieproc := statedb.SnapshotAccountReads + statedb.AccountReads + statedb.AccountUpdates - trieproc += statedb.SnapshotStorageReads + statedb.StorageReads + statedb.StorageUpdates + sm := statedb.Measurements() + accountReadTimer.Update(sm.AccountReads) + storageReadTimer.Update(sm.StorageReads) + accountUpdateTimer.Update(sm.AccountUpdates) + storageUpdateTimer.Update(sm.StorageUpdates) + snapshotAccountReadTimer.Update(sm.SnapshotAccountReads) + snapshotStorageReadTimer.Update(sm.SnapshotStorageReads) + accountHashTimer.Update(sm.AccountHashes) + storageHashTimer.Update(sm.StorageHashes) + triehash := sm.AccountHashes + sm.StorageHashes + trieproc := sm.SnapshotAccountReads + sm.AccountReads + sm.AccountUpdates + trieproc += sm.SnapshotStorageReads + sm.StorageReads + sm.StorageUpdates blockExecutionTimer.Update(time.Since(executionStart) - trieproc - triehash) // Update the metrics touched by new block @@ -422,10 +423,10 @@ func consensusCallbackBeginBlockFn( store.commitEVM(false) // Update the metrics touched during block commit - accountCommitTimer.Update(statedb.AccountCommits) - storageCommitTimer.Update(statedb.StorageCommits) - snapshotCommitTimer.Update(statedb.SnapshotCommits) - blockWriteTimer.Update(time.Since(commitStart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits) + accountCommitTimer.Update(sm.AccountCommits) + storageCommitTimer.Update(sm.StorageCommits) + snapshotCommitTimer.Update(sm.SnapshotCommits) + blockWriteTimer.Update(time.Since(commitStart) - sm.AccountCommits - sm.StorageCommits - sm.SnapshotCommits) blockInsertTimer.UpdateSince(start) now := time.Now() diff --git a/gossip/common_test.go b/gossip/common_test.go index c913db77b..79701b086 100644 --- a/gossip/common_test.go +++ b/gossip/common_test.go @@ -19,7 +19,6 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -373,7 +372,7 @@ func (env *testEnv) ReadOnly() *bind.CallOpts { return &bind.CallOpts{} } -func (env *testEnv) State() *state.StateDB { +func (env *testEnv) State() evmcore.StateDB { statedb, _ := env.store.evm.StateDB(env.store.GetBlockState().FinalizedStateRoot) return statedb } @@ -430,7 +429,7 @@ func (env *testEnv) HeaderByNumber(ctx context.Context, number *big.Int) (*types // callContract implements common code between normal and pending contract calls. // state is modified during execution, make sure to copy it if necessary. func (env *testEnv) callContract( - ctx context.Context, call ethereum.CallMsg, block *evmcore.EvmBlock, state *state.StateDB, + ctx context.Context, call ethereum.CallMsg, block *evmcore.EvmBlock, state evmcore.StateDB, ) ( ret []byte, usedGas uint64, failed bool, err error, ) { @@ -445,8 +444,7 @@ func (env *testEnv) callContract( call.Value = new(big.Int) } // Set infinite balance to the fake caller account. - from := state.GetOrNewStateObject(call.From) - from.SetBalance(big.NewInt(math.MaxInt64)) + state.SetBalance(call.From, big.NewInt(math.MaxInt64)) msg := callmsg{call} diff --git a/gossip/ethapi_backend.go b/gossip/ethapi_backend.go index 07d710894..8c43038a1 100644 --- a/gossip/ethapi_backend.go +++ b/gossip/ethapi_backend.go @@ -12,7 +12,6 @@ import ( "github.com/Fantom-foundation/lachesis-base/inter/idx" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" @@ -112,7 +111,7 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe } // StateAndHeaderByNumberOrHash returns evm state and block header by block number or block hash, err if not exists. -func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *evmcore.EvmHeader, error) { +func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (evmcore.StateDB, *evmcore.EvmHeader, error) { var header *evmcore.EvmHeader if number, ok := blockNrOrHash.Number(); ok && (number == rpc.LatestBlockNumber || number == rpc.PendingBlockNumber) { header = &b.state.CurrentBlock().EvmHeader @@ -316,7 +315,7 @@ func (b *EthAPIBackend) GetTd(_ common.Hash) *big.Int { return big.NewInt(0) } -func (b *EthAPIBackend) GetEVM(ctx context.Context, msg evmcore.Message, state *state.StateDB, header *evmcore.EvmHeader, vmConfig *vm.Config) (*vm.EVM, func() error, error) { +func (b *EthAPIBackend) GetEVM(ctx context.Context, msg evmcore.Message, state evmcore.StateDB, header *evmcore.EvmHeader, vmConfig *vm.Config) (*vm.EVM, func() error, error) { vmError := func() error { return nil } if vmConfig == nil { diff --git a/gossip/evm_state_reader.go b/gossip/evm_state_reader.go index d8a2464e0..bfbd9f5d4 100644 --- a/gossip/evm_state_reader.go +++ b/gossip/evm_state_reader.go @@ -6,7 +6,6 @@ import ( "github.com/Fantom-foundation/lachesis-base/hash" "github.com/Fantom-foundation/lachesis-base/inter/idx" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" @@ -138,6 +137,6 @@ func (r *EvmStateReader) getBlock(h hash.Event, n idx.Block, readTxs bool) *evmc return evmBlock } -func (r *EvmStateReader) StateAt(root common.Hash) (*state.StateDB, error) { +func (r *EvmStateReader) StateAt(root common.Hash) (evmcore.StateDB, error) { return r.store.evm.StateDB(hash.Hash(root)) } diff --git a/gossip/evmstore/apply_genesis.go b/gossip/evmstore/apply_genesis.go index 62f7bab8b..810e0ac1d 100644 --- a/gossip/evmstore/apply_genesis.go +++ b/gossip/evmstore/apply_genesis.go @@ -1,6 +1,8 @@ package evmstore import ( + "errors" + "github.com/Fantom-foundation/lachesis-base/kvdb" "github.com/Fantom-foundation/lachesis-base/kvdb/batched" @@ -9,6 +11,10 @@ import ( // ApplyGenesis writes initial state. func (s *Store) ApplyGenesis(g genesis.Genesis) (err error) { + if s.EvmDb == nil { + errors.New("genesis includes legacy MPT EVM data only") + } + batch := s.EvmDb.NewBatch() defer batch.Reset() g.RawEvmItems.ForEach(func(key, value []byte) bool { diff --git a/gossip/evmstore/store.go b/gossip/evmstore/store.go index f27969f49..151b5e093 100644 --- a/gossip/evmstore/store.go +++ b/gossip/evmstore/store.go @@ -19,6 +19,7 @@ import ( "github.com/ethereum/go-ethereum/trie" "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/Fantom-foundation/go-opera/evmcore" "github.com/Fantom-foundation/go-opera/inter/iblockproc" "github.com/Fantom-foundation/go-opera/logger" "github.com/Fantom-foundation/go-opera/topicsdb" @@ -102,6 +103,7 @@ func (s *Store) initCache() { } func (s *Store) initEVMDB() { + // NOTE: keep EvmDb nil if it is not legacy MPT format s.EvmDb = rawdb.NewDatabase( kvdb2ethdb.Wrap( nokeyiserr.Wrap( @@ -268,8 +270,9 @@ func (s *Store) Cap() { } // StateDB returns state database. -func (s *Store) StateDB(from hash.Hash) (*state.StateDB, error) { - return state.NewWithSnapLayers(common.Hash(from), s.EvmState, s.Snaps, 0) +func (s *Store) StateDB(from hash.Hash) (evmcore.StateDB, error) { + statedb, err := state.NewWithSnapLayers(common.Hash(from), s.EvmState, s.Snaps, 0) + return evmcore.ToStateDB(statedb), err } // HasStateDB returns if state database exists diff --git a/gossip/filters/api.go b/gossip/filters/api.go index 89e20e3f9..93ea5a51e 100644 --- a/gossip/filters/api.go +++ b/gossip/filters/api.go @@ -30,7 +30,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/rpc" ) @@ -69,7 +68,6 @@ func DefaultConfig() Config { type PublicFilterAPI struct { config Config backend Backend - chainDb ethdb.Database events *EventSystem filtersMu sync.Mutex filters map[rpc.ID]*filter @@ -80,7 +78,6 @@ func NewPublicFilterAPI(backend Backend, cfg Config) *PublicFilterAPI { api := &PublicFilterAPI{ config: cfg, backend: backend, - chainDb: backend.ChainDb(), events: NewEventSystem(backend), filters: make(map[rpc.ID]*filter), } diff --git a/gossip/filters/filter.go b/gossip/filters/filter.go index 5701a79d5..b4ffd7072 100644 --- a/gossip/filters/filter.go +++ b/gossip/filters/filter.go @@ -26,7 +26,6 @@ import ( "github.com/Fantom-foundation/lachesis-base/inter/idx" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" notify "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rpc" @@ -37,7 +36,6 @@ import ( ) type Backend interface { - ChainDb() ethdb.Database HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*evmcore.EvmHeader, error) HeaderByHash(ctx context.Context, blockHash common.Hash) (*evmcore.EvmHeader, error) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) @@ -59,7 +57,6 @@ type Filter struct { backend Backend config Config - db ethdb.Database addresses []common.Address topics [][]common.Hash @@ -98,7 +95,6 @@ func newFilter(backend Backend, cfg Config, addresses []common.Address, topics [ config: cfg, addresses: addresses, topics: topics, - db: backend.ChainDb(), } } diff --git a/gossip/filters/filter_system_test.go b/gossip/filters/filter_system_test.go index dcbeca3cb..89e2874bd 100644 --- a/gossip/filters/filter_system_test.go +++ b/gossip/filters/filter_system_test.go @@ -157,10 +157,17 @@ func TestBlockSubscription(t *testing.T) { backend = newTestBackend() api = NewPublicFilterAPI(backend, testConfig()) - statedb, _ = state.New(common.Hash{}, state.NewDatabase(backend.db), nil) - genesis = evmcore.MustApplyFakeGenesis(statedb, evmcore.FakeGenesisTime, map[common.Address]*big.Int{}) + stateAt = func(root common.Hash) evmcore.StateDB { + st, err := state.New(root, state.NewDatabase(backend.db), nil) + if err != nil { + panic(err) + } + return evmcore.ToStateDB(st) + } + + genesis = evmcore.MustApplyFakeGenesis(stateAt(common.Hash{}), evmcore.FakeGenesisTime, map[common.Address]*big.Int{}) chain, _, _ = evmcore.GenerateChain( - params.TestChainConfig, genesis, backend.db, 10, nil) + params.TestChainConfig, genesis, 10, stateAt, nil) chainEvents = []evmcore.ChainHeadNotify{} ) diff --git a/gossip/handler.go b/gossip/handler.go index 3da134aed..0ae91b162 100644 --- a/gossip/handler.go +++ b/gossip/handler.go @@ -238,18 +238,23 @@ func newHandler( } stateDb := h.store.EvmStore().EvmDb - var stateBloom *trie.SyncBloom - if false { - // NOTE: Construct the downloader (long sync) and its backing state bloom if fast - // sync is requested. The downloader is responsible for deallocating the state - // bloom when it's done. - // Note: we don't enable it if snap-sync is performed, since it's very heavy - // and the heal-portion of the snap sync is much lighter than fast. What we particularly - // want to avoid, is a 90%-finished (but restarted) snap-sync to begin - // indexing the entire trie - stateBloom = trie.NewSyncBloom(configBloomCache, stateDb) - } - h.snapLeecher = snapleecher.New(stateDb, stateBloom, h.removePeer) + if stateDb != nil { + var stateBloom *trie.SyncBloom + if false { + // NOTE: Construct the downloader (long sync) and its backing state bloom if fast + // sync is requested. The downloader is responsible for deallocating the state + // bloom when it's done. + // Note: we don't enable it if snap-sync is performed, since it's very heavy + // and the heal-portion of the snap sync is much lighter than fast. What we particularly + // want to avoid, is a 90%-finished (but restarted) snap-sync to begin + // indexing the entire trie + stateBloom = trie.NewSyncBloom(configBloomCache, stateDb) + } + h.snapLeecher = snapleecher.New(stateDb, stateBloom, h.removePeer) + } else { + log.Warn("Snapsync impossible because of unknown EvmDb format.") + h.config.AllowSnapsync = false + } h.dagFetcher = itemsfetcher.New(h.config.Protocol.DagFetcher, itemsfetcher.Callback{ OnlyInterested: func(ids []interface{}) []interface{} { diff --git a/gossip/service.go b/gossip/service.go index b4b49ea0a..7daa021c9 100644 --- a/gossip/service.go +++ b/gossip/service.go @@ -389,9 +389,11 @@ func MakeProtocols(svc *Service, backend *handler, disc enode.Iterator) []p2p.Pr // Protocols returns protocols the service can communicate on. func (s *Service) Protocols() []p2p.Protocol { - protos := append( - MakeProtocols(s, s.handler, s.operaDialCandidates), - snap.MakeProtocols((*snapHandler)(s.handler), s.snapDialCandidates)...) + protos := MakeProtocols(s, s.handler, s.operaDialCandidates) + if s.store.EvmStore().EvmDb != nil { + protos = append(protos, + snap.MakeProtocols((*snapHandler)(s.handler), s.snapDialCandidates)...) + } return protos } diff --git a/integration/makegenesis/genesis.go b/integration/makegenesis/genesis.go index 07aee09ab..5f4f6a071 100644 --- a/integration/makegenesis/genesis.go +++ b/integration/makegenesis/genesis.go @@ -8,9 +8,12 @@ import ( "github.com/Fantom-foundation/lachesis-base/hash" "github.com/Fantom-foundation/lachesis-base/kvdb" + "github.com/Fantom-foundation/lachesis-base/kvdb/nokeyiserr" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/rlp" "github.com/Fantom-foundation/go-opera/evmcore" @@ -19,7 +22,6 @@ import ( "github.com/Fantom-foundation/go-opera/gossip/blockproc/eventmodule" "github.com/Fantom-foundation/go-opera/gossip/blockproc/evmmodule" "github.com/Fantom-foundation/go-opera/gossip/blockproc/sealmodule" - "github.com/Fantom-foundation/go-opera/gossip/evmstore" "github.com/Fantom-foundation/go-opera/inter" "github.com/Fantom-foundation/go-opera/inter/iblockproc" "github.com/Fantom-foundation/go-opera/inter/ibr" @@ -27,14 +29,13 @@ import ( "github.com/Fantom-foundation/go-opera/opera" "github.com/Fantom-foundation/go-opera/opera/genesis" "github.com/Fantom-foundation/go-opera/opera/genesisstore" + "github.com/Fantom-foundation/go-opera/utils/adapters/kvdb2ethdb" "github.com/Fantom-foundation/go-opera/utils/iodb" ) type GenesisBuilder struct { - dbs kvdb.DBProducer - - tmpEvmStore *evmstore.Store - tmpStateDB *state.StateDB + tmpEvmDb ethdb.Database + tmpStateDB evmcore.StateDB totalSupply *big.Int @@ -63,14 +64,6 @@ func DefaultBlockProc() BlockProc { } } -func (b *GenesisBuilder) GetStateDB() *state.StateDB { - if b.tmpStateDB == nil { - tmpEvmStore := evmstore.NewStore(b.dbs, evmstore.LiteStoreConfig()) - b.tmpStateDB, _ = tmpEvmStore.StateDB(hash.Zero) - } - return b.tmpStateDB -} - func (b *GenesisBuilder) AddBalance(acc common.Address, balance *big.Int) { b.tmpStateDB.AddBalance(acc, balance) b.totalSupply.Add(b.totalSupply, balance) @@ -110,12 +103,24 @@ func (b *GenesisBuilder) CurrentHash() hash.Hash { } func NewGenesisBuilder(dbs kvdb.DBProducer) *GenesisBuilder { - tmpEvmStore := evmstore.NewStore(dbs, evmstore.LiteStoreConfig()) - statedb, _ := tmpEvmStore.StateDB(hash.Zero) + table, err := dbs.OpenDB("genesis_state") + if err != nil { + panic(err) + } + + evmDb := rawdb.NewDatabase( + kvdb2ethdb.Wrap( + nokeyiserr.Wrap( + table))) + + statedb, err := state.New(common.Hash{}, state.NewDatabase((evmDb)), nil) + if err != nil { + panic(err) + } + return &GenesisBuilder{ - dbs: dbs, - tmpEvmStore: tmpEvmStore, - tmpStateDB: statedb, + tmpEvmDb: evmDb, + tmpStateDB: evmcore.ToStateDB(statedb), totalSupply: new(big.Int), } } @@ -218,7 +223,8 @@ func (b *GenesisBuilder) ExecuteGenesisTxs(blockProc BlockProc, genesisTxs types } b.epochs = append(b.epochs, b.currentEpoch) - return b.tmpEvmStore.Commit(bs.LastBlock.Idx, bs.FinalizedStateRoot, true) + trieDB := b.tmpStateDB.Database().TrieDB() + return trieDB.Commit(evmBlock.Root, false, nil) } type memFile struct { @@ -246,7 +252,7 @@ func (b *GenesisBuilder) Build(head genesis.Header) *genesisstore.Store { return buf, nil } if name == genesisstore.EvmSection(0) { - it := b.tmpEvmStore.EvmDb.NewIterator(nil, nil) + it := b.tmpEvmDb.NewIterator(nil, nil) defer it.Release() _ = iodb.Write(buf, it) }