core/state: access trie through Database interface, track errors (#14589)
With this commit, core/state's access to the underlying key/value database is mediated through an interface. Database errors are tracked in StateDB and returned by CommitTo or the new Error method. Motivation for this change: We can remove the light client's duplicated copy of core/state. The light client now supports node iteration, so tracing and storage enumeration can work with the light client (not implemented in this commit).
This commit is contained in:
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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, 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, 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))
|
||||
}
|
||||
|
@ -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 {
|
||||
|
316
light/state.go
316
light/state.go
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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{}, 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.Commit(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")
|
||||
}
|
||||
}
|
253
light/trie.go
253
light/trie.go
@ -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
83
light/trie_test.go
Normal 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
|
||||
}
|
@ -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.
|
||||
|
194
light/vm_env.go
194
light/vm_env.go
@ -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
|
||||
}
|
Reference in New Issue
Block a user