Skip to content

Commit

Permalink
apply statedb to evm
Browse files Browse the repository at this point in the history
  • Loading branch information
rabbitprincess committed Nov 27, 2023
1 parent 709b9f9 commit dc00d6f
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 89 deletions.
11 changes: 3 additions & 8 deletions chain/chainhandle.go
Original file line number Diff line number Diff line change
Expand Up @@ -638,8 +638,8 @@ func NewTxExecutor(execCtx context.Context, ccc consensus.ChainConsensusCluster,
return ErrInvalidBlockHeader
}
blockSnap := bState.Snapshot()

err := executeTx(execCtx, ccc, cdb, bState, tx, bi, preloadService)
evmService := evm.NewEVM(bState.GetEvmRoot(), bState.EvmStateDB)
err := executeTx(execCtx, ccc, cdb, bState, tx, bi, preloadService, evmService)
if err != nil {
logger.Error().Err(err).Str("hash", base58.Encode(tx.GetHash())).Msg("tx failed")
if err2 := bState.Rollback(blockSnap); err2 != nil {
Expand All @@ -656,7 +656,6 @@ func (e *blockExecutor) execute() error {
// Receipt must be committed unconditionally.
if !e.commitOnly {
defer contract.CloseDatabase()
defer evmService.CloseDatabase()

var preloadTx *types.Tx
nCand := len(e.txs)
Expand Down Expand Up @@ -723,10 +722,6 @@ func (e *blockExecutor) commit() error {
return err
}

if err := evmService.Commit(); err != nil {
return err
}

return nil
}

Expand Down Expand Up @@ -893,7 +888,7 @@ func resetAccount(account *state.AccountState, fee *big.Int, nonce *uint64) erro
return account.PutState()
}

func executeTx(execCtx context.Context, ccc consensus.ChainConsensusCluster, cdb contract.ChainAccessor, bs *state.BlockState, tx types.Transaction, bi *types.BlockHeaderInfo, preloadService int) error {
func executeTx(execCtx context.Context, ccc consensus.ChainConsensusCluster, cdb contract.ChainAccessor, bs *state.BlockState, tx types.Transaction, bi *types.BlockHeaderInfo, preloadService int, evmService *evm.EVM) error {
var (
txBody = tx.GetBody()
isQuirkTx = types.IsQuirkTx(tx.GetHash())
Expand Down
22 changes: 12 additions & 10 deletions chain/chainhandle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/aergoio/aergo-lib/db"
"github.com/aergoio/aergo/v2/account/key"
"github.com/aergoio/aergo/v2/contract"
"github.com/aergoio/aergo/v2/evm"
"github.com/aergoio/aergo/v2/internal/common"
"github.com/aergoio/aergo/v2/state"
"github.com/aergoio/aergo/v2/types"
Expand Down Expand Up @@ -73,43 +74,44 @@ func TestErrorInExecuteTx(t *testing.T) {
initTest(t, true)
defer deinitTest()
bs := state.NewBlockState(sdb.GetStateDB(), sdb.GetEvmStateDB())
evmService := evm.NewEVM(bs.GetEvmRoot(), bs.EvmStateDB)

tx := &types.Tx{}

err := executeTx(nil, nil, nil, bs, types.NewTransaction(tx), newTestBlockInfo(chainID), contract.ChainService)
err := executeTx(nil, nil, nil, bs, types.NewTransaction(tx), newTestBlockInfo(chainID), contract.ChainService, evmService)
assert.EqualError(t, err, types.ErrTxFormatInvalid.Error(), "execute empty tx")

tx.Body = &types.TxBody{}

err = executeTx(nil, nil, nil, bs, types.NewTransaction(tx), newTestBlockInfo(chainID), contract.ChainService)
err = executeTx(nil, nil, nil, bs, types.NewTransaction(tx), newTestBlockInfo(chainID), contract.ChainService, evmService)
assert.EqualError(t, err, types.ErrTxInvalidChainIdHash.Error(), "execute empty tx body")

tx.Body.ChainIdHash = common.Hasher(chainID)
tx.Body.Account = makeTestAddress(t)
tx.Body.Recipient = makeTestAddress(t)
err = executeTx(nil, nil, nil, bs, types.NewTransaction(tx), newTestBlockInfo(chainID), contract.ChainService)
err = executeTx(nil, nil, nil, bs, types.NewTransaction(tx), newTestBlockInfo(chainID), contract.ChainService, evmService)
assert.EqualError(t, err, types.ErrTxHasInvalidHash.Error(), "execute tx body with account")

signTestAddress(t, tx)
err = executeTx(nil, nil, nil, bs, types.NewTransaction(tx), newTestBlockInfo(chainID), contract.ChainService)
err = executeTx(nil, nil, nil, bs, types.NewTransaction(tx), newTestBlockInfo(chainID), contract.ChainService, evmService)
assert.EqualError(t, err, types.ErrTxNonceTooLow.Error(), "execute tx body with account")

tx.Body.Nonce = 1
tx.Body.Amount = new(big.Int).Add(types.StakingMinimum, types.StakingMinimum).Bytes()
signTestAddress(t, tx)
err = executeTx(nil, nil, nil, bs, types.NewTransaction(tx), newTestBlockInfo(chainID), contract.ChainService)
err = executeTx(nil, nil, nil, bs, types.NewTransaction(tx), newTestBlockInfo(chainID), contract.ChainService, evmService)
assert.EqualError(t, err, types.ErrInsufficientBalance.Error(), "execute tx body with nonce")

tx.Body.Amount = types.MaxAER.Bytes()
signTestAddress(t, tx)
err = executeTx(nil, nil, nil, bs, types.NewTransaction(tx), newTestBlockInfo(chainID), contract.ChainService)
err = executeTx(nil, nil, nil, bs, types.NewTransaction(tx), newTestBlockInfo(chainID), contract.ChainService, evmService)
assert.EqualError(t, err, types.ErrInsufficientBalance.Error(), "execute tx body with nonce")
}

func TestBasicExecuteTx(t *testing.T) {
initTest(t, true)
defer deinitTest()
bs := state.NewBlockState(sdb.GetStateDB(), sdb.GetEvmStateDB())
evmService := evm.NewEVM(bs.GetEvmRoot(), bs.EvmStateDB)

tx := &types.Tx{Body: &types.TxBody{}}

Expand All @@ -118,13 +120,13 @@ func TestBasicExecuteTx(t *testing.T) {
tx.Body.Recipient = makeTestAddress(t)
tx.Body.Nonce = 1
signTestAddress(t, tx)
err := executeTx(nil, nil, nil, bs, types.NewTransaction(tx), newTestBlockInfo(chainID), contract.ChainService)
err := executeTx(nil, nil, nil, bs, types.NewTransaction(tx), newTestBlockInfo(chainID), contract.ChainService, evmService)
assert.NoError(t, err, "execute amount 0")

tx.Body.Nonce = 2
tx.Body.Amount = new(big.Int).SetUint64(1000).Bytes()
signTestAddress(t, tx)
err = executeTx(nil, nil, nil, bs, types.NewTransaction(tx), newTestBlockInfo(chainID), contract.ChainService)
err = executeTx(nil, nil, nil, bs, types.NewTransaction(tx), newTestBlockInfo(chainID), contract.ChainService, evmService)
assert.NoError(t, err, "execute amount 1000")

tx.Body.Nonce = 3
Expand All @@ -134,6 +136,6 @@ func TestBasicExecuteTx(t *testing.T) {
tx.Body.Type = types.TxType_GOVERNANCE
tx.Body.Payload = []byte(`{"Name":"v1stake"}`)
signTestAddress(t, tx)
err = executeTx(nil, nil, nil, bs, types.NewTransaction(tx), newTestBlockInfo(chainID), contract.ChainService)
err = executeTx(nil, nil, nil, bs, types.NewTransaction(tx), newTestBlockInfo(chainID), contract.ChainService, evmService)
assert.NoError(t, err, "execute governance type")
}
9 changes: 2 additions & 7 deletions chain/chainservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ var (
ErrRecoInvalidSdbRoot = errors.New("state root of sdb is invalid")

TestDebugger *Debugger

evmService *evm.EVM
)

// Core represents a storage layer of a blockchain (chain & state DB).
Expand Down Expand Up @@ -97,10 +95,6 @@ func (core *Core) init(dbType string, dataDir string, testModeOn bool, forceRese
return err
}

contract.LoadDatabase(dataDir)
evmService = evm.NewEVM()
evmService.LoadDatabase(dataDir)

return nil
}

Expand Down Expand Up @@ -169,7 +163,6 @@ func (core *Core) Close() {
core.cdb.Close()
}
contract.CloseDatabase()
evmService.CloseDatabase()
}

// InitGenesisBlock initialize chain database and generate specified genesis block if necessary
Expand Down Expand Up @@ -877,6 +870,8 @@ func (cw *ChainWorker) Receive(context actor.Context) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
logger.Info().Msgf("evm query received for contract %s with payload %s", hex.EncodeToString(msg.Contract), hex.EncodeToString(msg.Queryinfo))

evmService := evm.NewEVMCall(cw.sdb.GetEvmRoot(), cw.sdb.GetEvmStateDB())
res, _, err := evmService.Query(nil, msg.Contract, msg.Queryinfo)
context.Respond(message.GetEVMQueryRsp{Result: res, Err: err})
case *message.GetElected:
Expand Down
89 changes: 30 additions & 59 deletions evm/evm.go
Original file line number Diff line number Diff line change
@@ -1,79 +1,40 @@
package evm

import (
"github.com/aergoio/aergo-lib/log"
"errors"

"github.com/aergoio/aergo-lib/log"
key "github.com/aergoio/aergo/v2/account/key/crypto"
"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/vm"
"github.com/ethereum/go-ethereum/core/vm/runtime"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
)

const (
rootHashKey = "roothashkey"
nullRootHash = "0x0000000000000000000000000000000000000000000000000000000000000000"
)

var (
logger = log.NewLogger("evm")
)

type EVM struct {
levelDB ethdb.Database
stateDB state.Database
ethState *state.StateDB
queryStateRoot common.Hash
prevStateRoot common.Hash
}

func NewEVM() *EVM {
evm := &EVM{}
return evm
readonly bool
ethState *state.StateDB
stateRoot common.Hash
}

func (evm *EVM) LoadDatabase(dbPath string) {
logger.Info().Msgf("opening a new levelDB for EVM")
evm.openDatabase(dbPath)

// set up state
evm.prevStateRoot = common.Hash{} // FIXME: fetch prev root state hash
item, err := evm.levelDB.Get([]byte(rootHashKey))
if err != nil && item == nil {
// start with null root
logger.Info().Msg("loaded with null root")
} else {
evm.prevStateRoot.SetBytes(item)
func NewEVM(prevStateRoot []byte, ethState *state.StateDB) *EVM {
return &EVM{
readonly: false,
stateRoot: common.BytesToHash(prevStateRoot),
ethState: ethState,
}
evm.ethState, _ = state.New(evm.prevStateRoot, evm.stateDB, nil)
if evm.ethState == nil {
logger.Error().Msgf("eth state not created")
}
logger.Info().Msgf("created eth state with root %s", evm.prevStateRoot.String())

}

func (evm *EVM) openDatabase(dbPath string) error {
evm.levelDB, _ = rawdb.NewLevelDBDatabase(dbPath, 128, 1024, "", false)
evm.stateDB = state.NewDatabase(evm.levelDB)
return nil
}

func (evm *EVM) CloseDatabase() {
logger.Info().Msgf("closing levelDB for EVM with root %s", evm.prevStateRoot.String())
evm.levelDB.Sync()
evm.levelDB.Close()
}

func (evm *EVM) Commit() error {
evm.queryStateRoot = evm.prevStateRoot
evm.prevStateRoot, _ = evm.ethState.Commit(true)
evm.levelDB.Put([]byte(rootHashKey), evm.prevStateRoot.Bytes())
evm.ethState, _ = state.New(evm.prevStateRoot, evm.stateDB, nil)
logger.Debug().Msgf("commiting eth state with root hash %s", evm.prevStateRoot.String())
return nil
func NewEVMCall(queryStateRoot []byte, ethState *state.StateDB) *EVM {
return &EVM{
readonly: true,
stateRoot: common.BytesToHash(queryStateRoot),
ethState: ethState,
}
}

func (evm *EVM) Query(originAddress []byte, contractAddress []byte, payload []byte) ([]byte, uint64, error) {
Expand All @@ -83,7 +44,7 @@ func (evm *EVM) Query(originAddress []byte, contractAddress []byte, payload []by
}

// create call cfg
queryState, _ := state.New(evm.queryStateRoot, evm.stateDB, nil)
queryState, _ := state.New(evm.stateRoot, evm.ethState.Database(), nil)
runtimeCfg := &runtime.Config{
State: queryState,
EVMConfig: evmCfg,
Expand All @@ -103,6 +64,10 @@ func (evm *EVM) Query(originAddress []byte, contractAddress []byte, payload []by
}

func (evm *EVM) Call(originAddress []byte, contractAddress []byte, payload []byte) ([]byte, uint64, error) {
if evm.readonly {
return nil, 0, errors.New("cannot call on readonly")
}

// create evmCfg
evmCfg := vm.Config{
NoBaseFee: true,
Expand All @@ -128,6 +93,10 @@ func (evm *EVM) Call(originAddress []byte, contractAddress []byte, payload []byt
}

func (evm *EVM) Create(originAddress []byte, payload []byte) ([]byte, []byte, uint64, error) {
if evm.readonly {
return nil, nil, 0, errors.New("cannot create on readonly")
}

// create evmCfg
evmCfg := vm.Config{}

Expand All @@ -150,6 +119,8 @@ func (evm *EVM) Create(originAddress []byte, payload []byte) ([]byte, []byte, ui
}

func ConvertAddress(aergoAddress []byte) []byte {
addressHash := crypto.Keccak256(aergoAddress)[:20]
return addressHash
if unCompressed := key.ConvAddressUncompressed(aergoAddress); unCompressed != nil {
return common.BytesToAddress(unCompressed).Bytes()
}
return nil
}
16 changes: 11 additions & 5 deletions state/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
ethstate "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/ethdb"
)

var (
Expand All @@ -39,7 +40,6 @@ func NewChainStateDB() *ChainStateDB {
func (sdb *ChainStateDB) Clone() *ChainStateDB {
sdb.Lock()
defer sdb.Unlock()

newSdb := &ChainStateDB{
luaStore: sdb.luaStore,
evmStore: sdb.evmStore,
Expand Down Expand Up @@ -78,11 +78,17 @@ func (sdb *ChainStateDB) Init(dbType string, dataDir string, bestBlock *types.Bl

if sdb.evmStore == nil {
dbPath := common.PathMkdirAll(dataDir, "state_evm")
testLevelDB, err := rawdb.NewLevelDBDatabase(dbPath, 128, 1024, "", false)
if err != nil {
return err

var testDB ethdb.Database
if db.ImplType(dbType) == db.MemoryImpl {
testDB = rawdb.NewMemoryDatabase()
} else {
testDB, err = rawdb.NewLevelDBDatabase(dbPath, 128, 1024, "", false)
if err != nil {
return err
}
}
sdb.evmStore = ethstate.NewDatabase(testLevelDB)
sdb.evmStore = ethstate.NewDatabase(testDB)
}

if sdb.evmStates == nil {
Expand Down

0 comments on commit dc00d6f

Please sign in to comment.