Commit 760fd654 authored by Zsolt Felfoldi's avatar Zsolt Felfoldi Committed by Felix Lange

light: light chain, VM env and tx pool

parent 8b1df1a2
......@@ -632,6 +632,37 @@ func (self *BlockChain) Rollback(chain []common.Hash) {
}
}
// SetReceiptsData computes all the non-consensus fields of the receipts
func SetReceiptsData(block *types.Block, receipts types.Receipts) {
transactions, logIndex := block.Transactions(), uint(0)
for j := 0; j < len(receipts); j++ {
// The transaction hash can be retrieved from the transaction itself
receipts[j].TxHash = transactions[j].Hash()
// The contract address can be derived from the transaction itself
if MessageCreatesContract(transactions[j]) {
from, _ := transactions[j].From()
receipts[j].ContractAddress = crypto.CreateAddress(from, transactions[j].Nonce())
}
// The used gas can be calculated based on previous receipts
if j == 0 {
receipts[j].GasUsed = new(big.Int).Set(receipts[j].CumulativeGasUsed)
} else {
receipts[j].GasUsed = new(big.Int).Sub(receipts[j].CumulativeGasUsed, receipts[j-1].CumulativeGasUsed)
}
// The derived log fields can simply be set from the block and transaction
for k := 0; k < len(receipts[j].Logs); k++ {
receipts[j].Logs[k].BlockNumber = block.NumberU64()
receipts[j].Logs[k].BlockHash = block.Hash()
receipts[j].Logs[k].TxHash = receipts[j].TxHash
receipts[j].Logs[k].TxIndex = uint(j)
receipts[j].Logs[k].Index = logIndex
logIndex++
}
}
}
// InsertReceiptChain attempts to complete an already existing header chain with
// transaction and receipt data.
func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
......@@ -673,32 +704,7 @@ func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain
continue
}
// Compute all the non-consensus fields of the receipts
transactions, logIndex := block.Transactions(), uint(0)
for j := 0; j < len(receipts); j++ {
// The transaction hash can be retrieved from the transaction itself
receipts[j].TxHash = transactions[j].Hash()
// The contract address can be derived from the transaction itself
if MessageCreatesContract(transactions[j]) {
from, _ := transactions[j].From()
receipts[j].ContractAddress = crypto.CreateAddress(from, transactions[j].Nonce())
}
// The used gas can be calculated based on previous receipts
if j == 0 {
receipts[j].GasUsed = new(big.Int).Set(receipts[j].CumulativeGasUsed)
} else {
receipts[j].GasUsed = new(big.Int).Sub(receipts[j].CumulativeGasUsed, receipts[j-1].CumulativeGasUsed)
}
// The derived log fields can simply be set from the block and transaction
for k := 0; k < len(receipts[j].Logs); k++ {
receipts[j].Logs[k].BlockNumber = block.NumberU64()
receipts[j].Logs[k].BlockHash = block.Hash()
receipts[j].Logs[k].TxHash = receipts[j].TxHash
receipts[j].Logs[k].TxIndex = uint(j)
receipts[j].Logs[k].Index = logIndex
logIndex++
}
}
SetReceiptsData(block, receipts)
// Write all the data out into the database
if err := WriteBody(self.chainDb, block.Hash(), block.NumberU64(), block.Body()); err != nil {
errs[index] = fmt.Errorf("failed to write block body: %v", err)
......
......@@ -347,8 +347,13 @@ func WriteBody(db ethdb.Database, hash common.Hash, number uint64, body *types.B
if err != nil {
return err
}
return WriteBodyRLP(db, hash, number, data)
}
// WriteBodyRLP writes a serialized body of a block into the database.
func WriteBodyRLP(db ethdb.Database, hash common.Hash, number uint64, rlp rlp.RawValue) error {
key := append(append(bodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
if err := db.Put(key, data); err != nil {
if err := db.Put(key, rlp); err != nil {
glog.Fatalf("failed to store block body into database: %v", err)
}
glog.V(logger.Debug).Infof("stored block body [%x…]", hash.Bytes()[:4])
......@@ -446,6 +451,16 @@ func WriteTransactions(db ethdb.Database, block *types.Block) error {
return nil
}
// WriteReceipt stores a single transaction receipt into the database.
func WriteReceipt(db ethdb.Database, receipt *types.Receipt) error {
storageReceipt := (*types.ReceiptForStorage)(receipt)
data, err := rlp.EncodeToBytes(storageReceipt)
if err != nil {
return err
}
return db.Put(append(receiptsPrefix, receipt.TxHash.Bytes()...), data)
}
// WriteReceipts stores a batch of transaction receipts into the database.
func WriteReceipts(db ethdb.Database, receipts types.Receipts) error {
batch := db.NewBatch()
......@@ -614,3 +629,30 @@ func GetChainConfig(db ethdb.Database, hash common.Hash) (*ChainConfig, error) {
return &config, nil
}
// FindCommonAncestor returns the last common ancestor of two block headers
func FindCommonAncestor(db ethdb.Database, a, b *types.Header) *types.Header {
for a.GetNumberU64() > b.GetNumberU64() {
a = GetHeader(db, a.ParentHash, a.GetNumberU64()-1)
if a == nil {
return nil
}
}
for a.GetNumberU64() < b.GetNumberU64() {
b = GetHeader(db, b.ParentHash, b.GetNumberU64()-1)
if b == nil {
return nil
}
}
for a.Hash() != b.Hash() {
a = GetHeader(db, a.ParentHash, a.GetNumberU64()-1)
if a == nil {
return nil
}
b = GetHeader(db, b.ParentHash, b.GetNumberU64()-1)
if b == nil {
return nil
}
}
return a
}
......@@ -116,6 +116,15 @@ type jsonHeader struct {
Nonce *BlockNonce `json:"nonce"`
}
func (h *Header) GetNumber() *big.Int { return new(big.Int).Set(h.Number) }
func (h *Header) GetGasLimit() *big.Int { return new(big.Int).Set(h.GasLimit) }
func (h *Header) GetGasUsed() *big.Int { return new(big.Int).Set(h.GasUsed) }
func (h *Header) GetDifficulty() *big.Int { return new(big.Int).Set(h.Difficulty) }
func (h *Header) GetTime() *big.Int { return new(big.Int).Set(h.Time) }
func (h *Header) GetNumberU64() uint64 { return h.Number.Uint64() }
func (h *Header) GetNonce() uint64 { return binary.BigEndian.Uint64(h.Nonce[:]) }
func (h *Header) GetExtra() []byte { return common.CopyBytes(h.Extra) }
// Hash returns the block hash of the header, which is simply the keccak256 hash of its
// RLP encoding.
func (h *Header) Hash() common.Hash {
......
This diff is collapsed.
This diff is collapsed.
......@@ -19,14 +19,22 @@
package light
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp"
"golang.org/x/net/context"
)
// OdrBackend is an interface to a backend service that handles odr retrievals
// NoOdr is the default context passed to an ODR capable function when the ODR
// service is not required.
var NoOdr = context.Background()
// OdrBackend is an interface to a backend service that handles ODR retrievals
type OdrBackend interface {
Database() ethdb.Database
Retrieve(ctx context.Context, req OdrRequest) error
......@@ -37,17 +45,44 @@ type OdrRequest interface {
StoreResult(db ethdb.Database)
}
// TrieID identifies a state or account storage trie
type TrieID struct {
BlockHash, Root common.Hash
AccKey []byte
}
// StateTrieID returns a TrieID for a state trie belonging to a certain block
// header.
func StateTrieID(header *types.Header) *TrieID {
return &TrieID{
BlockHash: header.Hash(),
AccKey: nil,
Root: header.Root,
}
}
// 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 {
return &TrieID{
BlockHash: state.BlockHash,
AccKey: crypto.Keccak256(addr[:]),
Root: root,
}
}
// TrieRequest is the ODR request type for state/storage trie entries
type TrieRequest struct {
OdrRequest
root common.Hash
key []byte
proof []rlp.RawValue
Id *TrieID
Key []byte
Proof []rlp.RawValue
}
// StoreResult stores the retrieved data in local database
func (req *TrieRequest) StoreResult(db ethdb.Database) {
storeProof(db, req.proof)
storeProof(db, req.Proof)
}
// storeProof stores the new trie nodes obtained from a merkle proof in the database
......@@ -61,38 +96,61 @@ func storeProof(db ethdb.Database, proof []rlp.RawValue) {
}
}
// NodeDataRequest is the ODR request type for node data (used for retrieving contract code)
type NodeDataRequest struct {
// CodeRequest is the ODR request type for retrieving contract code
type CodeRequest struct {
OdrRequest
hash common.Hash
data []byte
Id *TrieID
Hash common.Hash
Data []byte
}
// GetData returns the retrieved node data after a successful request
func (req *NodeDataRequest) GetData() []byte {
return req.data
// StoreResult stores the retrieved data in local database
func (req *CodeRequest) StoreResult(db ethdb.Database) {
db.Put(req.Hash[:], req.Data)
}
// BlockRequest is the ODR request type for retrieving block bodies
type BlockRequest struct {
OdrRequest
Hash common.Hash
Number uint64
Rlp []byte
}
// StoreResult stores the retrieved data in local database
func (req *NodeDataRequest) StoreResult(db ethdb.Database) {
db.Put(req.hash[:], req.GetData())
func (req *BlockRequest) StoreResult(db ethdb.Database) {
core.WriteBodyRLP(db, req.Hash, req.Number, req.Rlp)
}
var sha3_nil = crypto.Keccak256Hash(nil)
// ReceiptsRequest is the ODR request type for retrieving block bodies
type ReceiptsRequest struct {
OdrRequest
Hash common.Hash
Number uint64
Receipts types.Receipts
}
// retrieveNodeData tries to retrieve node data with the given hash from the network
func retrieveNodeData(ctx context.Context, odr OdrBackend, hash common.Hash) ([]byte, error) {
if hash == sha3_nil {
return nil, nil
}
res, _ := odr.Database().Get(hash[:])
if res != nil {
return res, nil
}
r := &NodeDataRequest{hash: hash}
if err := odr.Retrieve(ctx, r); err != nil {
return nil, err
} else {
return r.GetData(), nil
}
// StoreResult stores the retrieved data in local database
func (req *ReceiptsRequest) StoreResult(db ethdb.Database) {
core.WriteBlockReceipts(db, req.Hash, req.Number, req.Receipts)
}
// TrieRequest is the ODR request type for state/storage trie entries
type ChtRequest struct {
OdrRequest
ChtNum, BlockNum uint64
ChtRoot common.Hash
Header *types.Header
Td *big.Int
Proof []rlp.RawValue
}
// StoreResult stores the retrieved data in local database
func (req *ChtRequest) StoreResult(db ethdb.Database) {
// if there is a canonical hash, there is a header too
core.WriteHeader(db, req.Header)
hash, num := req.Header.Hash(), req.Header.Number.Uint64()
core.WriteTd(db, hash, num, req.Td)
core.WriteCanonicalHash(db, hash, num)
//storeProof(db, req.Proof)
}
This diff is collapsed.
// 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"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
"golang.org/x/net/context"
)
var sha3_nil = crypto.Keccak256Hash(nil)
var (
ErrNoTrustedCht = errors.New("No trusted canonical hash trie")
ErrNoHeader = errors.New("Header not found")
ChtFrequency = uint64(4096)
trustedChtKey = []byte("TrustedCHT")
)
type ChtNode struct {
Hash common.Hash
Td *big.Int
}
type TrustedCht struct {
Number uint64
Root common.Hash
}
func GetTrustedCht(db ethdb.Database) TrustedCht {
data, _ := db.Get(trustedChtKey)
var res TrustedCht
if err := rlp.DecodeBytes(data, &res); err != nil {
return TrustedCht{0, common.Hash{}}
}
return res
}
func WriteTrustedCht(db ethdb.Database, cht TrustedCht) {
data, _ := rlp.EncodeToBytes(cht)
db.Put(trustedChtKey, data)
}
func DeleteTrustedCht(db ethdb.Database) {
db.Delete(trustedChtKey)
}
func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*types.Header, error) {
db := odr.Database()
hash := core.GetCanonicalHash(db, number)
if (hash != common.Hash{}) {
// if there is a canonical hash, there is a header too
header := core.GetHeader(db, hash, number)
if header == nil {
panic("Canonical hash present but header not found")
}
return header, nil
}
cht := GetTrustedCht(db)
if number >= cht.Number*ChtFrequency {
return nil, ErrNoTrustedCht
}
r := &ChtRequest{ChtRoot: cht.Root, ChtNum: cht.Number, BlockNum: number}
if err := odr.Retrieve(ctx, r); err != nil {
return nil, err
} else {
return r.Header, nil
}
}
func GetCanonicalHash(ctx context.Context, odr OdrBackend, number uint64) (common.Hash, error) {
hash := core.GetCanonicalHash(odr.Database(), number)
if (hash != common.Hash{}) {
return hash, nil
}
header, err := GetHeaderByNumber(ctx, odr, number)
if header != nil {
return header.Hash(), nil
}
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 {
return data, nil
}
r := &BlockRequest{Hash: hash, Number: number}
if err := odr.Retrieve(ctx, r); err != nil {
return nil, err
} else {
return r.Rlp, nil
}
}
// GetBody retrieves the block body (transactons, uncles) corresponding to the
// hash.
func GetBody(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (*types.Body, error) {
data, err := GetBodyRLP(ctx, odr, hash, number)
if err != nil {
return nil, err
}
body := new(types.Body)
if err := rlp.Decode(bytes.NewReader(data), body); err != nil {
glog.V(logger.Error).Infof("invalid block body RLP for hash %x: %v", hash, err)
return nil, err
}
return body, nil
}
// GetBlock retrieves an entire block corresponding to the hash, assembling it
// back from the stored header and body.
func GetBlock(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (*types.Block, error) {
// Retrieve the block header and body contents
header := core.GetHeader(odr.Database(), hash, number)
if header == nil {
return nil, ErrNoHeader
}
body, err := GetBody(ctx, odr, hash, number)
if err != nil {
return nil, err
}
// Reassemble the block and return
return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles), nil
}
// GetBlockReceipts retrieves the receipts generated by the transactions included
// in a block given by its hash.
func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (types.Receipts, error) {
receipts := core.GetBlockReceipts(odr.Database(), hash, number)
if receipts != nil {
return receipts, nil
}
r := &ReceiptsRequest{Hash: hash, Number: number}
if err := odr.Retrieve(ctx, r); err != nil {
return nil, err
} else {
return r.Receipts, nil
}
}
......@@ -20,6 +20,7 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"golang.org/x/net/context"
......@@ -33,10 +34,11 @@ var StartingNonce uint64
// 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
odr OdrBackend
trie *LightTrie
id *TrieID
stateObjects map[string]*StateObject
refund *big.Int
}
// NewLightState creates a new LightState with the specified root.
......@@ -44,15 +46,25 @@ type LightState struct {
// 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(root common.Hash, odr OdrBackend) *LightState {
tr := NewLightTrie(root, odr, true)
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)
......@@ -109,9 +121,9 @@ func (self *LightState) GetState(ctx context.Context, a common.Address, b common
return common.Hash{}, err
}
// IsDeleted returns true if the given account has been marked for deletion
// HasSuicided returns true if the given account has been marked for deletion
// or false if the account does not exist
func (self *LightState) IsDeleted(ctx context.Context, addr common.Address) (bool, error) {
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
......@@ -145,7 +157,7 @@ func (self *LightState) SetNonce(ctx context.Context, addr common.Address, nonce
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(code)
stateObject.SetCode(crypto.Keccak256Hash(code), code)
}
return err
}
......@@ -160,7 +172,7 @@ func (self *LightState) SetState(ctx context.Context, addr common.Address, key c
}
// Delete marks an account to be removed and clears its balance
func (self *LightState) Delete(ctx context.Context, addr common.Address) (bool, error) {
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()
......@@ -194,7 +206,7 @@ func (self *LightState) GetStateObject(ctx context.Context, addr common.Address)
return nil, nil
}
stateObject, err = DecodeObject(ctx, addr, self.odr, []byte(data))
stateObject, err = DecodeObject(ctx, self.id, addr, self.odr, []byte(data))
if err != nil {
return nil, err
}
......@@ -258,14 +270,16 @@ func (self *LightState) CreateStateObject(ctx context.Context, addr common.Addre
// Copy creates a copy of the state
func (self *LightState) Copy() *LightState {
// ignore error - we assume state-to-be-copied always exists
state := NewLightState(common.Hash{}, self.odr)
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
}
......@@ -274,4 +288,10 @@ func (self *LightState) Copy() *LightState {
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
}
......@@ -40,7 +40,7 @@ func (self Code) String() string {
}
// Storage is a memory map cache of a contract storage
type Storage map[string]common.Hash
type Storage map[common.Hash]common.Hash
// String returns a string representation of the storage cache
func (self Storage) String() (str string) {
......@@ -100,7 +100,7 @@ func NewStateObject(address common.Address, odr OdrBackend) *StateObject {
codeHash: emptyCodeHash,
storage: make(Storage),
}
object.trie = NewLightTrie(common.Hash{}, odr, true)
object.trie = NewLightTrie(&TrieID{}, odr, true)
return object
}
......@@ -133,8 +133,7 @@ func (self *StateObject) Storage() 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) {
strkey := key.Str()
value, exists := self.storage[strkey]
value, exists := self.storage[key]
if !exists {
var err error
value, err = self.getAddr(ctx, key)
......@@ -142,7 +141,7 @@ func (self *StateObject) GetState(ctx context.Context, key common.Hash) (common.
return common.Hash{}, err
}
if (value != common.Hash{}) {
self.storage[strkey] = value
self.storage[key] = value
}
}
......@@ -151,7 +150,7 @@ func (self *StateObject) GetState(ctx context.Context, key common.Hash) (common.
// SetState sets the storage value at the given address
func (self *StateObject) SetState(k, value common.Hash) {
self.storage[k.Str()] = value
self.storage[k] = value
self.dirty = true
}
......@@ -179,6 +178,9 @@ func (c *StateObject) SetBalance(amount *big.Int) {
c.dirty = true
}
// ReturnGas returns the gas back to the origin. Used by the Virtual machine or Closures
func (c *StateObject) ReturnGas(gas, price *big.Int) {}
// Copy creates a copy of the state object
func (self *StateObject) Copy() *StateObject {
stateObject := NewStateObject(self.Address(), self.odr)
......@@ -215,9 +217,9 @@ func (self *StateObject) Code() []byte {
}
// SetCode sets the contract code
func (self *StateObject) SetCode(code []byte) {
func (self *StateObject) SetCode(hash common.Hash, code []byte) {
self.code = code
self.codeHash = crypto.Keccak256(code)
self.codeHash = hash[:]
self.dirty = true
}
......@@ -232,6 +234,23 @@ 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 {
......@@ -242,7 +261,7 @@ type extStateObject struct {
}
// DecodeObject decodes an RLP-encoded state object.
func DecodeObject(ctx context.Context, address common.Address, odr OdrBackend, data []byte) (*StateObject, error) {
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
......@@ -251,9 +270,10 @@ func DecodeObject(ctx context.Context, address common.Address, odr OdrBackend, d
if err = rlp.DecodeBytes(data, &ext); err != nil {
return nil, err
}
obj.trie = NewLightTrie(ext.Root, odr, true)
trieID := StorageTrieID(stateID, address, ext.Root)
obj.trie = NewLightTrie(trieID, odr, true)
if !bytes.Equal(ext.CodeHash, emptyCodeHash) {
if obj.code, err = retrieveNodeData(ctx, obj.odr, common.BytesToHash(ext.CodeHash)); err != nil {
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)
}
}
......
......@@ -22,33 +22,13 @@ import (
"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"
"github.com/ethereum/go-ethereum/trie"
"golang.org/x/net/context"
)
type testOdr struct {
OdrBackend
sdb, ldb ethdb.Database
}
func (odr *testOdr) Database() ethdb.Database {
return odr.ldb
}
func (odr *testOdr) Retrieve(ctx context.Context, req OdrRequest) error {
switch req := req.(type) {
case *TrieRequest:
t, _ := trie.New(req.root, odr.sdb)
req.proof = t.Prove(req.key)
case *NodeDataRequest:
req.data, _ = odr.sdb.Get(req.hash[:])
}
req.StoreResult(odr.ldb)
return nil
}
func makeTestState() (common.Hash, ethdb.Database) {
sdb, _ := ethdb.NewMemDatabase()
st, _ := state.New(common.Hash{}, sdb)
......@@ -67,9 +47,11 @@ func makeTestState() (common.Hash, ethdb.Database) {
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(root, odr)
ls := NewLightState(StateTrieID(header), odr)
ctx := context.Background()
for i := byte(0); i < 100; i++ {
......@@ -151,9 +133,11 @@ func TestLightStateOdr(t *testing.T) {
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(root, odr)
ls := NewLightState(StateTrieID(header), odr)
ctx := context.Background()
for i := byte(0); i < 100; i++ {
......@@ -227,9 +211,11 @@ func TestLightStateSetCopy(t *testing.T) {
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(root, odr)
ls := NewLightState(StateTrieID(header), odr)
ctx := context.Background()
addr := common.Address{42}
......@@ -242,21 +228,21 @@ func TestLightStateDelete(t *testing.T) {
t.Fatalf("HasAccount returned false, expected true")
}
b, err = ls.IsDeleted(ctx, addr)
b, err = ls.HasSuicided(ctx, addr)
if err != nil {
t.Fatalf("IsDeleted error: %v", err)
t.Fatalf("HasSuicided error: %v", err)
}
if b {
t.Fatalf("IsDeleted returned true, expected false")
t.Fatalf("HasSuicided returned true, expected false")
}
ls.Delete(ctx, addr)
ls.Suicide(ctx, addr)
b, err = ls.IsDeleted(ctx, addr)
b, err = ls.HasSuicided(ctx, addr)
if err != nil {
t.Fatalf("IsDeleted error: %v", err)
t.Fatalf("HasSuicided error: %v", err)
}
if !b {
t.Fatalf("IsDeleted returned false, expected true")
t.Fatalf("HasSuicided returned false, expected true")
}
}
......@@ -17,7 +17,6 @@
package light
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie"
"golang.org/x/net/context"
......@@ -25,28 +24,28 @@ import (
// LightTrie is an ODR-capable wrapper around trie.SecureTrie
type LightTrie struct {
trie *trie.SecureTrie
originalRoot common.Hash
odr OdrBackend
db ethdb.Database
trie *trie.SecureTrie
id *TrieID
odr OdrBackend
db ethdb.Database
}
// 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(root common.Hash, odr OdrBackend, useFakeMap bool) *LightTrie {
func NewLightTrie(id *TrieID, odr OdrBackend, useFakeMap bool) *LightTrie {
return &LightTrie{
// SecureTrie is initialized before first request
originalRoot: root,
odr: odr,
db: odr.Database(),
id: id,
odr: odr,
db: odr.Database(),
}
}
// 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{root: t.originalRoot, key: key}
r := &TrieRequest{Id: t.id, Key: key}
return t.odr.Retrieve(ctx, r) == nil
}
......@@ -79,7 +78,7 @@ func (t *LightTrie) do(ctx context.Context, fallbackKey []byte, fn func() error)
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.originalRoot, t.db, 0)
t.trie, err = trie.NewSecure(t.id.Root, t.db, 0)
}
if err == nil {
res, err = t.trie.TryGet(key)
......@@ -98,7 +97,7 @@ func (t *LightTrie) Get(ctx context.Context, key []byte) (res []byte, err error)
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.originalRoot, t.db, 0)
t.trie, err = trie.NewSecure(t.id.Root, t.db, 0)
}
if err == nil {
err = t.trie.TryUpdate(key, value)
......@@ -112,7 +111,7 @@ func (t *LightTrie) Update(ctx context.Context, key, value []byte) (err error) {
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.originalRoot, t.db, 0)
t.trie, err = trie.NewSecure(t.id.Root, t.db, 0)
}
if err == nil {
err = t.trie.TryDelete(key)
......
This diff is collapsed.
// Copyright 2014 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 (
"math"
"math/big"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
"golang.org/x/net/context"
)
type testTxRelay struct {
send, nhMined, nhRollback, discard int
}
func (self *testTxRelay) Send(txs types.Transactions) {
self.send = len(txs)
}
func (self *testTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) {
self.nhMined = len(mined)
self.nhRollback = len(rollback)
}
func (self *testTxRelay) Discard(hashes []common.Hash) {
self.discard = len(hashes)
}
const poolTestTxs = 1000
const poolTestBlocks = 100
// test tx 0..n-1
var testTx [poolTestTxs]*types.Transaction
// txs sent before block i
func sentTx(i int) int {
return int(math.Pow(float64(i)/float64(poolTestBlocks), 0.9) * poolTestTxs)
}
// txs included in block i or before that (minedTx(i) <= sentTx(i))
func minedTx(i int) int {
return int(math.Pow(float64(i)/float64(poolTestBlocks), 1.1) * poolTestTxs)
}
func txPoolTestChainGen(i int, block *core.BlockGen) {
s := minedTx(i)
e := minedTx(i + 1)
for i := s; i < e; i++ {
block.AddTx(testTx[i])
}
}
func TestTxPool(t *testing.T) {
for i, _ := range testTx {
testTx[i], _ = types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil).SignECDSA(testBankKey)
}
var (
evmux = new(event.TypeMux)
pow = new(core.FakePow)
sdb, _ = ethdb.NewMemDatabase()
ldb, _ = ethdb.NewMemDatabase()
genesis = core.WriteGenesisBlockForTesting(sdb, core.GenesisAccount{Address: testBankAddress, Balance: testBankFunds})
)
core.WriteGenesisBlockForTesting(ldb, core.GenesisAccount{Address: testBankAddress, Balance: testBankFunds})
// Assemble the test environment
blockchain, _ := core.NewBlockChain(sdb, testChainConfig(), pow, evmux)
gchain, _ := core.GenerateChain(nil, genesis, sdb, poolTestBlocks, txPoolTestChainGen)
if _, err := blockchain.InsertChain(gchain); err != nil {
panic(err)
}
odr := &testOdr{sdb: sdb, ldb: ldb}
relay := &testTxRelay{}
lightchain, _ := NewLightChain(odr, testChainConfig(), pow, evmux)
lightchain.SetValidator(bproc{})
txPermanent = 50
pool := NewTxPool(testChainConfig(), evmux, lightchain, relay)
for ii, block := range gchain {
i := ii + 1
ctx, _ := context.WithTimeout(context.Background(), 200*time.Millisecond)
s := sentTx(i - 1)
e := sentTx(i)
for i := s; i < e; i++ {
relay.send = 0
pool.Add(ctx, testTx[i])
got := relay.send
exp := 1
if got != exp {
t.Errorf("relay.Send expected len = %d, got %d", exp, got)
}
}
relay.nhMined = 0
relay.nhRollback = 0
relay.discard = 0
if _, err := lightchain.InsertHeaderChain([]*types.Header{block.Header()}, 1); err != nil {
panic(err)
}
time.Sleep(time.Millisecond * 30)
got := relay.nhMined
exp := minedTx(i) - minedTx(i-1)
if got != exp {
t.Errorf("relay.NewHead expected len(mined) = %d, got %d", exp, got)
}
got = relay.discard
exp = 0
if i > int(txPermanent)+1 {
exp = minedTx(i-int(txPermanent)-1) - minedTx(i-int(txPermanent)-2)
}
if got != exp {
t.Errorf("relay.Discard expected len = %d, got %d", exp, got)
}
}
}
// 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 (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"golang.org/x/net/context"
)
// VMEnv is the light client version of the vm execution environment.
// Unlike other structures, VMEnv holds a context that is applied by state
// retrieval requests through the entire execution. If any state operation
// returns an error, the execution fails.
type VMEnv struct {
vm.Environment
ctx context.Context
chainConfig *core.ChainConfig
evm *vm.EVM
state *VMState
header *types.Header
msg core.Message
depth int
chain *LightChain
err error
}
// NewEnv creates a new execution environment based on an ODR capable light state
func NewEnv(ctx context.Context, state *LightState, chainConfig *core.ChainConfig, chain *LightChain, msg core.Message, header *types.Header, cfg vm.Config) *VMEnv {
env := &VMEnv{
chainConfig: chainConfig,
chain: chain,
header: header,
msg: msg,
}
env.state = &VMState{ctx: ctx, state: state, env: env}
env.evm = vm.New(env, cfg)
return env
}
func (self *VMEnv) RuleSet() vm.RuleSet { return self.chainConfig }
func (self *VMEnv) Vm() vm.Vm { return self.evm }
func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f }
func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number }
func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase }
func (self *VMEnv) Time() *big.Int { return self.header.Time }
func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty }
func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit }
func (self *VMEnv) Db() vm.Database { return self.state }
func (self *VMEnv) Depth() int { return self.depth }
func (self *VMEnv) SetDepth(i int) { self.depth = i }
func (self *VMEnv) GetHash(n uint64) common.Hash {
for header := self.chain.GetHeader(self.header.ParentHash, self.header.Number.Uint64()-1); header != nil; header = self.chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) {
if header.GetNumberU64() == n {
return header.Hash()
}
}
return common.Hash{}
}
func (self *VMEnv) AddLog(log *vm.Log) {
//self.state.AddLog(log)
}
func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
return self.state.GetBalance(from).Cmp(balance) >= 0
}
func (self *VMEnv) SnapshotDatabase() int {
return self.state.SnapshotDatabase()
}
func (self *VMEnv) RevertToSnapshot(idx int) {
self.state.RevertToSnapshot(idx)
}
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {
core.Transfer(from, to, amount)
}
func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
return core.Call(self, me, addr, data, gas, price, value)
}
func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
return core.CallCode(self, me, addr, data, gas, price, value)
}
func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
return core.DelegateCall(self, me, addr, data, gas, price)
}
func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
return core.Create(self, me, data, gas, price, value)
}
// Error returns the error (if any) that happened during execution.
func (self *VMEnv) Error() error {
return self.err
}
// 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 {
vm.Database
ctx context.Context
state *LightState
snapshots []*LightState
env *VMEnv
}
// errHandler handles and stores any state error that happens during execution.
func (s *VMState) errHandler(err error) {
if err != nil && s.env.err == nil {
s.env.err = err
}
}
func (self *VMState) SnapshotDatabase() 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]
}
// GetAccount returns the account object of the given account or nil if the
// account does not exist
func (s *VMState) GetAccount(addr common.Address) vm.Account {
so, err := s.state.GetStateObject(s.ctx, addr)
s.errHandler(err)
if err != nil {
// return a dummy state object to avoid panics
so = s.state.newStateObject(addr)
}
return so
}
// CreateAccount creates creates a new account object and takes ownership.
func (s *VMState) CreateAccount(addr common.Address) vm.Account {
so, err := s.state.CreateStateObject(s.ctx, addr)
s.errHandler(err)
if err != nil {
// return a dummy state object to avoid panics
so = s.state.newStateObject(addr)
}
return so
}
// 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)
}
// 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
}
// 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
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment