les, light: use core/state implementation of state

Also improve error reporting in ODR tests because it was impossible to
see what's wrong when the only output is "odr mismatch".
This commit is contained in:
Felix Lange 2017-04-28 11:58:23 +02:00
parent 44c5e48935
commit cc860e38e5
18 changed files with 418 additions and 1339 deletions

View file

@ -31,7 +31,6 @@ import (
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
)
@ -81,11 +80,11 @@ func (b *EthApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumb
return b.eth.blockchain.GetBlockByNumber(uint64(blockNr)), nil
}
func (b *EthApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (ethapi.State, *types.Header, error) {
func (b *EthApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
// Pending state is only known by the miner
if blockNr == rpc.PendingBlockNumber {
block, state := b.eth.miner.Pending()
return EthApiState{state}, block.Header(), nil
return state, block.Header(), nil
}
// Otherwise resolve the block number and return its state
header, err := b.HeaderByNumber(ctx, blockNr)
@ -93,7 +92,7 @@ func (b *EthApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.
return nil, nil, err
}
stateDb, err := b.eth.BlockChain().StateAt(header.Root)
return EthApiState{stateDb}, header, err
return stateDb, header, err
}
func (b *EthApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) {
@ -108,14 +107,12 @@ func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int {
return b.eth.blockchain.GetTdByHash(blockHash)
}
func (b *EthApiBackend) GetEVM(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) {
statedb := state.(EthApiState).state
from := statedb.GetOrNewStateObject(msg.From())
from.SetBalance(math.MaxBig256)
func (b *EthApiBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) {
state.SetBalance(msg.From(), math.MaxBig256)
vmError := func() error { return nil }
context := core.NewEVMContext(msg, header, b.eth.BlockChain(), nil)
return vm.NewEVM(context, statedb, b.eth.chainConfig, vmCfg), vmError, nil
return vm.NewEVM(context, state, b.eth.chainConfig, vmCfg), vmError, nil
}
func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
@ -200,23 +197,3 @@ func (b *EthApiBackend) EventMux() *event.TypeMux {
func (b *EthApiBackend) AccountManager() *accounts.Manager {
return b.eth.AccountManager()
}
type EthApiState struct {
state *state.StateDB
}
func (s EthApiState) GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) {
return s.state.GetBalance(addr), nil
}
func (s EthApiState) GetCode(ctx context.Context, addr common.Address) ([]byte, error) {
return s.state.GetCode(addr), nil
}
func (s EthApiState) GetState(ctx context.Context, a common.Address, b common.Hash) (common.Hash, error) {
return s.state.GetState(a, b), nil
}
func (s EthApiState) GetNonce(ctx context.Context, addr common.Address) (uint64, error) {
return s.state.GetNonce(addr), nil
}

View file

@ -54,14 +54,12 @@ func NewContractBackend(apiBackend ethapi.Backend) *ContractBackend {
// CodeAt retrieves any code associated with the contract from the local API.
func (b *ContractBackend) CodeAt(ctx context.Context, contract common.Address, blockNum *big.Int) ([]byte, error) {
out, err := b.bcapi.GetCode(ctx, contract, toBlockNumber(blockNum))
return common.FromHex(out), err
return b.bcapi.GetCode(ctx, contract, toBlockNumber(blockNum))
}
// CodeAt retrieves any code associated with the contract from the local API.
func (b *ContractBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
out, err := b.bcapi.GetCode(ctx, contract, rpc.PendingBlockNumber)
return common.FromHex(out), err
return b.bcapi.GetCode(ctx, contract, rpc.PendingBlockNumber)
}
// ContractCall implements bind.ContractCaller executing an Ethereum contract

View file

@ -447,8 +447,8 @@ func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Add
if state == nil || err != nil {
return nil, err
}
return state.GetBalance(ctx, address)
b := state.GetBalance(address)
return b, state.Error()
}
// GetBlockByNumber returns the requested block. When blockNr is -1 the chain head is returned. When fullTx is true all
@ -529,31 +529,25 @@ func (s *PublicBlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, bloc
}
// GetCode returns the code stored at the given address in the state for the given block number.
func (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (string, error) {
func (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
if state == nil || err != nil {
return "", err
return nil, err
}
res, err := state.GetCode(ctx, address)
if len(res) == 0 || err != nil { // backwards compatibility
return "0x", err
}
return common.ToHex(res), nil
code := state.GetCode(address)
return code, state.Error()
}
// GetStorageAt returns the storage from the state at the given address, key and
// block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block
// numbers are also allowed.
func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, key string, blockNr rpc.BlockNumber) (string, error) {
func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, key string, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
if state == nil || err != nil {
return "0x", err
return nil, err
}
res, err := state.GetState(ctx, address, common.HexToHash(key))
if err != nil {
return "0x", err
}
return res.Hex(), nil
res := state.GetState(address, common.HexToHash(key))
return res[:], state.Error()
}
// callmsg is the message type used for call transitions.
@ -978,11 +972,8 @@ func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, addr
if state == nil || err != nil {
return nil, err
}
nonce, err := state.GetNonce(ctx, address)
if err != nil {
return nil, err
}
return (*hexutil.Uint64)(&nonce), nil
nonce := state.GetNonce(address)
return (*hexutil.Uint64)(&nonce), state.Error()
}
// getTransactionBlockData fetches the meta data for the given transaction from the chain database. This is useful to

View file

@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"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/eth/downloader"
@ -47,11 +48,12 @@ type Backend interface {
SetHead(number uint64)
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error)
StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (State, *types.Header, error)
StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error)
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error)
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
GetTd(blockHash common.Hash) *big.Int
GetEVM(ctx context.Context, msg core.Message, state State, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error)
GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error)
// TxPool API
SendTx(ctx context.Context, signedTx *types.Transaction) error
RemoveTx(txHash common.Hash)
@ -65,13 +67,6 @@ type Backend interface {
CurrentBlock() *types.Block
}
type State interface {
GetBalance(ctx context.Context, addr common.Address) (*big.Int, error)
GetCode(ctx context.Context, addr common.Address) ([]byte, error)
GetState(ctx context.Context, a common.Address, b common.Hash) (common.Hash, error)
GetNonce(ctx context.Context, addr common.Address) (uint64, error)
}
func GetAPIs(apiBackend Backend) []rpc.API {
nonceLock := new(AddrLocker)
return []rpc.API{

View file

@ -24,13 +24,13 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core"
"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/eth/downloader"
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
@ -70,12 +70,12 @@ func (b *LesApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumb
return b.GetBlock(ctx, header.Hash())
}
func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (ethapi.State, *types.Header, error) {
func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
header, err := b.HeaderByNumber(ctx, blockNr)
if header == nil || err != nil {
return nil, nil, err
}
return light.NewLightState(light.StateTrieID(header), b.eth.odr), header, nil
return light.NewState(ctx, header, b.eth.odr), header, nil
}
func (b *LesApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) {
@ -90,18 +90,10 @@ func (b *LesApiBackend) GetTd(blockHash common.Hash) *big.Int {
return b.eth.blockchain.GetTdByHash(blockHash)
}
func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) {
stateDb := state.(*light.LightState).Copy()
addr := msg.From()
from, err := stateDb.GetOrNewStateObject(ctx, addr)
if err != nil {
return nil, nil, err
}
from.SetBalance(math.MaxBig256)
vmstate := light.NewVMState(ctx, stateDb)
func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) {
state.SetBalance(msg.From(), math.MaxBig256)
context := core.NewEVMContext(msg, header, b.eth.blockchain, nil)
return vm.NewEVM(context, vmstate, b.eth.chainConfig, vmCfg), vmstate.Error, nil
return vm.NewEVM(context, state, b.eth.chainConfig, vmCfg), state.Error, nil
}
func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {

View file

@ -75,24 +75,23 @@ func odrAccounts(ctx context.Context, db ethdb.Database, config *params.ChainCon
dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678")
acc := []common.Address{testBankAddress, acc1Addr, acc2Addr, dummyAddr}
var res []byte
var (
res []byte
st *state.StateDB
err error
)
for _, addr := range acc {
if bc != nil {
header := bc.GetHeaderByHash(bhash)
st, err := state.New(header.Root, state.NewDatabase(db))
if err == nil {
bal := st.GetBalance(addr)
rlp, _ := rlp.EncodeToBytes(bal)
res = append(res, rlp...)
}
st, err = state.New(header.Root, state.NewDatabase(db))
} else {
header := lc.GetHeaderByHash(bhash)
st := light.NewLightState(light.StateTrieID(header), lc.Odr())
bal, err := st.GetBalance(ctx, addr)
if err == nil {
rlp, _ := rlp.EncodeToBytes(bal)
res = append(res, rlp...)
}
st = light.NewState(ctx, header, lc.Odr())
}
if err == nil {
bal := st.GetBalance(addr)
rlp, _ := rlp.EncodeToBytes(bal)
res = append(res, rlp...)
}
}
@ -133,23 +132,15 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
}
} else {
header := lc.GetHeaderByHash(bhash)
state := light.NewLightState(light.StateTrieID(header), lc.Odr())
vmstate := light.NewVMState(ctx, state)
from, err := state.GetOrNewStateObject(ctx, testBankAddress)
if err == nil {
from.SetBalance(math.MaxBig256)
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(100000), new(big.Int), data, false)}
context := core.NewEVMContext(msg, header, lc, nil)
vmenv := vm.NewEVM(context, vmstate, config, vm.Config{})
//vmenv := light.NewEnv(ctx, state, config, lc, msg, header, vm.Config{})
gp := new(core.GasPool).AddGas(math.MaxBig256)
ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
if vmstate.Error() == nil {
res = append(res, ret...)
}
state := light.NewState(ctx, header, lc.Odr())
state.SetBalance(testBankAddress, math.MaxBig256)
msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), big.NewInt(100000), new(big.Int), data, false)}
context := core.NewEVMContext(msg, header, lc, nil)
vmenv := vm.NewEVM(context, state, config, vm.Config{})
gp := new(core.GasPool).AddGas(math.MaxBig256)
ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
if state.Error() == nil {
res = append(res, ret...)
}
}
}

View file

@ -62,7 +62,7 @@ func tfCodeAccess(db ethdb.Database, bhash common.Hash, number uint64) light.Odr
return nil
}
sti := light.StateTrieID(header)
ci := light.StorageTrieID(sti, testContractAddr, common.Hash{})
ci := light.StorageTrieID(sti, crypto.Keccak256Hash(testContractAddr[:]), common.Hash{})
return &light.CodeRequest{Id: ci, Hash: crypto.Keccak256Hash(testContractCodeDeployed)}
}

View file

@ -180,11 +180,6 @@ func (self *LightChain) Status() (td *big.Int, currentBlock common.Hash, genesis
return self.GetTd(hash, header.Number.Uint64()), hash, self.genesisBlock.Hash()
}
// State returns a new mutable state based on the current HEAD block.
func (self *LightChain) State() *LightState {
return NewLightState(StateTrieID(self.hc.CurrentHeader()), self.odr)
}
// Reset purges the entire blockchain, restoring it to its genesis state.
func (bc *LightChain) Reset() {
bc.ResetWithGenesisBlock(bc.genesisBlock)

View file

@ -34,7 +34,7 @@ import (
// service is not required.
var NoOdr = context.Background()
// OdrBackend is an interface to a backend service that handles ODR retrievals
// OdrBackend is an interface to a backend service that handles ODR retrievals type
type OdrBackend interface {
Database() ethdb.Database
Retrieve(ctx context.Context, req OdrRequest) error
@ -66,11 +66,11 @@ func StateTrieID(header *types.Header) *TrieID {
// StorageTrieID returns a TrieID for a contract storage trie at a given account
// of a given state trie. It also requires the root hash of the trie for
// checking Merkle proofs.
func StorageTrieID(state *TrieID, addr common.Address, root common.Hash) *TrieID {
func StorageTrieID(state *TrieID, addrHash, root common.Hash) *TrieID {
return &TrieID{
BlockHash: state.BlockHash,
BlockNumber: state.BlockNumber,
AccKey: crypto.Keccak256(addr[:]),
AccKey: addrHash[:],
Root: root,
}
}
@ -102,7 +102,7 @@ func storeProof(db ethdb.Database, proof []rlp.RawValue) {
// CodeRequest is the ODR request type for retrieving contract code
type CodeRequest struct {
OdrRequest
Id *TrieID
Id *TrieID // references storage trie of the account
Hash common.Hash
Data []byte
}

View file

@ -86,11 +86,11 @@ func (odr *testOdr) Retrieve(ctx context.Context, req OdrRequest) error {
return nil
}
type odrTestFn func(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) []byte
type odrTestFn func(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error)
func TestOdrGetBlockLes1(t *testing.T) { testChainOdr(t, 1, 1, odrGetBlock) }
func TestOdrGetBlockLes1(t *testing.T) { testChainOdr(t, 1, odrGetBlock) }
func odrGetBlock(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) []byte {
func odrGetBlock(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) {
var block *types.Block
if bc != nil {
block = bc.GetBlockByHash(bhash)
@ -98,15 +98,15 @@ func odrGetBlock(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc
block, _ = lc.GetBlockByHash(ctx, bhash)
}
if block == nil {
return nil
return nil, nil
}
rlp, _ := rlp.EncodeToBytes(block)
return rlp
return rlp, nil
}
func TestOdrGetReceiptsLes1(t *testing.T) { testChainOdr(t, 1, 1, odrGetReceipts) }
func TestOdrGetReceiptsLes1(t *testing.T) { testChainOdr(t, 1, odrGetReceipts) }
func odrGetReceipts(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) []byte {
func odrGetReceipts(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) {
var receipts types.Receipts
if bc != nil {
receipts = core.GetBlockReceipts(db, bhash, core.GetBlockNumber(db, bhash))
@ -114,43 +114,37 @@ func odrGetReceipts(ctx context.Context, db ethdb.Database, bc *core.BlockChain,
receipts, _ = GetBlockReceipts(ctx, lc.Odr(), bhash, core.GetBlockNumber(db, bhash))
}
if receipts == nil {
return nil
return nil, nil
}
rlp, _ := rlp.EncodeToBytes(receipts)
return rlp
return rlp, nil
}
func TestOdrAccountsLes1(t *testing.T) { testChainOdr(t, 1, 1, odrAccounts) }
func TestOdrAccountsLes1(t *testing.T) { testChainOdr(t, 1, odrAccounts) }
func odrAccounts(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) []byte {
func odrAccounts(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) {
dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678")
acc := []common.Address{testBankAddress, acc1Addr, acc2Addr, dummyAddr}
var res []byte
for _, addr := range acc {
if bc != nil {
header := bc.GetHeaderByHash(bhash)
st, err := state.New(header.Root, state.NewDatabase(db))
if err == nil {
bal := st.GetBalance(addr)
rlp, _ := rlp.EncodeToBytes(bal)
res = append(res, rlp...)
}
} else {
header := lc.GetHeaderByHash(bhash)
st := NewLightState(StateTrieID(header), lc.Odr())
bal, err := st.GetBalance(ctx, addr)
if err == nil {
rlp, _ := rlp.EncodeToBytes(bal)
res = append(res, rlp...)
}
}
var st *state.StateDB
if bc == nil {
header := lc.GetHeaderByHash(bhash)
st = NewState(ctx, header, lc.Odr())
} else {
header := bc.GetHeaderByHash(bhash)
st, _ = state.New(header.Root, state.NewDatabase(db))
}
return res
var res []byte
for _, addr := range acc {
bal := st.GetBalance(addr)
rlp, _ := rlp.EncodeToBytes(bal)
res = append(res, rlp...)
}
return res, st.Error()
}
func TestOdrContractCallLes1(t *testing.T) { testChainOdr(t, 1, 2, odrContractCall) }
func TestOdrContractCallLes1(t *testing.T) { testChainOdr(t, 1, odrContractCall) }
type callmsg struct {
types.Message
@ -158,50 +152,42 @@ type callmsg struct {
func (callmsg) CheckNonce() bool { return false }
func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) []byte {
func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) {
data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000")
config := params.TestChainConfig
var res []byte
for i := 0; i < 3; i++ {
data[35] = byte(i)
if bc != nil {
header := bc.GetHeaderByHash(bhash)
statedb, err := state.New(header.Root, state.NewDatabase(db))
if err == nil {
from := statedb.GetOrNewStateObject(testBankAddress)
from.SetBalance(math.MaxBig256)
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)}
context := core.NewEVMContext(msg, header, bc, nil)
vmenv := vm.NewEVM(context, statedb, config, vm.Config{})
gp := new(core.GasPool).AddGas(math.MaxBig256)
ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
res = append(res, ret...)
}
var (
st *state.StateDB
header *types.Header
chain core.ChainContext
)
if bc == nil {
chain = lc
header = lc.GetHeaderByHash(bhash)
st = NewState(ctx, header, lc.Odr())
} else {
header := lc.GetHeaderByHash(bhash)
state := NewLightState(StateTrieID(header), lc.Odr())
vmstate := NewVMState(ctx, state)
from, err := state.GetOrNewStateObject(ctx, testBankAddress)
if err == nil {
from.SetBalance(math.MaxBig256)
chain = bc
header = bc.GetHeaderByHash(bhash)
st, _ = state.New(header.Root, state.NewDatabase(db))
}
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)}
context := core.NewEVMContext(msg, header, lc, nil)
vmenv := vm.NewEVM(context, vmstate, config, vm.Config{})
gp := new(core.GasPool).AddGas(math.MaxBig256)
ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
if vmstate.Error() == nil {
res = append(res, ret...)
}
}
// Perform read-only call.
st.SetBalance(testBankAddress, math.MaxBig256)
msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)}
context := core.NewEVMContext(msg, header, chain, nil)
vmenv := vm.NewEVM(context, st, config, vm.Config{})
gp := new(core.GasPool).AddGas(math.MaxBig256)
ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
res = append(res, ret...)
if st.Error() != nil {
return res, st.Error()
}
}
return res
return res, nil
}
func testChainGen(i int, block *core.BlockGen) {
@ -245,7 +231,7 @@ func testChainGen(i int, block *core.BlockGen) {
}
}
func testChainOdr(t *testing.T, protocol int, expFail uint64, fn odrTestFn) {
func testChainOdr(t *testing.T, protocol int, fn odrTestFn) {
var (
evmux = new(event.TypeMux)
sdb, _ = ethdb.NewMemDatabase()
@ -258,46 +244,58 @@ func testChainOdr(t *testing.T, protocol int, expFail uint64, fn odrTestFn) {
blockchain, _ := core.NewBlockChain(sdb, params.TestChainConfig, ethash.NewFullFaker(), evmux, vm.Config{})
gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, sdb, 4, testChainGen)
if _, err := blockchain.InsertChain(gchain); err != nil {
panic(err)
t.Fatal(err)
}
odr := &testOdr{sdb: sdb, ldb: ldb}
lightchain, _ := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker(), evmux)
lightchain, err := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker(), evmux)
if err != nil {
t.Fatal(err)
}
headers := make([]*types.Header, len(gchain))
for i, block := range gchain {
headers[i] = block.Header()
}
if _, err := lightchain.InsertHeaderChain(headers, 1); err != nil {
panic(err)
t.Fatal(err)
}
test := func(expFail uint64) {
test := func(expFail int) {
for i := uint64(0); i <= blockchain.CurrentHeader().Number.Uint64(); i++ {
bhash := core.GetCanonicalHash(sdb, i)
b1 := fn(NoOdr, sdb, blockchain, nil, bhash)
b1, err := fn(NoOdr, sdb, blockchain, nil, bhash)
if err != nil {
t.Fatalf("error in full-node test for block %d: %v", i, err)
}
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
defer cancel()
b2 := fn(ctx, ldb, nil, lightchain, bhash)
exp := i < uint64(expFail)
b2, err := fn(ctx, ldb, nil, lightchain, bhash)
if err != nil && exp {
t.Errorf("error in ODR test for block %d: %v", i, err)
}
eq := bytes.Equal(b1, b2)
exp := i < expFail
if exp && !eq {
t.Errorf("odr mismatch")
}
if !exp && eq {
t.Errorf("unexpected odr match")
t.Errorf("ODR test output for block %d doesn't match full node", i)
}
}
}
odr.disable = true
// expect retrievals to fail (except genesis block) without a les peer
test(expFail)
odr.disable = false
// expect all retrievals to pass
test(5)
t.Log("checking without ODR")
odr.disable = true
test(1)
// expect all retrievals to pass with ODR enabled
t.Log("checking with ODR")
odr.disable = false
test(len(gchain))
// still expect all retrievals to pass, now data should be cached locally
test(5)
t.Log("checking without ODR, should be cached")
odr.disable = true
test(len(gchain))
}

View file

@ -106,25 +106,6 @@ func GetCanonicalHash(ctx context.Context, odr OdrBackend, number uint64) (commo
return common.Hash{}, err
}
// retrieveContractCode tries to retrieve the contract code of the given account
// with the given hash from the network (id points to the storage trie belonging
// to the same account)
func retrieveContractCode(ctx context.Context, odr OdrBackend, id *TrieID, hash common.Hash) ([]byte, error) {
if hash == sha3_nil {
return nil, nil
}
res, _ := odr.Database().Get(hash[:])
if res != nil {
return res, nil
}
r := &CodeRequest{Id: id, Hash: hash}
if err := odr.Retrieve(ctx, r); err != nil {
return nil, err
} else {
return r.Data, nil
}
}
// GetBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
func GetBodyRLP(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (rlp.RawValue, error) {
if data := core.GetBodyRLP(odr.Database(), hash, number); data != nil {

View file

@ -1,316 +0,0 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package light
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
// LightState is a memory representation of a state.
// This version is ODR capable, caching only the already accessed part of the
// state, retrieving unknown parts on-demand from the ODR backend. Changes are
// never stored in the local database, only in the memory objects.
type LightState struct {
odr OdrBackend
trie *LightTrie
id *TrieID
stateObjects map[string]*StateObject
refund *big.Int
}
// NewLightState creates a new LightState with the specified root.
// Note that the creation of a light state is always successful, even if the
// root is non-existent. In that case, ODR retrieval will always be unsuccessful
// and every operation will return with an error or wait for the context to be
// cancelled.
func NewLightState(id *TrieID, odr OdrBackend) *LightState {
var tr *LightTrie
if id != nil {
tr = NewLightTrie(id, odr, true)
}
return &LightState{
odr: odr,
trie: tr,
id: id,
stateObjects: make(map[string]*StateObject),
refund: new(big.Int),
}
}
// AddRefund adds an amount to the refund value collected during a vm execution
func (self *LightState) AddRefund(gas *big.Int) {
self.refund.Add(self.refund, gas)
}
// HasAccount returns true if an account exists at the given address
func (self *LightState) HasAccount(ctx context.Context, addr common.Address) (bool, error) {
so, err := self.GetStateObject(ctx, addr)
return so != nil, err
}
// GetBalance retrieves the balance from the given address or 0 if the account does
// not exist
func (self *LightState) GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) {
stateObject, err := self.GetStateObject(ctx, addr)
if err != nil {
return common.Big0, err
}
if stateObject != nil {
return stateObject.balance, nil
}
return common.Big0, nil
}
// GetNonce returns the nonce at the given address or 0 if the account does
// not exist
func (self *LightState) GetNonce(ctx context.Context, addr common.Address) (uint64, error) {
stateObject, err := self.GetStateObject(ctx, addr)
if err != nil {
return 0, err
}
if stateObject != nil {
return stateObject.nonce, nil
}
return 0, nil
}
// GetCode returns the contract code at the given address or nil if the account
// does not exist
func (self *LightState) GetCode(ctx context.Context, addr common.Address) ([]byte, error) {
stateObject, err := self.GetStateObject(ctx, addr)
if err != nil {
return nil, err
}
if stateObject != nil {
return stateObject.code, nil
}
return nil, nil
}
// GetState returns the contract storage value at storage address b from the
// contract address a or common.Hash{} if the account does not exist
func (self *LightState) GetState(ctx context.Context, a common.Address, b common.Hash) (common.Hash, error) {
stateObject, err := self.GetStateObject(ctx, a)
if err == nil && stateObject != nil {
return stateObject.GetState(ctx, b)
}
return common.Hash{}, err
}
// HasSuicided returns true if the given account has been marked for deletion
// or false if the account does not exist
func (self *LightState) HasSuicided(ctx context.Context, addr common.Address) (bool, error) {
stateObject, err := self.GetStateObject(ctx, addr)
if err == nil && stateObject != nil {
return stateObject.remove, nil
}
return false, err
}
/*
* SETTERS
*/
// AddBalance adds the given amount to the balance of the specified account
func (self *LightState) AddBalance(ctx context.Context, addr common.Address, amount *big.Int) error {
stateObject, err := self.GetOrNewStateObject(ctx, addr)
if err == nil && stateObject != nil {
stateObject.AddBalance(amount)
}
return err
}
// SubBalance adds the given amount to the balance of the specified account
func (self *LightState) SubBalance(ctx context.Context, addr common.Address, amount *big.Int) error {
stateObject, err := self.GetOrNewStateObject(ctx, addr)
if err == nil && stateObject != nil {
stateObject.SubBalance(amount)
}
return err
}
// SetNonce sets the nonce of the specified account
func (self *LightState) SetNonce(ctx context.Context, addr common.Address, nonce uint64) error {
stateObject, err := self.GetOrNewStateObject(ctx, addr)
if err == nil && stateObject != nil {
stateObject.SetNonce(nonce)
}
return err
}
// SetCode sets the contract code at the specified account
func (self *LightState) SetCode(ctx context.Context, addr common.Address, code []byte) error {
stateObject, err := self.GetOrNewStateObject(ctx, addr)
if err == nil && stateObject != nil {
stateObject.SetCode(crypto.Keccak256Hash(code), code)
}
return err
}
// SetState sets the storage value at storage address key of the account addr
func (self *LightState) SetState(ctx context.Context, addr common.Address, key common.Hash, value common.Hash) error {
stateObject, err := self.GetOrNewStateObject(ctx, addr)
if err == nil && stateObject != nil {
stateObject.SetState(key, value)
}
return err
}
// Delete marks an account to be removed and clears its balance
func (self *LightState) Suicide(ctx context.Context, addr common.Address) (bool, error) {
stateObject, err := self.GetOrNewStateObject(ctx, addr)
if err == nil && stateObject != nil {
stateObject.MarkForDeletion()
stateObject.balance = new(big.Int)
return true, nil
}
return false, err
}
//
// Get, set, new state object methods
//
// GetStateObject returns the state object of the given account or nil if the
// account does not exist
func (self *LightState) GetStateObject(ctx context.Context, addr common.Address) (stateObject *StateObject, err error) {
stateObject = self.stateObjects[addr.Str()]
if stateObject != nil {
if stateObject.deleted {
stateObject = nil
}
return stateObject, nil
}
data, err := self.trie.Get(ctx, addr[:])
if err != nil {
return nil, err
}
if len(data) == 0 {
return nil, nil
}
stateObject, err = DecodeObject(ctx, self.id, addr, self.odr, []byte(data))
if err != nil {
return nil, err
}
self.SetStateObject(stateObject)
return stateObject, nil
}
// SetStateObject sets the state object of the given account
func (self *LightState) SetStateObject(object *StateObject) {
self.stateObjects[object.Address().Str()] = object
}
// GetOrNewStateObject returns the state object of the given account or creates a
// new one if the account does not exist
func (self *LightState) GetOrNewStateObject(ctx context.Context, addr common.Address) (*StateObject, error) {
stateObject, err := self.GetStateObject(ctx, addr)
if err == nil && (stateObject == nil || stateObject.deleted) {
stateObject, err = self.CreateStateObject(ctx, addr)
}
return stateObject, err
}
// newStateObject creates a state object whether it exists in the state or not
func (self *LightState) newStateObject(addr common.Address) *StateObject {
stateObject := NewStateObject(addr, self.odr)
self.stateObjects[addr.Str()] = stateObject
return stateObject
}
// CreateStateObject creates creates a new state object and takes ownership.
// This is different from "NewStateObject"
func (self *LightState) CreateStateObject(ctx context.Context, addr common.Address) (*StateObject, error) {
// Get previous (if any)
so, err := self.GetStateObject(ctx, addr)
if err != nil {
return nil, err
}
// Create a new one
newSo := self.newStateObject(addr)
// If it existed set the balance to the new account
if so != nil {
newSo.balance = so.balance
}
return newSo, nil
}
// ForEachStorage calls a callback function for every key/value pair found
// in the local storage cache. Note that unlike core/state.StateObject,
// light.StateObject only returns cached values and doesn't download the
// entire storage tree.
func (self *LightState) ForEachStorage(ctx context.Context, addr common.Address, cb func(key, value common.Hash) bool) error {
so, err := self.GetStateObject(ctx, addr)
if err != nil {
return err
}
if so == nil {
return nil
}
for h, v := range so.storage {
cb(h, v)
}
return nil
}
//
// Setting, copying of the state methods
//
// Copy creates a copy of the state
func (self *LightState) Copy() *LightState {
// ignore error - we assume state-to-be-copied always exists
state := NewLightState(nil, self.odr)
state.trie = self.trie
state.id = self.id
for k, stateObject := range self.stateObjects {
if stateObject.dirty {
state.stateObjects[k] = stateObject.Copy()
}
}
state.refund.Set(self.refund)
return state
}
// Set copies the contents of the given state onto this state, overwriting
// its contents
func (self *LightState) Set(state *LightState) {
self.trie = state.trie
self.stateObjects = state.stateObjects
self.refund = state.refund
}
// GetRefund returns the refund value collected during a vm execution
func (self *LightState) GetRefund() *big.Int {
return self.refund
}

View file

@ -1,275 +0,0 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package light
import (
"bytes"
"context"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
)
var emptyCodeHash = crypto.Keccak256(nil)
// Code represents a contract code in binary form
type Code []byte
// String returns a string representation of the code
func (self Code) String() string {
return string(self) //strings.Join(Disassemble(self), " ")
}
// Storage is a memory map cache of a contract storage
type Storage map[common.Hash]common.Hash
// String returns a string representation of the storage cache
func (self Storage) String() (str string) {
for key, value := range self {
str += fmt.Sprintf("%X : %X\n", key, value)
}
return
}
// Copy copies the contents of a storage cache
func (self Storage) Copy() Storage {
cpy := make(Storage)
for key, value := range self {
cpy[key] = value
}
return cpy
}
// StateObject is a memory representation of an account or contract and its storage.
// This version is ODR capable, caching only the already accessed part of the
// storage, retrieving unknown parts on-demand from the ODR backend. Changes are
// never stored in the local database, only in the memory objects.
type StateObject struct {
odr OdrBackend
trie *LightTrie
// Address belonging to this account
address common.Address
// The balance of the account
balance *big.Int
// The nonce of the account
nonce uint64
// The code hash if code is present (i.e. a contract)
codeHash []byte
// The code for this account
code Code
// Cached storage (flushed when updated)
storage Storage
// Mark for deletion
// When an object is marked for deletion it will be delete from the trie
// during the "update" phase of the state transition
remove bool
deleted bool
dirty bool
}
// NewStateObject creates a new StateObject of the specified account address
func NewStateObject(address common.Address, odr OdrBackend) *StateObject {
object := &StateObject{
odr: odr,
address: address,
balance: new(big.Int),
dirty: true,
codeHash: emptyCodeHash,
storage: make(Storage),
}
object.trie = NewLightTrie(&TrieID{}, odr, true)
return object
}
// MarkForDeletion marks an account to be removed
func (self *StateObject) MarkForDeletion() {
self.remove = true
self.dirty = true
}
// getAddr gets the storage value at the given address from the trie
func (c *StateObject) getAddr(ctx context.Context, addr common.Hash) (common.Hash, error) {
var ret []byte
val, err := c.trie.Get(ctx, addr[:])
if err != nil {
return common.Hash{}, err
}
rlp.DecodeBytes(val, &ret)
return common.BytesToHash(ret), nil
}
// Storage returns the storage cache object of the account
func (self *StateObject) Storage() Storage {
return self.storage
}
// GetState returns the storage value at the given address from either the cache
// or the trie
func (self *StateObject) GetState(ctx context.Context, key common.Hash) (common.Hash, error) {
value, exists := self.storage[key]
if !exists {
var err error
value, err = self.getAddr(ctx, key)
if err != nil {
return common.Hash{}, err
}
if (value != common.Hash{}) {
self.storage[key] = value
}
}
return value, nil
}
// SetState sets the storage value at the given address
func (self *StateObject) SetState(k, value common.Hash) {
self.storage[k] = value
self.dirty = true
}
// AddBalance adds the given amount to the account balance
func (c *StateObject) AddBalance(amount *big.Int) {
c.SetBalance(new(big.Int).Add(c.balance, amount))
}
// SubBalance subtracts the given amount from the account balance
func (c *StateObject) SubBalance(amount *big.Int) {
c.SetBalance(new(big.Int).Sub(c.balance, amount))
}
// SetBalance sets the account balance to the given amount
func (c *StateObject) SetBalance(amount *big.Int) {
c.balance = amount
c.dirty = true
}
// ReturnGas returns the gas back to the origin. Used by the Virtual machine or Closures
func (c *StateObject) ReturnGas(gas *big.Int) {}
// Copy creates a copy of the state object
func (self *StateObject) Copy() *StateObject {
stateObject := NewStateObject(self.Address(), self.odr)
stateObject.balance.Set(self.balance)
stateObject.codeHash = common.CopyBytes(self.codeHash)
stateObject.nonce = self.nonce
stateObject.trie = self.trie
stateObject.code = self.code
stateObject.storage = self.storage.Copy()
stateObject.remove = self.remove
stateObject.dirty = self.dirty
stateObject.deleted = self.deleted
return stateObject
}
//
// Attribute accessors
//
// empty returns whether the account is considered empty.
func (self *StateObject) empty() bool {
return self.nonce == 0 && self.balance.Sign() == 0 && bytes.Equal(self.codeHash, emptyCodeHash)
}
// Balance returns the account balance
func (self *StateObject) Balance() *big.Int {
return self.balance
}
// Address returns the address of the contract/account
func (self *StateObject) Address() common.Address {
return self.address
}
// Code returns the contract code
func (self *StateObject) Code() []byte {
return self.code
}
// SetCode sets the contract code
func (self *StateObject) SetCode(hash common.Hash, code []byte) {
self.code = code
self.codeHash = hash[:]
self.dirty = true
}
// SetNonce sets the account nonce
func (self *StateObject) SetNonce(nonce uint64) {
self.nonce = nonce
self.dirty = true
}
// Nonce returns the account nonce
func (self *StateObject) Nonce() uint64 {
return self.nonce
}
// ForEachStorage calls a callback function for every key/value pair found
// in the local storage cache. Note that unlike core/state.StateObject,
// light.StateObject only returns cached values and doesn't download the
// entire storage tree.
func (self *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) {
for h, v := range self.storage {
cb(h, v)
}
}
// Never called, but must be present to allow StateObject to be used
// as a vm.Account interface that also satisfies the vm.ContractRef
// interface. Interfaces are awesome.
func (self *StateObject) Value() *big.Int {
panic("Value on StateObject should never be called")
}
// Encoding
type extStateObject struct {
Nonce uint64
Balance *big.Int
Root common.Hash
CodeHash []byte
}
// DecodeObject decodes an RLP-encoded state object.
func DecodeObject(ctx context.Context, stateID *TrieID, address common.Address, odr OdrBackend, data []byte) (*StateObject, error) {
var (
obj = &StateObject{address: address, odr: odr, storage: make(Storage)}
ext extStateObject
err error
)
if err = rlp.DecodeBytes(data, &ext); err != nil {
return nil, err
}
trieID := StorageTrieID(stateID, address, ext.Root)
obj.trie = NewLightTrie(trieID, odr, true)
if !bytes.Equal(ext.CodeHash, emptyCodeHash) {
if obj.code, err = retrieveContractCode(ctx, obj.odr, trieID, common.BytesToHash(ext.CodeHash)); err != nil {
return nil, fmt.Errorf("can't find code for hash %x: %v", ext.CodeHash, err)
}
}
obj.nonce = ext.Nonce
obj.balance = ext.Balance
obj.codeHash = ext.CodeHash
return obj, nil
}

View file

@ -1,248 +0,0 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package light
import (
"bytes"
"context"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
)
func makeTestState() (common.Hash, ethdb.Database) {
sdb, _ := ethdb.NewMemDatabase()
st, _ := state.New(common.Hash{}, state.NewDatabase(sdb))
for i := byte(0); i < 100; i++ {
addr := common.Address{i}
for j := byte(0); j < 100; j++ {
st.SetState(addr, common.Hash{j}, common.Hash{i, j})
}
st.SetNonce(addr, 100)
st.AddBalance(addr, big.NewInt(int64(i)))
st.SetCode(addr, []byte{i, i, i})
}
root, _ := st.CommitTo(sdb, false)
return root, sdb
}
func TestLightStateOdr(t *testing.T) {
root, sdb := makeTestState()
header := &types.Header{Root: root, Number: big.NewInt(0)}
core.WriteHeader(sdb, header)
ldb, _ := ethdb.NewMemDatabase()
odr := &testOdr{sdb: sdb, ldb: ldb}
ls := NewLightState(StateTrieID(header), odr)
ctx := context.Background()
for i := byte(0); i < 100; i++ {
addr := common.Address{i}
err := ls.AddBalance(ctx, addr, big.NewInt(1000))
if err != nil {
t.Fatalf("Error adding balance to acc[%d]: %v", i, err)
}
err = ls.SetState(ctx, addr, common.Hash{100}, common.Hash{i, 100})
if err != nil {
t.Fatalf("Error setting storage of acc[%d]: %v", i, err)
}
}
addr := common.Address{100}
_, err := ls.CreateStateObject(ctx, addr)
if err != nil {
t.Fatalf("Error creating state object: %v", err)
}
err = ls.SetCode(ctx, addr, []byte{100, 100, 100})
if err != nil {
t.Fatalf("Error setting code: %v", err)
}
err = ls.AddBalance(ctx, addr, big.NewInt(1100))
if err != nil {
t.Fatalf("Error adding balance to acc[100]: %v", err)
}
for j := byte(0); j < 101; j++ {
err = ls.SetState(ctx, addr, common.Hash{j}, common.Hash{100, j})
if err != nil {
t.Fatalf("Error setting storage of acc[100]: %v", err)
}
}
err = ls.SetNonce(ctx, addr, 100)
if err != nil {
t.Fatalf("Error setting nonce for acc[100]: %v", err)
}
for i := byte(0); i < 101; i++ {
addr := common.Address{i}
bal, err := ls.GetBalance(ctx, addr)
if err != nil {
t.Fatalf("Error getting balance of acc[%d]: %v", i, err)
}
if bal.Int64() != int64(i)+1000 {
t.Fatalf("Incorrect balance at acc[%d]: expected %v, got %v", i, int64(i)+1000, bal.Int64())
}
nonce, err := ls.GetNonce(ctx, addr)
if err != nil {
t.Fatalf("Error getting nonce of acc[%d]: %v", i, err)
}
if nonce != 100 {
t.Fatalf("Incorrect nonce at acc[%d]: expected %v, got %v", i, 100, nonce)
}
code, err := ls.GetCode(ctx, addr)
exp := []byte{i, i, i}
if err != nil {
t.Fatalf("Error getting code of acc[%d]: %v", i, err)
}
if !bytes.Equal(code, exp) {
t.Fatalf("Incorrect code at acc[%d]: expected %v, got %v", i, exp, code)
}
for j := byte(0); j < 101; j++ {
exp := common.Hash{i, j}
val, err := ls.GetState(ctx, addr, common.Hash{j})
if err != nil {
t.Fatalf("Error retrieving acc[%d].storage[%d]: %v", i, j, err)
}
if val != exp {
t.Fatalf("Retrieved wrong value from acc[%d].storage[%d]: expected %04x, got %04x", i, j, exp, val)
}
}
}
}
func TestLightStateSetCopy(t *testing.T) {
root, sdb := makeTestState()
header := &types.Header{Root: root, Number: big.NewInt(0)}
core.WriteHeader(sdb, header)
ldb, _ := ethdb.NewMemDatabase()
odr := &testOdr{sdb: sdb, ldb: ldb}
ls := NewLightState(StateTrieID(header), odr)
ctx := context.Background()
for i := byte(0); i < 100; i++ {
addr := common.Address{i}
err := ls.AddBalance(ctx, addr, big.NewInt(1000))
if err != nil {
t.Fatalf("Error adding balance to acc[%d]: %v", i, err)
}
err = ls.SetState(ctx, addr, common.Hash{100}, common.Hash{i, 100})
if err != nil {
t.Fatalf("Error setting storage of acc[%d]: %v", i, err)
}
}
ls2 := ls.Copy()
for i := byte(0); i < 100; i++ {
addr := common.Address{i}
err := ls2.AddBalance(ctx, addr, big.NewInt(1000))
if err != nil {
t.Fatalf("Error adding balance to acc[%d]: %v", i, err)
}
err = ls2.SetState(ctx, addr, common.Hash{100}, common.Hash{i, 200})
if err != nil {
t.Fatalf("Error setting storage of acc[%d]: %v", i, err)
}
}
lsx := ls.Copy()
ls.Set(ls2)
ls2.Set(lsx)
for i := byte(0); i < 100; i++ {
addr := common.Address{i}
// check balance in ls
bal, err := ls.GetBalance(ctx, addr)
if err != nil {
t.Fatalf("Error getting balance to acc[%d]: %v", i, err)
}
if bal.Int64() != int64(i)+2000 {
t.Fatalf("Incorrect balance at ls.acc[%d]: expected %v, got %v", i, int64(i)+1000, bal.Int64())
}
// check balance in ls2
bal, err = ls2.GetBalance(ctx, addr)
if err != nil {
t.Fatalf("Error getting balance to acc[%d]: %v", i, err)
}
if bal.Int64() != int64(i)+1000 {
t.Fatalf("Incorrect balance at ls.acc[%d]: expected %v, got %v", i, int64(i)+1000, bal.Int64())
}
// check storage in ls
exp := common.Hash{i, 200}
val, err := ls.GetState(ctx, addr, common.Hash{100})
if err != nil {
t.Fatalf("Error retrieving acc[%d].storage[100]: %v", i, err)
}
if val != exp {
t.Fatalf("Retrieved wrong value from acc[%d].storage[100]: expected %04x, got %04x", i, exp, val)
}
// check storage in ls2
exp = common.Hash{i, 100}
val, err = ls2.GetState(ctx, addr, common.Hash{100})
if err != nil {
t.Fatalf("Error retrieving acc[%d].storage[100]: %v", i, err)
}
if val != exp {
t.Fatalf("Retrieved wrong value from acc[%d].storage[100]: expected %04x, got %04x", i, exp, val)
}
}
}
func TestLightStateDelete(t *testing.T) {
root, sdb := makeTestState()
header := &types.Header{Root: root, Number: big.NewInt(0)}
core.WriteHeader(sdb, header)
ldb, _ := ethdb.NewMemDatabase()
odr := &testOdr{sdb: sdb, ldb: ldb}
ls := NewLightState(StateTrieID(header), odr)
ctx := context.Background()
addr := common.Address{42}
b, err := ls.HasAccount(ctx, addr)
if err != nil {
t.Fatalf("HasAccount error: %v", err)
}
if !b {
t.Fatalf("HasAccount returned false, expected true")
}
b, err = ls.HasSuicided(ctx, addr)
if err != nil {
t.Fatalf("HasSuicided error: %v", err)
}
if b {
t.Fatalf("HasSuicided returned true, expected false")
}
ls.Suicide(ctx, addr)
b, err = ls.HasSuicided(ctx, addr)
if err != nil {
t.Fatalf("HasSuicided error: %v", err)
}
if !b {
t.Fatalf("HasSuicided returned false, expected true")
}
}

View file

@ -18,99 +18,216 @@ package light
import (
"context"
"fmt"
"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/ethdb"
"github.com/ethereum/go-ethereum/trie"
)
// LightTrie is an ODR-capable wrapper around trie.SecureTrie
type LightTrie struct {
trie *trie.SecureTrie
id *TrieID
odr OdrBackend
db ethdb.Database
func NewState(ctx context.Context, head *types.Header, odr OdrBackend) *state.StateDB {
state, _ := state.New(head.Root, NewStateDatabase(ctx, head, odr))
return state
}
// NewLightTrie creates a new LightTrie instance. It doesn't instantly try to
// access the db or network and retrieve the root node, it only initializes its
// encapsulated SecureTrie at the first actual operation.
func NewLightTrie(id *TrieID, odr OdrBackend, useFakeMap bool) *LightTrie {
return &LightTrie{
// SecureTrie is initialized before first request
id: id,
odr: odr,
db: odr.Database(),
func NewStateDatabase(ctx context.Context, head *types.Header, odr OdrBackend) state.Database {
return &odrDatabase{ctx, StateTrieID(head), odr}
}
type odrDatabase struct {
ctx context.Context
id *TrieID
backend OdrBackend
}
func (db *odrDatabase) OpenTrie(root common.Hash) (state.Trie, error) {
return &odrTrie{db: db, id: db.id}, nil
}
func (db *odrDatabase) OpenStorageTrie(addrHash, root common.Hash) (state.Trie, error) {
return &odrTrie{db: db, id: StorageTrieID(db.id, addrHash, root)}, nil
}
func (db *odrDatabase) CopyTrie(t state.Trie) state.Trie {
switch t := t.(type) {
case *odrTrie:
cpy := &odrTrie{db: t.db, id: t.id}
if t.trie != nil {
cpytrie := *t.trie
cpy.trie = &cpytrie
}
return cpy
default:
panic(fmt.Errorf("unknown trie type %T", t))
}
}
// retrieveKey retrieves a single key, returns true and stores nodes in local
// database if successful
func (t *LightTrie) retrieveKey(ctx context.Context, key []byte) bool {
r := &TrieRequest{Id: t.id, Key: crypto.Keccak256(key)}
return t.odr.Retrieve(ctx, r) == nil
func (db *odrDatabase) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) {
if codeHash == sha3_nil {
return nil, nil
}
if code, err := db.backend.Database().Get(codeHash[:]); err == nil {
return code, nil
}
id := *db.id
id.AccKey = addrHash[:]
req := &CodeRequest{Id: &id, Hash: codeHash}
err := db.backend.Retrieve(db.ctx, req)
return req.Data, err
}
func (db *odrDatabase) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) {
code, err := db.ContractCode(addrHash, codeHash)
return len(code), err
}
type odrTrie struct {
db *odrDatabase
id *TrieID
trie *trie.Trie
}
func (t *odrTrie) TryGet(key []byte) ([]byte, error) {
key = crypto.Keccak256(key)
var res []byte
err := t.do(key, func() (err error) {
res, err = t.trie.TryGet(key)
return err
})
return res, err
}
func (t *odrTrie) TryUpdate(key, value []byte) error {
key = crypto.Keccak256(key)
return t.do(key, func() error {
return t.trie.TryDelete(key)
})
}
func (t *odrTrie) TryDelete(key []byte) error {
key = crypto.Keccak256(key)
return t.do(key, func() error {
return t.trie.TryDelete(key)
})
}
func (t *odrTrie) CommitTo(db trie.DatabaseWriter) (common.Hash, error) {
if t.trie == nil {
return t.id.Root, nil
}
return t.trie.CommitTo(db)
}
func (t *odrTrie) Hash() common.Hash {
if t.trie == nil {
return t.id.Root
}
return t.trie.Hash()
}
func (t *odrTrie) NodeIterator(startkey []byte) trie.NodeIterator {
return newNodeIterator(t, startkey)
}
func (t *odrTrie) GetKey(sha []byte) []byte {
return nil
}
// do tries and retries to execute a function until it returns with no error or
// an error type other than MissingNodeError
func (t *LightTrie) do(ctx context.Context, key []byte, fn func() error) error {
err := fn()
for err != nil {
func (t *odrTrie) do(key []byte, fn func() error) error {
for {
var err error
if t.trie == nil {
t.trie, err = trie.New(t.id.Root, t.db.backend.Database())
}
if err == nil {
err = fn()
}
if _, ok := err.(*trie.MissingNodeError); !ok {
return err
}
if !t.retrieveKey(ctx, key) {
break
r := &TrieRequest{Id: t.id, Key: key}
if err := t.db.backend.Retrieve(t.db.ctx, r); err != nil {
return fmt.Errorf("can't fetch trie key %x: %v", key, err)
}
err = fn()
}
return err
}
// Get returns the value for key stored in the trie.
// The value bytes must not be modified by the caller.
func (t *LightTrie) Get(ctx context.Context, key []byte) (res []byte, err error) {
err = t.do(ctx, key, func() (err error) {
if t.trie == nil {
t.trie, err = trie.NewSecure(t.id.Root, t.db, 0)
}
if err == nil {
res, err = t.trie.TryGet(key)
}
return
})
return
type nodeIterator struct {
trie.NodeIterator
t *odrTrie
err error
}
// Update associates key with value in the trie. Subsequent calls to
// Get will return value. If value has length zero, any existing value
// is deleted from the trie and calls to Get will return nil.
//
// The value bytes must not be modified by the caller while they are
// stored in the trie.
func (t *LightTrie) Update(ctx context.Context, key, value []byte) (err error) {
err = t.do(ctx, key, func() (err error) {
if t.trie == nil {
t.trie, err = trie.NewSecure(t.id.Root, t.db, 0)
}
if err == nil {
err = t.trie.TryUpdate(key, value)
}
return
func newNodeIterator(t *odrTrie, startkey []byte) trie.NodeIterator {
it := &nodeIterator{t: t}
// Open the actual non-ODR trie if that hasn't happened yet.
if t.trie == nil {
it.do(func() error {
t, err := trie.New(t.id.Root, t.db.backend.Database())
if err == nil {
it.t.trie = t
}
return err
})
}
it.do(func() error {
it.NodeIterator = it.t.trie.NodeIterator(startkey)
return it.NodeIterator.Error()
})
return
return it
}
// Delete removes any existing value for key from the trie.
func (t *LightTrie) Delete(ctx context.Context, key []byte) (err error) {
err = t.do(ctx, key, func() (err error) {
if t.trie == nil {
t.trie, err = trie.NewSecure(t.id.Root, t.db, 0)
}
if err == nil {
err = t.trie.TryDelete(key)
}
return
func (it *nodeIterator) Next(descend bool) bool {
var ok bool
it.do(func() error {
ok = it.NodeIterator.Next(descend)
return it.NodeIterator.Error()
})
return
return ok
}
// do runs fn and attempts to fill in missing nodes by retrieving.
func (it *nodeIterator) do(fn func() error) {
var lasthash common.Hash
for {
it.err = fn()
missing, ok := it.err.(*trie.MissingNodeError)
if !ok {
return
}
if missing.NodeHash == lasthash {
it.err = fmt.Errorf("retrieve loop for trie node %x", missing.NodeHash)
return
}
lasthash = missing.NodeHash
r := &TrieRequest{Id: it.t.id, Key: nibblesToKey(missing.Path)}
if it.err = it.t.db.backend.Retrieve(it.t.db.ctx, r); it.err != nil {
return
}
}
}
func (it *nodeIterator) Error() error {
if it.err != nil {
return it.err
}
return it.NodeIterator.Error()
}
func nibblesToKey(nib []byte) []byte {
if len(nib) > 0 && nib[len(nib)-1] == 0x10 {
nib = nib[:len(nib)-1] // drop terminator
}
if len(nib)&1 == 1 {
nib = append(nib, 0) // make even
}
key := make([]byte, len(nib)/2)
for bi, ni := 0, 0; ni < len(nib); bi, ni = bi+1, ni+2 {
key[bi] = nib[ni]<<4 | nib[ni+1]
}
return key
}

83
light/trie_test.go Normal file
View file

@ -0,0 +1,83 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package light
import (
"bytes"
"context"
"fmt"
"testing"
"github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum/consensus/ethash"
"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/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
)
func TestNodeIterator(t *testing.T) {
var (
fulldb, _ = ethdb.NewMemDatabase()
lightdb, _ = ethdb.NewMemDatabase()
gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}}
genesis = gspec.MustCommit(fulldb)
)
gspec.MustCommit(lightdb)
blockchain, _ := core.NewBlockChain(fulldb, params.TestChainConfig, ethash.NewFullFaker(), new(event.TypeMux), vm.Config{})
gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, fulldb, 4, testChainGen)
if _, err := blockchain.InsertChain(gchain); err != nil {
panic(err)
}
ctx := context.Background()
odr := &testOdr{sdb: fulldb, ldb: lightdb}
head := blockchain.CurrentHeader()
lightTrie, _ := NewStateDatabase(ctx, head, odr).OpenTrie(head.Root)
fullTrie, _ := state.NewDatabase(fulldb).OpenTrie(head.Root)
if err := diffTries(fullTrie, lightTrie); err != nil {
t.Fatal(err)
}
}
func diffTries(t1, t2 state.Trie) error {
i1 := trie.NewIterator(t1.NodeIterator(nil))
i2 := trie.NewIterator(t2.NodeIterator(nil))
for i1.Next() && i2.Next() {
if !bytes.Equal(i1.Key, i2.Key) {
spew.Dump(i2)
return fmt.Errorf("tries have different keys %x, %x", i1.Key, i2.Key)
}
if !bytes.Equal(i2.Value, i2.Value) {
return fmt.Errorf("tries differ at key %x", i1.Key)
}
}
switch {
case i1.Err != nil:
return fmt.Errorf("full trie iterator error: %v", i1.Err)
case i2.Err != nil:
return fmt.Errorf("light trie iterator error: %v", i1.Err)
case i1.Next():
return fmt.Errorf("full trie iterator has more k/v pairs")
case i2.Next():
return fmt.Errorf("light trie iterator has more k/v pairs")
}
return nil
}

View file

@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"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/event"
@ -100,17 +101,18 @@ func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, chain *Light
}
// currentState returns the light state of the current head header
func (pool *TxPool) currentState() *LightState {
return NewLightState(StateTrieID(pool.chain.CurrentHeader()), pool.odr)
func (pool *TxPool) currentState(ctx context.Context) *state.StateDB {
return NewState(ctx, pool.chain.CurrentHeader(), pool.odr)
}
// GetNonce returns the "pending" nonce of a given address. It always queries
// the nonce belonging to the latest header too in order to detect if another
// client using the same key sent a transaction.
func (pool *TxPool) GetNonce(ctx context.Context, addr common.Address) (uint64, error) {
nonce, err := pool.currentState().GetNonce(ctx, addr)
if err != nil {
return 0, err
state := pool.currentState(ctx)
nonce := state.GetNonce(addr)
if state.Error() != nil {
return 0, state.Error()
}
sn, ok := pool.nonce[addr]
if ok && sn > nonce {
@ -357,13 +359,9 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error
return core.ErrInvalidSender
}
// Last but not least check for nonce errors
currentState := pool.currentState()
if n, err := currentState.GetNonce(ctx, from); err == nil {
if n > tx.Nonce() {
return core.ErrNonceTooLow
}
} else {
return err
currentState := pool.currentState(ctx)
if n := currentState.GetNonce(from); n > tx.Nonce() {
return core.ErrNonceTooLow
}
// Check the transaction doesn't exceed the current
@ -382,12 +380,8 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error
// Transactor should have enough funds to cover the costs
// cost == V + GP * GL
if b, err := currentState.GetBalance(ctx, from); err == nil {
if b.Cmp(tx.Cost()) < 0 {
return core.ErrInsufficientFunds
}
} else {
return err
if b := currentState.GetBalance(from); b.Cmp(tx.Cost()) < 0 {
return core.ErrInsufficientFunds
}
// Should supply enough intrinsic gas
@ -395,7 +389,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error
return core.ErrIntrinsicGas
}
return nil
return currentState.Error()
}
// add validates a new transaction and sets its state pending if processable.

View file

@ -1,194 +0,0 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package light
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
)
// VMState is a wrapper for the light state that holds the actual context and
// passes it to any state operation that requires it.
type VMState struct {
ctx context.Context
state *LightState
snapshots []*LightState
err error
}
func NewVMState(ctx context.Context, state *LightState) *VMState {
return &VMState{ctx: ctx, state: state}
}
func (s *VMState) Error() error {
return s.err
}
func (s *VMState) AddLog(log *types.Log) {}
func (s *VMState) AddPreimage(hash common.Hash, preimage []byte) {}
// errHandler handles and stores any state error that happens during execution.
func (s *VMState) errHandler(err error) {
if err != nil && s.err == nil {
s.err = err
}
}
func (self *VMState) Snapshot() int {
self.snapshots = append(self.snapshots, self.state.Copy())
return len(self.snapshots) - 1
}
func (self *VMState) RevertToSnapshot(idx int) {
self.state.Set(self.snapshots[idx])
self.snapshots = self.snapshots[:idx]
}
// CreateAccount creates creates a new account object and takes ownership.
func (s *VMState) CreateAccount(addr common.Address) {
_, err := s.state.CreateStateObject(s.ctx, addr)
s.errHandler(err)
}
// AddBalance adds the given amount to the balance of the specified account
func (s *VMState) AddBalance(addr common.Address, amount *big.Int) {
err := s.state.AddBalance(s.ctx, addr, amount)
s.errHandler(err)
}
// SubBalance adds the given amount to the balance of the specified account
func (s *VMState) SubBalance(addr common.Address, amount *big.Int) {
err := s.state.SubBalance(s.ctx, addr, amount)
s.errHandler(err)
}
// ForEachStorage calls a callback function for every key/value pair found
// in the local storage cache. Note that unlike core/state.StateObject,
// light.StateObject only returns cached values and doesn't download the
// entire storage tree.
func (s *VMState) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) {
err := s.state.ForEachStorage(s.ctx, addr, cb)
s.errHandler(err)
}
// GetBalance retrieves the balance from the given address or 0 if the account does
// not exist
func (s *VMState) GetBalance(addr common.Address) *big.Int {
res, err := s.state.GetBalance(s.ctx, addr)
s.errHandler(err)
return res
}
// GetNonce returns the nonce at the given address or 0 if the account does
// not exist
func (s *VMState) GetNonce(addr common.Address) uint64 {
res, err := s.state.GetNonce(s.ctx, addr)
s.errHandler(err)
return res
}
// SetNonce sets the nonce of the specified account
func (s *VMState) SetNonce(addr common.Address, nonce uint64) {
err := s.state.SetNonce(s.ctx, addr, nonce)
s.errHandler(err)
}
// GetCode returns the contract code at the given address or nil if the account
// does not exist
func (s *VMState) GetCode(addr common.Address) []byte {
res, err := s.state.GetCode(s.ctx, addr)
s.errHandler(err)
return res
}
// GetCodeHash returns the contract code hash at the given address
func (s *VMState) GetCodeHash(addr common.Address) common.Hash {
res, err := s.state.GetCode(s.ctx, addr)
s.errHandler(err)
return crypto.Keccak256Hash(res)
}
// GetCodeSize returns the contract code size at the given address
func (s *VMState) GetCodeSize(addr common.Address) int {
res, err := s.state.GetCode(s.ctx, addr)
s.errHandler(err)
return len(res)
}
// SetCode sets the contract code at the specified account
func (s *VMState) SetCode(addr common.Address, code []byte) {
err := s.state.SetCode(s.ctx, addr, code)
s.errHandler(err)
}
// AddRefund adds an amount to the refund value collected during a vm execution
func (s *VMState) AddRefund(gas *big.Int) {
s.state.AddRefund(gas)
}
// GetRefund returns the refund value collected during a vm execution
func (s *VMState) GetRefund() *big.Int {
return s.state.GetRefund()
}
// GetState returns the contract storage value at storage address b from the
// contract address a or common.Hash{} if the account does not exist
func (s *VMState) GetState(a common.Address, b common.Hash) common.Hash {
res, err := s.state.GetState(s.ctx, a, b)
s.errHandler(err)
return res
}
// SetState sets the storage value at storage address key of the account addr
func (s *VMState) SetState(addr common.Address, key common.Hash, value common.Hash) {
err := s.state.SetState(s.ctx, addr, key, value)
s.errHandler(err)
}
// Suicide marks an account to be removed and clears its balance
func (s *VMState) Suicide(addr common.Address) bool {
res, err := s.state.Suicide(s.ctx, addr)
s.errHandler(err)
return res
}
// Exist returns true if an account exists at the given address
func (s *VMState) Exist(addr common.Address) bool {
res, err := s.state.HasAccount(s.ctx, addr)
s.errHandler(err)
return res
}
// Empty returns true if the account at the given address is considered empty
func (s *VMState) Empty(addr common.Address) bool {
so, err := s.state.GetStateObject(s.ctx, addr)
s.errHandler(err)
return so == nil || so.empty()
}
// HasSuicided returns true if the given account has been marked for deletion
// or false if the account does not exist
func (s *VMState) HasSuicided(addr common.Address) bool {
res, err := s.state.HasSuicided(s.ctx, addr)
s.errHandler(err)
return res
}