This commit is contained in:
Felföldi Zsolt 2015-11-19 03:26:04 +00:00
commit 8d48040f46
83 changed files with 3906 additions and 558 deletions

View file

@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
@ -113,7 +114,7 @@ func run(ctx *cli.Context) {
glog.SetV(ctx.GlobalInt(VerbosityFlag.Name)) glog.SetV(ctx.GlobalInt(VerbosityFlag.Name))
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db), access.NullCtx)
sender := statedb.CreateAccount(common.StringToAddress("sender")) sender := statedb.CreateAccount(common.StringToAddress("sender"))
receiver := statedb.CreateAccount(common.StringToAddress("receiver")) receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name))) receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)))
@ -186,7 +187,7 @@ func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int) *VM
} }
func (self *VMEnv) Db() vm.Database { return self.state } func (self *VMEnv) Db() vm.Database { return self.state }
func (self *VMEnv) MakeSnapshot() vm.Database { return self.state.Copy() } func (self *VMEnv) MakeSnapshot() vm.Database { return self.state.Copy(access.NullCtx) }
func (self *VMEnv) SetSnapshot(db vm.Database) { self.state.Set(db.(*state.StateDB)) } func (self *VMEnv) SetSnapshot(db vm.Database) { self.state.Set(db.(*state.StateDB)) }
func (self *VMEnv) Origin() common.Address { return *self.transactor } func (self *VMEnv) Origin() common.Address { return *self.transactor }
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 } func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }

View file

@ -22,6 +22,7 @@ import (
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/tests" "github.com/ethereum/go-ethereum/tests"
@ -124,7 +125,7 @@ func runOneBlockTest(ctx *cli.Context, test *tests.BlockTest) (*eth.Ethereum, er
if err != nil { if err != nil {
return ethereum, fmt.Errorf("Block Test load error: %v", err) return ethereum, fmt.Errorf("Block Test load error: %v", err)
} }
newDB, err := cm.State() newDB, err := cm.State(access.NullCtx)
if err != nil { if err != nil {
return ethereum, fmt.Errorf("Block Test get state error: %v", err) return ethereum, fmt.Errorf("Block Test get state error: %v", err)
} }

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -170,16 +171,16 @@ func dump(ctx *cli.Context) {
for _, arg := range ctx.Args() { for _, arg := range ctx.Args() {
var block *types.Block var block *types.Block
if hashish(arg) { if hashish(arg) {
block = chain.GetBlock(common.HexToHash(arg)) block = chain.GetBlock(common.HexToHash(arg), access.NoOdr)
} else { } else {
num, _ := strconv.Atoi(arg) num, _ := strconv.Atoi(arg)
block = chain.GetBlockByNumber(uint64(num)) block = chain.GetBlockByNumber(uint64(num), access.NoOdr)
} }
if block == nil { if block == nil {
fmt.Println("{}") fmt.Println("{}")
utils.Fatalf("block not found") utils.Fatalf("block not found")
} else { } else {
state, err := state.New(block.Root(), chainDb) state, err := state.New(block.Root(), access.NewDbChainAccess(chainDb), access.NullCtx)
if err != nil { if err != nil {
utils.Fatalf("could not create new state: %v", err) utils.Fatalf("could not create new state: %v", err)
return return

View file

@ -34,6 +34,7 @@ import (
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -304,6 +305,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
utils.BlockchainVersionFlag, utils.BlockchainVersionFlag,
utils.OlympicFlag, utils.OlympicFlag,
utils.FastSyncFlag, utils.FastSyncFlag,
utils.EthModeFlag,
utils.CacheFlag, utils.CacheFlag,
utils.LightKDFFlag, utils.LightKDFFlag,
utils.JSpathFlag, utils.JSpathFlag,
@ -555,12 +557,13 @@ func blockRecovery(ctx *cli.Context) {
if err != nil { if err != nil {
glog.Fatalln("could not open db:", err) glog.Fatalln("could not open db:", err)
} }
ca := access.NewDbChainAccess(blockDb)
var block *types.Block var block *types.Block
if arg[0] == '#' { if arg[0] == '#' {
block = core.GetBlock(blockDb, core.GetCanonicalHash(blockDb, common.String2Big(arg[1:]).Uint64())) block = core.GetBlock(ca, core.GetCanonicalHash(blockDb, common.String2Big(arg[1:]).Uint64()), access.NoOdr)
} else { } else {
block = core.GetBlock(blockDb, common.HexToHash(arg)) block = core.GetBlock(ca, common.HexToHash(arg), access.NoOdr)
} }
if block == nil { if block == nil {

View file

@ -28,12 +28,14 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv" "strconv"
"strings"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/ethereum/ethash" "github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
@ -133,11 +135,6 @@ var (
Name: "natspec", Name: "natspec",
Usage: "Enable NatSpec confirmation notice", Usage: "Enable NatSpec confirmation notice",
} }
DocRootFlag = DirectoryFlag{
Name: "docroot",
Usage: "Document Root for HTTPClient file scheme",
Value: DirectoryString{common.HomeDir()},
}
CacheFlag = cli.IntFlag{ CacheFlag = cli.IntFlag{
Name: "cache", Name: "cache",
Usage: "Megabytes of memory allocated to internal caching (min 16MB / database forced)", Usage: "Megabytes of memory allocated to internal caching (min 16MB / database forced)",
@ -156,6 +153,11 @@ var (
Name: "lightkdf", Name: "lightkdf",
Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
} }
EthModeFlag = cli.StringFlag{
Name: "mode",
Value: "archive",
Usage: "Client mode of operation (archive, full, light)",
}
// Miner settings // Miner settings
// TODO: refactor CPU vs GPU mining flags // TODO: refactor CPU vs GPU mining flags
MiningEnabledFlag = cli.BoolFlag{ MiningEnabledFlag = cli.BoolFlag{
@ -424,12 +426,25 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
if err != nil { if err != nil {
glog.V(logger.Error).Infoln("WARNING: No etherbase set and no accounts found as default") glog.V(logger.Error).Infoln("WARNING: No etherbase set and no accounts found as default")
} }
// Resolve the mode of opeation from the string flag
var clientMode eth.Mode
switch strings.ToLower(ctx.GlobalString(EthModeFlag.Name)) {
case "archive":
clientMode = eth.ArchiveMode
case "full":
clientMode = eth.FullMode
case "light":
clientMode = eth.LightMode
default:
glog.Fatalf("Unknown node type requested: %s", ctx.GlobalString(EthModeFlag.Name))
}
// Assemble the entire eth configuration and return // Assemble the entire eth configuration and return
cfg := &eth.Config{ cfg := &eth.Config{
Name: common.MakeName(clientID, version), Name: common.MakeName(clientID, version),
DataDir: MustDataDir(ctx), DataDir: MustDataDir(ctx),
GenesisFile: ctx.GlobalString(GenesisFileFlag.Name), GenesisFile: ctx.GlobalString(GenesisFileFlag.Name),
FastSync: ctx.GlobalBool(FastSyncFlag.Name), FastSync: ctx.GlobalBool(FastSyncFlag.Name),
Mode: clientMode,
BlockChainVersion: ctx.GlobalInt(BlockchainVersionFlag.Name), BlockChainVersion: ctx.GlobalInt(BlockchainVersionFlag.Name),
DatabaseCache: ctx.GlobalInt(CacheFlag.Name), DatabaseCache: ctx.GlobalInt(CacheFlag.Name),
SkipBcVersionCheck: false, SkipBcVersionCheck: false,
@ -446,7 +461,6 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
Olympic: ctx.GlobalBool(OlympicFlag.Name), Olympic: ctx.GlobalBool(OlympicFlag.Name),
NAT: MakeNAT(ctx), NAT: MakeNAT(ctx),
NatSpec: ctx.GlobalBool(NatspecEnabledFlag.Name), NatSpec: ctx.GlobalBool(NatspecEnabledFlag.Name),
DocRoot: ctx.GlobalString(DocRootFlag.Name),
Discovery: !ctx.GlobalBool(NoDiscoverFlag.Name), Discovery: !ctx.GlobalBool(NoDiscoverFlag.Name),
NodeKey: MakeNodeKey(ctx), NodeKey: MakeNodeKey(ctx),
Shh: ctx.GlobalBool(WhisperEnabledFlag.Name), Shh: ctx.GlobalBool(WhisperEnabledFlag.Name),
@ -552,12 +566,12 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database
eventMux := new(event.TypeMux) eventMux := new(event.TypeMux)
pow := ethash.New() pow := ethash.New()
//genesis := core.GenesisBlock(uint64(ctx.GlobalInt(GenesisNonceFlag.Name)), blockDB) //genesis := core.GenesisBlock(uint64(ctx.GlobalInt(GenesisNonceFlag.Name)), blockDB)
chain, err = core.NewBlockChain(chainDb, pow, eventMux) chain, err = core.NewBlockChain(access.NewDbChainAccess(chainDb), pow, eventMux)
if err != nil { if err != nil {
Fatalf("Could not start chainmanager: %v", err) Fatalf("Could not start chainmanager: %v", err)
} }
proc := core.NewBlockProcessor(chainDb, pow, chain, eventMux) proc := core.NewBlockProcessor(access.NewDbChainAccess(chainDb), pow, chain, eventMux)
chain.SetProcessor(proc) chain.SetProcessor(proc)
return chain, chainDb return chain, chainDb
} }

View file

@ -24,6 +24,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -168,8 +169,9 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
// Time the insertion of the new chain. // Time the insertion of the new chain.
// State and blocks are stored in the same DB. // State and blocks are stored in the same DB.
evmux := new(event.TypeMux) evmux := new(event.TypeMux)
chainman, _ := NewBlockChain(db, FakePow{}, evmux) ca := access.NewDbChainAccess(db)
chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux)) chainman, _ := NewBlockChain(ca, FakePow{}, evmux)
chainman.SetProcessor(NewBlockProcessor(ca, FakePow{}, chainman, evmux))
defer chainman.Stop() defer chainman.Stop()
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()

View file

@ -23,11 +23,11 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
@ -43,7 +43,7 @@ const (
) )
type BlockProcessor struct { type BlockProcessor struct {
chainDb ethdb.Database chainAccess *access.ChainAccess
// Mutex for locking the block processor. Blocks can only be handled one at a time // Mutex for locking the block processor. Blocks can only be handled one at a time
mutex sync.Mutex mutex sync.Mutex
// Canonical block chain // Canonical block chain
@ -85,9 +85,9 @@ func (gp *GasPool) String() string {
return (*big.Int)(gp).String() return (*big.Int)(gp).String()
} }
func NewBlockProcessor(db ethdb.Database, pow pow.PoW, blockchain *BlockChain, eventMux *event.TypeMux) *BlockProcessor { func NewBlockProcessor(ca *access.ChainAccess, pow pow.PoW, blockchain *BlockChain, eventMux *event.TypeMux) *BlockProcessor {
sm := &BlockProcessor{ sm := &BlockProcessor{
chainDb: db, chainAccess: ca,
mem: make(map[string]*big.Int), mem: make(map[string]*big.Int),
Pow: pow, Pow: pow,
bc: blockchain, bc: blockchain,
@ -189,7 +189,7 @@ func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs vm.Logs, err er
if !sm.bc.HasBlock(block.ParentHash()) { if !sm.bc.HasBlock(block.ParentHash()) {
return nil, ParentError(block.ParentHash()) return nil, ParentError(block.ParentHash())
} }
parent := sm.bc.GetBlock(block.ParentHash()) parent := sm.bc.GetBlock(block.ParentHash(), access.NoOdr)
// FIXME Change to full header validation. See #1225 // FIXME Change to full header validation. See #1225
errch := make(chan bool) errch := make(chan bool)
@ -212,12 +212,12 @@ func (sm *BlockProcessor) Process(block *types.Block) (logs vm.Logs, receipts ty
defer sm.mutex.Unlock() defer sm.mutex.Unlock()
if sm.bc.HasBlock(block.Hash()) { if sm.bc.HasBlock(block.Hash()) {
if _, err := state.New(block.Root(), sm.chainDb); err == nil { if _, err := state.New(block.Root(), sm.chainAccess, access.NullCtx); err == nil {
return nil, nil, &KnownBlockError{block.Number(), block.Hash()} return nil, nil, &KnownBlockError{block.Number(), block.Hash()}
} }
} }
if parent := sm.bc.GetBlock(block.ParentHash()); parent != nil { if parent := sm.bc.GetBlock(block.ParentHash(), access.NoOdr); parent != nil {
if _, err := state.New(parent.Root(), sm.chainDb); err == nil { if _, err := state.New(parent.Root(), sm.chainAccess, access.NullCtx); err == nil {
return sm.processWithParent(block, parent) return sm.processWithParent(block, parent)
} }
} }
@ -226,7 +226,7 @@ func (sm *BlockProcessor) Process(block *types.Block) (logs vm.Logs, receipts ty
func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs vm.Logs, receipts types.Receipts, err error) { func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs vm.Logs, receipts types.Receipts, err error) {
// Create a new state based on the parent's root (e.g., create copy) // Create a new state based on the parent's root (e.g., create copy)
state, err := state.New(parent.Root(), sm.chainDb) state, err := state.New(parent.Root(), sm.chainAccess, access.NullCtx)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -369,8 +369,8 @@ func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *ty
// GetBlockReceipts returns the receipts beloniging to the block hash // GetBlockReceipts returns the receipts beloniging to the block hash
func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) types.Receipts { func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) types.Receipts {
if block := sm.BlockChain().GetBlock(bhash); block != nil { if block := sm.BlockChain().GetBlock(bhash, access.NoOdr); block != nil {
return GetBlockReceipts(sm.chainDb, block.Hash()) return GetBlockReceipts(sm.chainAccess, block.Hash(), access.NoOdr)
} }
return nil return nil
@ -380,7 +380,7 @@ func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) types.Receipts {
// where it tries to get it from the (updated) method which gets them from the receipts or // where it tries to get it from the (updated) method which gets them from the receipts or
// the depricated way by re-processing the block. // the depricated way by re-processing the block.
func (sm *BlockProcessor) GetLogs(block *types.Block) (logs vm.Logs, err error) { func (sm *BlockProcessor) GetLogs(block *types.Block) (logs vm.Logs, err error) {
receipts := GetBlockReceipts(sm.chainDb, block.Hash()) receipts := GetBlockReceipts(sm.chainAccess, block.Hash(), access.NoOdr)
// coalesce logs // coalesce logs
for _, receipt := range receipts { for _, receipt := range receipts {
logs = append(logs, receipt.Logs...) logs = append(logs, receipt.Logs...)

View file

@ -22,6 +22,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
@ -32,21 +33,22 @@ import (
func proc() (*BlockProcessor, *BlockChain) { func proc() (*BlockProcessor, *BlockChain) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
ca := access.NewDbChainAccess(db)
var mux event.TypeMux var mux event.TypeMux
WriteTestNetGenesisBlock(db, 0) WriteTestNetGenesisBlock(db, 0)
blockchain, err := NewBlockChain(db, thePow(), &mux) blockchain, err := NewBlockChain(ca, thePow(), &mux)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
return NewBlockProcessor(db, ezp.New(), blockchain, &mux), blockchain return NewBlockProcessor(ca, ezp.New(), blockchain, &mux), blockchain
} }
func TestNumber(t *testing.T) { func TestNumber(t *testing.T) {
pow := ezp.New() pow := ezp.New()
_, chain := proc() _, chain := proc()
statedb, _ := state.New(chain.Genesis().Root(), chain.chainDb) statedb, _ := state.New(chain.Genesis().Root(), access.NewDbChainAccess(chain.chainDb), access.NullCtx)
header := makeHeader(chain.Genesis(), statedb) header := makeHeader(chain.Genesis(), statedb)
header.Number = big.NewInt(3) header.Number = big.NewInt(3)
err := ValidateHeader(pow, header, chain.Genesis().Header(), false, false) err := ValidateHeader(pow, header, chain.Genesis().Header(), false, false)
@ -82,7 +84,7 @@ func TestPutReceipt(t *testing.T) {
}} }}
PutReceipts(db, types.Receipts{receipt}) PutReceipts(db, types.Receipts{receipt})
receipt = GetReceipt(db, common.Hash{}) receipt = GetReceipt(access.NewDbChainAccess(db), common.Hash{}, access.NoOdr)
if receipt == nil { if receipt == nil {
t.Error("expected to get 1 receipt, got none.") t.Error("expected to get 1 receipt, got none.")
} }

View file

@ -19,6 +19,7 @@ package core
import ( import (
crand "crypto/rand" crand "crypto/rand"
"bytes"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -31,6 +32,7 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -64,6 +66,7 @@ const (
) )
type BlockChain struct { type BlockChain struct {
chainAccess *access.ChainAccess
chainDb ethdb.Database chainDb ethdb.Database
processor types.BlockProcessor processor types.BlockProcessor
eventMux *event.TypeMux eventMux *event.TypeMux
@ -73,9 +76,11 @@ type BlockChain struct {
chainmu sync.RWMutex chainmu sync.RWMutex
tsmu sync.RWMutex tsmu sync.RWMutex
lightMode bool // block head == header head, block data is only retrieved when necessary
checkpoint int // checkpoint counts towards the new checkpoint checkpoint int // checkpoint counts towards the new checkpoint
currentHeader *types.Header // Current head of the header chain (may be above the block chain!) currentHeader *types.Header // Current head of the header chain (may be above the block chain!)
currentBlock *types.Block // Current head of the block chain currentBlock *types.Block // Current head of the block chain (can be nil in light client mode)
currentFastBlock *types.Block // Current head of the fast-sync chain (may be above the block chain!) currentFastBlock *types.Block // Current head of the fast-sync chain (may be above the block chain!)
headerCache *lru.Cache // Cache for the most recent block headers headerCache *lru.Cache // Cache for the most recent block headers
@ -95,7 +100,7 @@ type BlockChain struct {
rand *mrand.Rand rand *mrand.Rand
} }
func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) { func NewBlockChain(chainAccess *access.ChainAccess, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) {
headerCache, _ := lru.New(headerCacheLimit) headerCache, _ := lru.New(headerCacheLimit)
bodyCache, _ := lru.New(bodyCacheLimit) bodyCache, _ := lru.New(bodyCacheLimit)
bodyRLPCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit)
@ -104,7 +109,8 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
futureBlocks, _ := lru.New(maxFutureBlocks) futureBlocks, _ := lru.New(maxFutureBlocks)
bc := &BlockChain{ bc := &BlockChain{
chainDb: chainDb, chainDb: chainAccess.Db(),
chainAccess: chainAccess,
eventMux: mux, eventMux: mux,
quit: make(chan struct{}), quit: make(chan struct{}),
headerCache: headerCache, headerCache: headerCache,
@ -114,6 +120,7 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
blockCache: blockCache, blockCache: blockCache,
futureBlocks: futureBlocks, futureBlocks: futureBlocks,
pow: pow, pow: pow,
lightMode: chainAccess.OdrEnabled(),
} }
// Seed a fast but crypto originating random generator // Seed a fast but crypto originating random generator
seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
@ -122,13 +129,13 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
} }
bc.rand = mrand.New(mrand.NewSource(seed.Int64())) bc.rand = mrand.New(mrand.NewSource(seed.Int64()))
bc.genesisBlock = bc.GetBlockByNumber(0) bc.genesisBlock = bc.GetBlockByNumber(0, access.NoOdr)
if bc.genesisBlock == nil { if bc.genesisBlock == nil {
reader, err := NewDefaultGenesisReader() reader, err := NewDefaultGenesisReader()
if err != nil { if err != nil {
return nil, err return nil, err
} }
bc.genesisBlock, err = WriteGenesisBlock(chainDb, reader) bc.genesisBlock, err = WriteGenesisBlock(bc.chainDb, reader)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -153,13 +160,14 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
// loadLastState loads the last known chain state from the database. This method // loadLastState loads the last known chain state from the database. This method
// assumes that the chain manager mutex is held. // assumes that the chain manager mutex is held.
func (self *BlockChain) loadLastState() error { func (self *BlockChain) loadLastState() error {
if !self.lightMode {
// Restore the last known head block // Restore the last known head block
head := GetHeadBlockHash(self.chainDb) head := GetHeadBlockHash(self.chainDb)
if head == (common.Hash{}) { if head == (common.Hash{}) {
// Corrupt or empty database, init from scratch // Corrupt or empty database, init from scratch
self.Reset() self.Reset()
} else { } else {
if block := self.GetBlock(head); block != nil { if block := self.GetBlock(head, access.NoOdr); block != nil {
// Block found, set as the current head // Block found, set as the current head
self.currentBlock = block self.currentBlock = block
} else { } else {
@ -167,28 +175,35 @@ func (self *BlockChain) loadLastState() error {
self.Reset() self.Reset()
} }
} }
// Restore the last known head header
self.currentHeader = self.currentBlock.Header() self.currentHeader = self.currentBlock.Header()
}
// Restore the last known head header
if head := GetHeadHeaderHash(self.chainDb); head != (common.Hash{}) { if head := GetHeadHeaderHash(self.chainDb); head != (common.Hash{}) {
if header := self.GetHeader(head); header != nil { if header := self.GetHeader(head); header != nil {
self.currentHeader = header self.currentHeader = header
} }
} }
if !self.lightMode {
// Restore the last known head fast block // Restore the last known head fast block
self.currentFastBlock = self.currentBlock self.currentFastBlock = self.currentBlock
if head := GetHeadFastBlockHash(self.chainDb); head != (common.Hash{}) { if head := GetHeadFastBlockHash(self.chainDb); head != (common.Hash{}) {
if block := self.GetBlock(head); block != nil { if block := self.GetBlock(head, access.NoOdr); block != nil {
self.currentFastBlock = block self.currentFastBlock = block
} }
} }
}
// Issue a status log and return // Issue a status log and return
if self.currentHeader != nil {
headerTd := self.GetTd(self.currentHeader.Hash()) headerTd := self.GetTd(self.currentHeader.Hash())
glog.V(logger.Info).Infof("Last header: #%d [%x…] TD=%v", self.currentHeader.Number, self.currentHeader.Hash().Bytes()[:4], headerTd)
}
if !self.lightMode {
blockTd := self.GetTd(self.currentBlock.Hash()) blockTd := self.GetTd(self.currentBlock.Hash())
fastTd := self.GetTd(self.currentFastBlock.Hash()) fastTd := self.GetTd(self.currentFastBlock.Hash())
glog.V(logger.Info).Infof("Last header: #%d [%x…] TD=%v", self.currentHeader.Number, self.currentHeader.Hash().Bytes()[:4], headerTd)
glog.V(logger.Info).Infof("Last block: #%d [%x…] TD=%v", self.currentBlock.Number(), self.currentBlock.Hash().Bytes()[:4], blockTd) glog.V(logger.Info).Infof("Last block: #%d [%x…] TD=%v", self.currentBlock.Number(), self.currentBlock.Hash().Bytes()[:4], blockTd)
glog.V(logger.Info).Infof("Fast block: #%d [%x…] TD=%v", self.currentFastBlock.Number(), self.currentFastBlock.Hash().Bytes()[:4], fastTd) glog.V(logger.Info).Infof("Fast block: #%d [%x…] TD=%v", self.currentFastBlock.Number(), self.currentFastBlock.Hash().Bytes()[:4], fastTd)
}
return nil return nil
} }
@ -208,6 +223,7 @@ func (bc *BlockChain) SetHead(head uint64) {
height = hh height = hh
} }
} }
if !bc.lightMode {
if bc.currentBlock != nil { if bc.currentBlock != nil {
if bh := bc.currentBlock.NumberU64(); bh > height { if bh := bc.currentBlock.NumberU64(); bh > height {
height = bh height = bh
@ -218,6 +234,7 @@ func (bc *BlockChain) SetHead(head uint64) {
height = fbh height = fbh
} }
} }
}
// Gather all the hashes that need deletion // Gather all the hashes that need deletion
drop := make(map[common.Hash]struct{}) drop := make(map[common.Hash]struct{})
@ -225,13 +242,15 @@ func (bc *BlockChain) SetHead(head uint64) {
drop[bc.currentHeader.Hash()] = struct{}{} drop[bc.currentHeader.Hash()] = struct{}{}
bc.currentHeader = bc.GetHeader(bc.currentHeader.ParentHash) bc.currentHeader = bc.GetHeader(bc.currentHeader.ParentHash)
} }
if !bc.lightMode {
for bc.currentBlock != nil && bc.currentBlock.NumberU64() > head { for bc.currentBlock != nil && bc.currentBlock.NumberU64() > head {
drop[bc.currentBlock.Hash()] = struct{}{} drop[bc.currentBlock.Hash()] = struct{}{}
bc.currentBlock = bc.GetBlock(bc.currentBlock.ParentHash()) bc.currentBlock = bc.GetBlock(bc.currentBlock.ParentHash(), access.NoOdr)
} }
for bc.currentFastBlock != nil && bc.currentFastBlock.NumberU64() > head { for bc.currentFastBlock != nil && bc.currentFastBlock.NumberU64() > head {
drop[bc.currentFastBlock.Hash()] = struct{}{} drop[bc.currentFastBlock.Hash()] = struct{}{}
bc.currentFastBlock = bc.GetBlock(bc.currentFastBlock.ParentHash()) bc.currentFastBlock = bc.GetBlock(bc.currentFastBlock.ParentHash(), access.NoOdr)
}
} }
// Roll back the canonical chain numbering // Roll back the canonical chain numbering
for i := height; i > head; i-- { for i := height; i > head; i-- {
@ -250,10 +269,12 @@ func (bc *BlockChain) SetHead(head uint64) {
bc.blockCache.Purge() bc.blockCache.Purge()
bc.futureBlocks.Purge() bc.futureBlocks.Purge()
if !bc.lightMode {
// Update all computed fields to the new head // Update all computed fields to the new head
if bc.currentBlock == nil { if bc.currentBlock == nil {
bc.currentBlock = bc.genesisBlock bc.currentBlock = bc.genesisBlock
} }
}
if bc.currentHeader == nil { if bc.currentHeader == nil {
bc.currentHeader = bc.genesisBlock.Header() bc.currentHeader = bc.genesisBlock.Header()
} }
@ -276,11 +297,11 @@ func (bc *BlockChain) SetHead(head uint64) {
// irrelevant what the chain contents were prior. // irrelevant what the chain contents were prior.
func (self *BlockChain) FastSyncCommitHead(hash common.Hash) error { func (self *BlockChain) FastSyncCommitHead(hash common.Hash) error {
// Make sure that both the block as well at its state trie exists // Make sure that both the block as well at its state trie exists
block := self.GetBlock(hash) block := self.GetBlock(hash, access.NoOdr)
if block == nil { if block == nil {
return fmt.Errorf("non existent block [%x…]", hash[:4]) return fmt.Errorf("non existent block [%x…]", hash[:4])
} }
if _, err := trie.NewSecure(block.Root(), self.chainDb); err != nil { if _, err := trie.NewSecure(block.Root(), self.chainDb, nil); err != nil {
return err return err
} }
// If all checks out, manually set the head block // If all checks out, manually set the head block
@ -296,14 +317,46 @@ func (self *BlockChain) GasLimit() *big.Int {
self.mu.RLock() self.mu.RLock()
defer self.mu.RUnlock() defer self.mu.RUnlock()
if self.lightMode {
return self.currentHeader.GasLimit
} else {
return self.currentBlock.GasLimit() return self.currentBlock.GasLimit()
}
} }
func (self *BlockChain) LastBlockHash() common.Hash { func (self *BlockChain) LastBlockHash() common.Hash {
self.mu.RLock() self.mu.RLock()
defer self.mu.RUnlock() defer self.mu.RUnlock()
if self.lightMode {
return self.currentHeader.Hash()
} else {
return self.currentBlock.Hash() return self.currentBlock.Hash()
}
}
func (self *BlockChain) LastBlockNumberU64() uint64 {
self.mu.RLock()
defer self.mu.RUnlock()
if self.lightMode {
return self.currentHeader.Number.Uint64()
} else {
return self.currentBlock.NumberU64()
}
}
// CurrentBlockHeader returns the header of the current block, which is
// currentBlock.Header in full client mode and currentHeader in light mode
func (self *BlockChain) CurrentBlockHeader() *types.Header {
self.mu.RLock()
defer self.mu.RUnlock()
if self.lightMode {
return self.currentHeader
} else {
return self.currentBlock.Header()
}
} }
// CurrentHeader retrieves the current head header of the canonical chain. The // CurrentHeader retrieves the current head header of the canonical chain. The
@ -318,9 +371,25 @@ func (self *BlockChain) CurrentHeader() *types.Header {
// CurrentBlock retrieves the current head block of the canonical chain. The // CurrentBlock retrieves the current head block of the canonical chain. The
// block is retrieved from the blockchain's internal cache. // block is retrieved from the blockchain's internal cache.
func (self *BlockChain) CurrentBlock() *types.Block { func (self *BlockChain) CurrentBlock() *types.Block {
return self.CurrentBlockOdr(access.NoOdr)
}
// CurrentBlockOdr retrieves the current head block, using ODR in light mode
func (self *BlockChain) CurrentBlockOdr(ctx *access.OdrContext) *types.Block {
self.mu.RLock() self.mu.RLock()
defer self.mu.RUnlock() defer self.mu.RUnlock()
if self.lightMode {
hash := self.LastBlockHash()
var bhash common.Hash
if self.currentBlock != nil {
bhash = self.currentBlock.Hash()
}
if self.currentBlock == nil || bytes.Compare(bhash[:], hash[:]) != 0 {
self.currentBlock = self.GetBlock(hash, ctx)
}
}
return self.currentBlock return self.currentBlock
} }
@ -337,15 +406,29 @@ func (self *BlockChain) Status() (td *big.Int, currentBlock common.Hash, genesis
self.mu.RLock() self.mu.RLock()
defer self.mu.RUnlock() defer self.mu.RUnlock()
return self.GetTd(self.currentBlock.Hash()), self.currentBlock.Hash(), self.genesisBlock.Hash() return self.GetTd(self.LastBlockHash()), self.LastBlockHash(), self.genesisBlock.Hash()
} }
func (self *BlockChain) SetProcessor(proc types.BlockProcessor) { func (self *BlockChain) SetProcessor(proc types.BlockProcessor) {
self.processor = proc self.processor = proc
} }
func (self *BlockChain) State() (*state.StateDB, error) { func (self *BlockChain) StateNoOdr() (*state.StateDB, error) {
return state.New(self.CurrentBlock().Root(), self.chainDb) return self.State(access.NullCtx)
}
func (self *BlockChain) State(ctx *access.OdrContext) (*state.StateDB, error) {
var root common.Hash
if self.lightMode {
if self.currentHeader != nil {
root = self.currentHeader.Root
} else {
root = common.Hash{}
}
} else {
root = self.CurrentBlock().Root()
}
return state.New(root, self.chainAccess, ctx)
} }
// Reset purges the entire blockchain, restoring it to its genesis state. // Reset purges the entire blockchain, restoring it to its genesis state.
@ -378,7 +461,7 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) {
// Export writes the active chain to the given writer. // Export writes the active chain to the given writer.
func (self *BlockChain) Export(w io.Writer) error { func (self *BlockChain) Export(w io.Writer) error {
if err := self.ExportN(w, uint64(0), self.currentBlock.NumberU64()); err != nil { if err := self.ExportN(w, uint64(0), self.CurrentBlock().NumberU64()); err != nil {
return err return err
} }
return nil return nil
@ -396,7 +479,7 @@ func (self *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
glog.V(logger.Info).Infof("exporting %d blocks...\n", last-first+1) glog.V(logger.Info).Infof("exporting %d blocks...\n", last-first+1)
for nr := first; nr <= last; nr++ { for nr := first; nr <= last; nr++ {
block := self.GetBlockByNumber(nr) block := self.GetBlockByNumber(nr, access.NoOdr)
if block == nil { if block == nil {
return fmt.Errorf("export failed on #%d: not found", nr) return fmt.Errorf("export failed on #%d: not found", nr)
} }
@ -481,13 +564,13 @@ func (self *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
// GetBody retrieves a block body (transactions and uncles) from the database by // GetBody retrieves a block body (transactions and uncles) from the database by
// hash, caching it if found. // hash, caching it if found.
func (self *BlockChain) GetBody(hash common.Hash) *types.Body { func (self *BlockChain) GetBody(hash common.Hash, ctx *access.OdrContext) *types.Body {
// Short circuit if the body's already in the cache, retrieve otherwise // Short circuit if the body's already in the cache, retrieve otherwise
if cached, ok := self.bodyCache.Get(hash); ok { if cached, ok := self.bodyCache.Get(hash); ok {
body := cached.(*types.Body) body := cached.(*types.Body)
return body return body
} }
body := GetBody(self.chainDb, hash) body := GetBody(self.chainAccess, hash, ctx)
if body == nil { if body == nil {
return nil return nil
} }
@ -498,12 +581,12 @@ func (self *BlockChain) GetBody(hash common.Hash) *types.Body {
// GetBodyRLP retrieves a block body in RLP encoding from the database by hash, // GetBodyRLP retrieves a block body in RLP encoding from the database by hash,
// caching it if found. // caching it if found.
func (self *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue { func (self *BlockChain) GetBodyRLP(hash common.Hash, ctx *access.OdrContext) rlp.RawValue {
// Short circuit if the body's already in the cache, retrieve otherwise // Short circuit if the body's already in the cache, retrieve otherwise
if cached, ok := self.bodyRLPCache.Get(hash); ok { if cached, ok := self.bodyRLPCache.Get(hash); ok {
return cached.(rlp.RawValue) return cached.(rlp.RawValue)
} }
body := GetBodyRLP(self.chainDb, hash) body := GetBodyRLP(self.chainAccess, hash, ctx)
if len(body) == 0 { if len(body) == 0 {
return nil return nil
} }
@ -521,7 +604,7 @@ func (self *BlockChain) GetTd(hash common.Hash) *big.Int {
} }
td := GetTd(self.chainDb, hash) td := GetTd(self.chainDb, hash)
if td == nil { if td == nil {
return nil return big.NewInt(0)
} }
// Cache the found body for next time and return // Cache the found body for next time and return
self.tdCache.Add(hash, td) self.tdCache.Add(hash, td)
@ -531,16 +614,20 @@ func (self *BlockChain) GetTd(hash common.Hash) *big.Int {
// HasBlock checks if a block is fully present in the database or not, caching // HasBlock checks if a block is fully present in the database or not, caching
// it if present. // it if present.
func (bc *BlockChain) HasBlock(hash common.Hash) bool { func (bc *BlockChain) HasBlock(hash common.Hash) bool {
return bc.GetBlock(hash) != nil return bc.GetBlock(hash, access.NoOdr) != nil
}
func (self *BlockChain) GetBlockNoOdr(hash common.Hash) *types.Block {
return self.GetBlock(hash, access.NoOdr)
} }
// GetBlock retrieves a block from the database by hash, caching it if found. // GetBlock retrieves a block from the database by hash, caching it if found.
func (self *BlockChain) GetBlock(hash common.Hash) *types.Block { func (self *BlockChain) GetBlock(hash common.Hash, ctx *access.OdrContext) *types.Block {
// Short circuit if the block's already in the cache, retrieve otherwise // Short circuit if the block's already in the cache, retrieve otherwise
if block, ok := self.blockCache.Get(hash); ok { if block, ok := self.blockCache.Get(hash); ok {
return block.(*types.Block) return block.(*types.Block)
} }
block := GetBlock(self.chainDb, hash) block := GetBlock(self.chainAccess, hash, ctx)
if block == nil { if block == nil {
return nil return nil
} }
@ -551,12 +638,12 @@ func (self *BlockChain) GetBlock(hash common.Hash) *types.Block {
// GetBlockByNumber retrieves a block from the database by number, caching it // GetBlockByNumber retrieves a block from the database by number, caching it
// (associated with its hash) if found. // (associated with its hash) if found.
func (self *BlockChain) GetBlockByNumber(number uint64) *types.Block { func (self *BlockChain) GetBlockByNumber(number uint64, ctx *access.OdrContext) *types.Block {
hash := GetCanonicalHash(self.chainDb, number) hash := GetCanonicalHash(self.chainDb, number)
if hash == (common.Hash{}) { if hash == (common.Hash{}) {
return nil return nil
} }
return self.GetBlock(hash) return self.GetBlock(hash, ctx)
} }
// GetBlockHashesFromHash retrieves a number of block hashes starting at a given // GetBlockHashesFromHash retrieves a number of block hashes starting at a given
@ -585,7 +672,7 @@ func (self *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []c
// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors. // GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors.
func (self *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) { func (self *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) {
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
block := self.GetBlock(hash) block := self.GetBlock(hash, access.NoOdr)
if block == nil { if block == nil {
break break
} }
@ -601,7 +688,7 @@ func (self *BlockChain) GetUnclesInChain(block *types.Block, length int) []*type
uncles := []*types.Header{} uncles := []*types.Header{}
for i := 0; block != nil && i < length; i++ { for i := 0; block != nil && i < length; i++ {
uncles = append(uncles, block.Uncles()...) uncles = append(uncles, block.Uncles()...)
block = self.GetBlock(block.ParentHash()) block = self.GetBlock(block.ParentHash(), access.NoOdr)
} }
return uncles return uncles
} }
@ -824,16 +911,16 @@ func (self *BlockChain) Rollback(chain []common.Hash) {
for i := len(chain) - 1; i >= 0; i-- { for i := len(chain) - 1; i >= 0; i-- {
hash := chain[i] hash := chain[i]
if self.currentHeader.Hash() == hash { if self.currentHeader != nil && self.currentHeader.Hash() == hash {
self.currentHeader = self.GetHeader(self.currentHeader.ParentHash) self.currentHeader = self.GetHeader(self.currentHeader.ParentHash)
WriteHeadHeaderHash(self.chainDb, self.currentHeader.Hash()) WriteHeadHeaderHash(self.chainDb, self.currentHeader.Hash())
} }
if self.currentFastBlock.Hash() == hash { if self.currentFastBlock != nil && self.currentFastBlock.Hash() == hash {
self.currentFastBlock = self.GetBlock(self.currentFastBlock.ParentHash()) self.currentFastBlock = self.GetBlock(self.currentFastBlock.ParentHash(), access.NoOdr)
WriteHeadFastBlockHash(self.chainDb, self.currentFastBlock.Hash()) WriteHeadFastBlockHash(self.chainDb, self.currentFastBlock.Hash())
} }
if self.currentBlock.Hash() == hash { if self.currentBlock != nil && self.currentBlock.Hash() == hash {
self.currentBlock = self.GetBlock(self.currentBlock.ParentHash()) self.currentBlock = self.GetBlock(self.currentBlock.ParentHash(), access.NoOdr)
WriteHeadBlockHash(self.chainDb, self.currentBlock.Hash()) WriteHeadBlockHash(self.chainDb, self.currentBlock.Hash())
} }
} }
@ -1161,12 +1248,12 @@ func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
// first reduce whoever is higher bound // first reduce whoever is higher bound
if oldBlock.NumberU64() > newBlock.NumberU64() { if oldBlock.NumberU64() > newBlock.NumberU64() {
// reduce old chain // reduce old chain
for oldBlock = oldBlock; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = self.GetBlock(oldBlock.ParentHash()) { for oldBlock = oldBlock; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = self.GetBlock(oldBlock.ParentHash(), access.NoOdr) {
deletedTxs = append(deletedTxs, oldBlock.Transactions()...) deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
} }
} else { } else {
// reduce new chain and append new chain blocks for inserting later on // reduce new chain and append new chain blocks for inserting later on
for newBlock = newBlock; newBlock != nil && newBlock.NumberU64() != oldBlock.NumberU64(); newBlock = self.GetBlock(newBlock.ParentHash()) { for newBlock = newBlock; newBlock != nil && newBlock.NumberU64() != oldBlock.NumberU64(); newBlock = self.GetBlock(newBlock.ParentHash(), access.NoOdr) {
newChain = append(newChain, newBlock) newChain = append(newChain, newBlock)
} }
} }
@ -1186,7 +1273,7 @@ func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
newChain = append(newChain, newBlock) newChain = append(newChain, newBlock)
deletedTxs = append(deletedTxs, oldBlock.Transactions()...) deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
oldBlock, newBlock = self.GetBlock(oldBlock.ParentHash()), self.GetBlock(newBlock.ParentHash()) oldBlock, newBlock = self.GetBlock(oldBlock.ParentHash(), access.NoOdr), self.GetBlock(newBlock.ParentHash(), access.NoOdr)
if oldBlock == nil { if oldBlock == nil {
return fmt.Errorf("Invalid old chain") return fmt.Errorf("Invalid old chain")
} }
@ -1209,7 +1296,7 @@ func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
if err := PutTransactions(self.chainDb, block, block.Transactions()); err != nil { if err := PutTransactions(self.chainDb, block, block.Transactions()); err != nil {
return err return err
} }
receipts := GetBlockReceipts(self.chainDb, block.Hash()) receipts := GetBlockReceipts(self.chainAccess, block.Hash(), access.NoOdr)
// write receipts // write receipts
if err := PutReceipts(self.chainDb, receipts); err != nil { if err := PutReceipts(self.chainDb, receipts); err != nil {
return err return err

View file

@ -28,6 +28,7 @@ import (
"github.com/ethereum/ethash" "github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -51,13 +52,14 @@ func thePow() pow.PoW {
func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain { func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain {
var eventMux event.TypeMux var eventMux event.TypeMux
WriteTestNetGenesisBlock(db, 0) WriteTestNetGenesisBlock(db, 0)
blockchain, err := NewBlockChain(db, thePow(), &eventMux) ca := access.NewDbChainAccess(db)
blockchain, err := NewBlockChain(ca, thePow(), &eventMux)
if err != nil { if err != nil {
t.Error("failed creating chainmanager:", err) t.Error("failed creating chainmanager:", err)
t.FailNow() t.FailNow()
return nil return nil
} }
blockMan := NewBlockProcessor(db, nil, blockchain, &eventMux) blockMan := NewBlockProcessor(ca, nil, blockchain, &eventMux)
blockchain.SetProcessor(blockMan) blockchain.SetProcessor(blockMan)
return blockchain return blockchain
@ -73,8 +75,8 @@ func testFork(t *testing.T, processor *BlockProcessor, i, n int, full bool, comp
// Assert the chains have the same header/block at #i // Assert the chains have the same header/block at #i
var hash1, hash2 common.Hash var hash1, hash2 common.Hash
if full { if full {
hash1 = processor.bc.GetBlockByNumber(uint64(i)).Hash() hash1 = processor.bc.GetBlockByNumber(uint64(i), access.NoOdr).Hash()
hash2 = processor2.bc.GetBlockByNumber(uint64(i)).Hash() hash2 = processor2.bc.GetBlockByNumber(uint64(i), access.NoOdr).Hash()
} else { } else {
hash1 = processor.bc.GetHeaderByNumber(uint64(i)).Hash() hash1 = processor.bc.GetHeaderByNumber(uint64(i)).Hash()
hash2 = processor2.bc.GetHeaderByNumber(uint64(i)).Hash() hash2 = processor2.bc.GetHeaderByNumber(uint64(i)).Hash()
@ -120,7 +122,7 @@ func testFork(t *testing.T, processor *BlockProcessor, i, n int, full bool, comp
func printChain(bc *BlockChain) { func printChain(bc *BlockChain) {
for i := bc.CurrentBlock().Number().Uint64(); i > 0; i-- { for i := bc.CurrentBlock().Number().Uint64(); i > 0; i-- {
b := bc.GetBlockByNumber(uint64(i)) b := bc.GetBlockByNumber(uint64(i), access.NoOdr)
fmt.Printf("\t%x %v\n", b.Hash(), b.Difficulty()) fmt.Printf("\t%x %v\n", b.Hash(), b.Difficulty())
} }
} }
@ -138,8 +140,8 @@ func testBlockChainImport(chain []*types.Block, processor *BlockProcessor) error
} }
// Manually insert the block into the database, but don't reorganize (allows subsequent testing) // Manually insert the block into the database, but don't reorganize (allows subsequent testing)
processor.bc.mu.Lock() processor.bc.mu.Lock()
WriteTd(processor.chainDb, block.Hash(), new(big.Int).Add(block.Difficulty(), processor.bc.GetTd(block.ParentHash()))) WriteTd(processor.chainAccess.Db(), block.Hash(), new(big.Int).Add(block.Difficulty(), processor.bc.GetTd(block.ParentHash())))
WriteBlock(processor.chainDb, block) WriteBlock(processor.chainAccess.Db(), block)
processor.bc.mu.Unlock() processor.bc.mu.Unlock()
} }
return nil return nil
@ -155,8 +157,8 @@ func testHeaderChainImport(chain []*types.Header, processor *BlockProcessor) err
} }
// Manually insert the header into the database, but don't reorganize (allows subsequent testing) // Manually insert the header into the database, but don't reorganize (allows subsequent testing)
processor.bc.mu.Lock() processor.bc.mu.Lock()
WriteTd(processor.chainDb, header.Hash(), new(big.Int).Add(header.Difficulty, processor.bc.GetTd(header.ParentHash))) WriteTd(processor.chainAccess.Db(), header.Hash(), new(big.Int).Add(header.Difficulty, processor.bc.GetTd(header.ParentHash)))
WriteHeader(processor.chainDb, header) WriteHeader(processor.chainAccess.Db(), header)
processor.bc.mu.Unlock() processor.bc.mu.Unlock()
} }
return nil return nil
@ -452,7 +454,8 @@ func makeBlockChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.B
func chm(genesis *types.Block, db ethdb.Database) *BlockChain { func chm(genesis *types.Block, db ethdb.Database) *BlockChain {
var eventMux event.TypeMux var eventMux event.TypeMux
bc := &BlockChain{chainDb: db, genesisBlock: genesis, eventMux: &eventMux, pow: FakePow{}, rand: rand.New(rand.NewSource(0))} ca := access.NewDbChainAccess(db)
bc := &BlockChain{chainAccess: ca, chainDb: db, genesisBlock: genesis, eventMux: &eventMux, pow: FakePow{}, rand: rand.New(rand.NewSource(0))}
bc.headerCache, _ = lru.New(100) bc.headerCache, _ = lru.New(100)
bc.bodyCache, _ = lru.New(100) bc.bodyCache, _ = lru.New(100)
bc.bodyRLPCache, _ = lru.New(100) bc.bodyRLPCache, _ = lru.New(100)
@ -500,7 +503,7 @@ func testReorg(t *testing.T, first, second []int, td int64, full bool) {
// Check that the chain is valid number and link wise // Check that the chain is valid number and link wise
if full { if full {
prev := bc.CurrentBlock() prev := bc.CurrentBlock()
for block := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 1); block.NumberU64() != 0; prev, block = block, bc.GetBlockByNumber(block.NumberU64()-1) { for block := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64()-1, access.NoOdr); block.NumberU64() != 0; prev, block = block, bc.GetBlockByNumber(block.NumberU64()-1, access.NoOdr) {
if prev.ParentHash() != block.Hash() { if prev.ParentHash() != block.Hash() {
t.Errorf("parent block hash mismatch: have %x, want %x", prev.ParentHash(), block.Hash()) t.Errorf("parent block hash mismatch: have %x, want %x", prev.ParentHash(), block.Hash())
} }
@ -587,7 +590,7 @@ func testReorgBadHashes(t *testing.T, full bool) {
defer func() { delete(BadHashes, headers[3].Hash()) }() defer func() { delete(BadHashes, headers[3].Hash()) }()
} }
// Create a new chain manager and check it rolled back the state // Create a new chain manager and check it rolled back the state
ncm, err := NewBlockChain(db, FakePow{}, new(event.TypeMux)) ncm, err := NewBlockChain(access.NewDbChainAccess(db), FakePow{}, new(event.TypeMux))
if err != nil { if err != nil {
t.Fatalf("failed to create new chain manager: %v", err) t.Fatalf("failed to create new chain manager: %v", err)
} }
@ -665,7 +668,7 @@ func testInsertNonceError(t *testing.T, full bool) {
// Check that all no blocks after the failing block have been inserted. // Check that all no blocks after the failing block have been inserted.
for j := 0; j < i-failAt; j++ { for j := 0; j < i-failAt; j++ {
if full { if full {
if block := bc.GetBlockByNumber(failNum + uint64(j)); block != nil { if block := bc.GetBlockByNumber(failNum+uint64(j), access.NoOdr); block != nil {
t.Errorf("test %d: invalid block in chain: %v", i, block) t.Errorf("test %d: invalid block in chain: %v", i, block)
} }
} else { } else {
@ -708,19 +711,21 @@ func TestFastVsFullChains(t *testing.T) {
}) })
// Import the chain as an archive node for the comparison baseline // Import the chain as an archive node for the comparison baseline
archiveDb, _ := ethdb.NewMemDatabase() archiveDb, _ := ethdb.NewMemDatabase()
archiveCa := access.NewDbChainAccess(archiveDb)
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds}) WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux)) archive, _ := NewBlockChain(archiveCa, FakePow{}, new(event.TypeMux))
archive.SetProcessor(NewBlockProcessor(archiveDb, FakePow{}, archive, new(event.TypeMux))) archive.SetProcessor(NewBlockProcessor(archiveCa, FakePow{}, archive, new(event.TypeMux)))
if n, err := archive.InsertChain(blocks); err != nil { if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err) t.Fatalf("failed to process block %d: %v", n, err)
} }
// Fast import the chain as a non-archive node to test // Fast import the chain as a non-archive node to test
fastDb, _ := ethdb.NewMemDatabase() fastDb, _ := ethdb.NewMemDatabase()
fastCa := access.NewDbChainAccess(fastDb)
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds}) WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux)) fast, _ := NewBlockChain(fastCa, FakePow{}, new(event.TypeMux))
fast.SetProcessor(NewBlockProcessor(fastDb, FakePow{}, fast, new(event.TypeMux))) fast.SetProcessor(NewBlockProcessor(fastCa, FakePow{}, fast, new(event.TypeMux)))
headers := make([]*types.Header, len(blocks)) headers := make([]*types.Header, len(blocks))
for i, block := range blocks { for i, block := range blocks {
@ -742,14 +747,14 @@ func TestFastVsFullChains(t *testing.T) {
if fheader, aheader := fast.GetHeader(hash), archive.GetHeader(hash); fheader.Hash() != aheader.Hash() { if fheader, aheader := fast.GetHeader(hash), archive.GetHeader(hash); fheader.Hash() != aheader.Hash() {
t.Errorf("block #%d [%x]: header mismatch: have %v, want %v", num, hash, fheader, aheader) t.Errorf("block #%d [%x]: header mismatch: have %v, want %v", num, hash, fheader, aheader)
} }
if fblock, ablock := fast.GetBlock(hash), archive.GetBlock(hash); fblock.Hash() != ablock.Hash() { if fblock, ablock := fast.GetBlock(hash, access.NoOdr), archive.GetBlock(hash, access.NoOdr); fblock.Hash() != ablock.Hash() {
t.Errorf("block #%d [%x]: block mismatch: have %v, want %v", num, hash, fblock, ablock) t.Errorf("block #%d [%x]: block mismatch: have %v, want %v", num, hash, fblock, ablock)
} else if types.DeriveSha(fblock.Transactions()) != types.DeriveSha(ablock.Transactions()) { } else if types.DeriveSha(fblock.Transactions()) != types.DeriveSha(ablock.Transactions()) {
t.Errorf("block #%d [%x]: transactions mismatch: have %v, want %v", num, hash, fblock.Transactions(), ablock.Transactions()) t.Errorf("block #%d [%x]: transactions mismatch: have %v, want %v", num, hash, fblock.Transactions(), ablock.Transactions())
} else if types.CalcUncleHash(fblock.Uncles()) != types.CalcUncleHash(ablock.Uncles()) { } else if types.CalcUncleHash(fblock.Uncles()) != types.CalcUncleHash(ablock.Uncles()) {
t.Errorf("block #%d [%x]: uncles mismatch: have %v, want %v", num, hash, fblock.Uncles(), ablock.Uncles()) t.Errorf("block #%d [%x]: uncles mismatch: have %v, want %v", num, hash, fblock.Uncles(), ablock.Uncles())
} }
if freceipts, areceipts := GetBlockReceipts(fastDb, hash), GetBlockReceipts(archiveDb, hash); types.DeriveSha(freceipts) != types.DeriveSha(areceipts) { if freceipts, areceipts := GetBlockReceipts(fastCa, hash, access.NoOdr), GetBlockReceipts(archiveCa, hash, access.NoOdr); types.DeriveSha(freceipts) != types.DeriveSha(areceipts) {
t.Errorf("block #%d [%x]: receipts mismatch: have %v, want %v", num, hash, freceipts, areceipts) t.Errorf("block #%d [%x]: receipts mismatch: have %v, want %v", num, hash, freceipts, areceipts)
} }
} }
@ -794,10 +799,10 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
} }
// Import the chain as an archive node and ensure all pointers are updated // Import the chain as an archive node and ensure all pointers are updated
archiveDb, _ := ethdb.NewMemDatabase() archiveDb, _ := ethdb.NewMemDatabase()
archiveCa := access.NewDbChainAccess(archiveDb)
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds}) WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
archive, _ := NewBlockChain(archiveCa, FakePow{}, new(event.TypeMux))
archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux)) archive.SetProcessor(NewBlockProcessor(archiveCa, FakePow{}, archive, new(event.TypeMux)))
archive.SetProcessor(NewBlockProcessor(archiveDb, FakePow{}, archive, new(event.TypeMux)))
if n, err := archive.InsertChain(blocks); err != nil { if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err) t.Fatalf("failed to process block %d: %v", n, err)
@ -808,9 +813,10 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
// Import the chain as a non-archive node and ensure all pointers are updated // Import the chain as a non-archive node and ensure all pointers are updated
fastDb, _ := ethdb.NewMemDatabase() fastDb, _ := ethdb.NewMemDatabase()
fastCa := access.NewDbChainAccess(fastDb)
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds}) WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux)) fast, _ := NewBlockChain(fastCa, FakePow{}, new(event.TypeMux))
fast.SetProcessor(NewBlockProcessor(fastDb, FakePow{}, fast, new(event.TypeMux))) fast.SetProcessor(NewBlockProcessor(fastCa, FakePow{}, fast, new(event.TypeMux)))
headers := make([]*types.Header, len(blocks)) headers := make([]*types.Header, len(blocks))
for i, block := range blocks { for i, block := range blocks {
@ -828,9 +834,10 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
// Import the chain as a light node and ensure all pointers are updated // Import the chain as a light node and ensure all pointers are updated
lightDb, _ := ethdb.NewMemDatabase() lightDb, _ := ethdb.NewMemDatabase()
lightCa := access.NewDbChainAccess(lightDb)
WriteGenesisBlockForTesting(lightDb, GenesisAccount{address, funds}) WriteGenesisBlockForTesting(lightDb, GenesisAccount{address, funds})
light, _ := NewBlockChain(lightDb, FakePow{}, new(event.TypeMux)) light, _ := NewBlockChain(lightCa, FakePow{}, new(event.TypeMux))
light.SetProcessor(NewBlockProcessor(lightDb, FakePow{}, light, new(event.TypeMux))) light.SetProcessor(NewBlockProcessor(lightCa, FakePow{}, light, new(event.TypeMux)))
if n, err := light.InsertHeaderChain(headers, 1); err != nil { if n, err := light.InsertHeaderChain(headers, 1); err != nil {
t.Fatalf("failed to insert header %d: %v", n, err) t.Fatalf("failed to insert header %d: %v", n, err)
@ -895,8 +902,9 @@ func TestChainTxReorgs(t *testing.T) {
}) })
// Import the chain. This runs all block validation rules. // Import the chain. This runs all block validation rules.
evmux := &event.TypeMux{} evmux := &event.TypeMux{}
chainman, _ := NewBlockChain(db, FakePow{}, evmux) ca := access.NewDbChainAccess(db)
chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux)) chainman, _ := NewBlockChain(ca, FakePow{}, evmux)
chainman.SetProcessor(NewBlockProcessor(ca, FakePow{}, chainman, evmux))
if i, err := chainman.InsertChain(chain); err != nil { if i, err := chainman.InsertChain(chain); err != nil {
t.Fatalf("failed to insert original chain[%d]: %v", i, err) t.Fatalf("failed to insert original chain[%d]: %v", i, err)
} }
@ -929,7 +937,7 @@ func TestChainTxReorgs(t *testing.T) {
if GetTransaction(db, tx.Hash()) != nil { if GetTransaction(db, tx.Hash()) != nil {
t.Errorf("drop %d: tx found while shouldn't have been", i) t.Errorf("drop %d: tx found while shouldn't have been", i)
} }
if GetReceipt(db, tx.Hash()) != nil { if GetReceipt(ca, tx.Hash(), access.NoOdr) != nil {
t.Errorf("drop %d: receipt found while shouldn't have been", i) t.Errorf("drop %d: receipt found while shouldn't have been", i)
} }
} }
@ -938,7 +946,7 @@ func TestChainTxReorgs(t *testing.T) {
if GetTransaction(db, tx.Hash()) == nil { if GetTransaction(db, tx.Hash()) == nil {
t.Errorf("add %d: expected tx to be found", i) t.Errorf("add %d: expected tx to be found", i)
} }
if GetReceipt(db, tx.Hash()) == nil { if GetReceipt(ca, tx.Hash(), access.NoOdr) == nil {
t.Errorf("add %d: expected receipt to be found", i) t.Errorf("add %d: expected receipt to be found", i)
} }
} }
@ -947,7 +955,7 @@ func TestChainTxReorgs(t *testing.T) {
if GetTransaction(db, tx.Hash()) == nil { if GetTransaction(db, tx.Hash()) == nil {
t.Errorf("share %d: expected tx to be found", i) t.Errorf("share %d: expected tx to be found", i)
} }
if GetReceipt(db, tx.Hash()) == nil { if GetReceipt(ca, tx.Hash(), access.NoOdr) == nil {
t.Errorf("share %d: expected receipt to be found", i) t.Errorf("share %d: expected receipt to be found", i)
} }
} }

View file

@ -21,6 +21,7 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -164,7 +165,7 @@ func (b *BlockGen) OffsetTime(seconds int64) {
// values. Inserting them into BlockChain requires use of FakePow or // values. Inserting them into BlockChain requires use of FakePow or
// a similar non-validating proof of work implementation. // a similar non-validating proof of work implementation.
func GenerateChain(parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) { func GenerateChain(parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
statedb, err := state.New(parent.Root(), db) statedb, err := state.New(parent.Root(), access.NewDbChainAccess(db), access.NullCtx)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -222,8 +223,9 @@ func newCanonical(n int, full bool) (ethdb.Database, *BlockProcessor, error) {
// Initialize a fresh chain with only a genesis block // Initialize a fresh chain with only a genesis block
genesis, _ := WriteTestNetGenesisBlock(db, 0) genesis, _ := WriteTestNetGenesisBlock(db, 0)
blockchain, _ := NewBlockChain(db, FakePow{}, evmux) ca := access.NewDbChainAccess(db)
processor := NewBlockProcessor(db, FakePow{}, blockchain, evmux) blockchain, _ := NewBlockChain(ca, FakePow{}, evmux)
processor := NewBlockProcessor(ca, FakePow{}, blockchain, evmux)
processor.bc.SetProcessor(processor) processor.bc.SetProcessor(processor)
// Create and inject the requested chain // Create and inject the requested chain

View file

@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -77,14 +78,15 @@ func ExampleGenerateChain() {
// Import the chain. This runs all block validation rules. // Import the chain. This runs all block validation rules.
evmux := &event.TypeMux{} evmux := &event.TypeMux{}
chainman, _ := NewBlockChain(db, FakePow{}, evmux) ca := access.NewDbChainAccess(db)
chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux)) chainman, _ := NewBlockChain(ca, FakePow{}, evmux)
chainman.SetProcessor(NewBlockProcessor(ca, FakePow{}, chainman, evmux))
if i, err := chainman.InsertChain(chain); err != nil { if i, err := chainman.InsertChain(chain); err != nil {
fmt.Printf("insert error (block %d): %v\n", i, err) fmt.Printf("insert error (block %d): %v\n", i, err)
return return
} }
state, _ := chainman.State() state, _ := chainman.State(access.NullCtx)
fmt.Printf("last block: #%d\n", chainman.CurrentBlock().Number()) fmt.Printf("last block: #%d\n", chainman.CurrentBlock().Number())
fmt.Println("balance of addr1:", state.GetBalance(addr1)) fmt.Println("balance of addr1:", state.GetBalance(addr1))
fmt.Println("balance of addr2:", state.GetBalance(addr2)) fmt.Println("balance of addr2:", state.GetBalance(addr2))

View file

@ -23,6 +23,8 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/les/requests"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
@ -183,15 +185,17 @@ func GetHeader(db ethdb.Database, hash common.Hash) *types.Header {
} }
// GetBodyRLP retrieves the block body (transactions and uncles) in RLP encoding. // GetBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
func GetBodyRLP(db ethdb.Database, hash common.Hash) rlp.RawValue { func GetBodyRLP(ca *access.ChainAccess, hash common.Hash, ctx *access.OdrContext) rlp.RawValue {
data, _ := db.Get(append(append(blockPrefix, hash[:]...), bodySuffix...)) //fmt.Println("request block %v", hash)
return data r := requests.NewBlockAccess(ca.Db(), hash, GetHeader)
ca.Retrieve(r, ctx)
return r.GetRlp()
} }
// GetBody retrieves the block body (transactons, uncles) corresponding to the // GetBody retrieves the block body (transactons, uncles) corresponding to the
// hash, nil if none found. // hash, nil if none found.
func GetBody(db ethdb.Database, hash common.Hash) *types.Body { func GetBody(ca *access.ChainAccess, hash common.Hash, ctx *access.OdrContext) *types.Body {
data := GetBodyRLP(db, hash) data := GetBodyRLP(ca, hash, ctx)
if len(data) == 0 { if len(data) == 0 {
return nil return nil
} }
@ -220,13 +224,13 @@ func GetTd(db ethdb.Database, hash common.Hash) *big.Int {
// GetBlock retrieves an entire block corresponding to the hash, assembling it // GetBlock retrieves an entire block corresponding to the hash, assembling it
// back from the stored header and body. // back from the stored header and body.
func GetBlock(db ethdb.Database, hash common.Hash) *types.Block { func GetBlock(ca *access.ChainAccess, hash common.Hash, ctx *access.OdrContext) *types.Block {
// Retrieve the block header and body contents // Retrieve the block header and body contents
header := GetHeader(db, hash) header := GetHeader(ca.Db(), hash)
if header == nil { if header == nil {
return nil return nil
} }
body := GetBody(db, hash) body := GetBody(ca, hash, ctx)
if body == nil { if body == nil {
return nil return nil
} }

View file

@ -24,6 +24,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -121,6 +122,7 @@ func TestHeaderStorage(t *testing.T) {
// Tests block body storage and retrieval operations. // Tests block body storage and retrieval operations.
func TestBodyStorage(t *testing.T) { func TestBodyStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
ca := access.NewDbChainAccess(db)
// Create a test body to move around the database and make sure it's really new // Create a test body to move around the database and make sure it's really new
body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}} body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}}
@ -129,19 +131,19 @@ func TestBodyStorage(t *testing.T) {
rlp.Encode(hasher, body) rlp.Encode(hasher, body)
hash := common.BytesToHash(hasher.Sum(nil)) hash := common.BytesToHash(hasher.Sum(nil))
if entry := GetBody(db, hash); entry != nil { if entry := GetBody(ca, hash, access.NoOdr); entry != nil {
t.Fatalf("Non existent body returned: %v", entry) t.Fatalf("Non existent body returned: %v", entry)
} }
// Write and verify the body in the database // Write and verify the body in the database
if err := WriteBody(db, hash, body); err != nil { if err := WriteBody(db, hash, body); err != nil {
t.Fatalf("Failed to write body into database: %v", err) t.Fatalf("Failed to write body into database: %v", err)
} }
if entry := GetBody(db, hash); entry == nil { if entry := GetBody(ca, hash, access.NoOdr); entry == nil {
t.Fatalf("Stored body not found") t.Fatalf("Stored body not found")
} else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(types.Transactions(body.Transactions)) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) { } else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(types.Transactions(body.Transactions)) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) {
t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body) t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body)
} }
if entry := GetBodyRLP(db, hash); entry == nil { if entry := GetBodyRLP(ca, hash, access.NoOdr); entry == nil {
t.Fatalf("Stored body RLP not found") t.Fatalf("Stored body RLP not found")
} else { } else {
hasher := sha3.NewKeccak256() hasher := sha3.NewKeccak256()
@ -153,7 +155,7 @@ func TestBodyStorage(t *testing.T) {
} }
// Delete the body and verify the execution // Delete the body and verify the execution
DeleteBody(db, hash) DeleteBody(db, hash)
if entry := GetBody(db, hash); entry != nil { if entry := GetBody(ca, hash, access.NoOdr); entry != nil {
t.Fatalf("Deleted body returned: %v", entry) t.Fatalf("Deleted body returned: %v", entry)
} }
} }
@ -161,6 +163,7 @@ func TestBodyStorage(t *testing.T) {
// Tests block storage and retrieval operations. // Tests block storage and retrieval operations.
func TestBlockStorage(t *testing.T) { func TestBlockStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
ca := access.NewDbChainAccess(db)
// Create a test block to move around the database and make sure it's really new // Create a test block to move around the database and make sure it's really new
block := types.NewBlockWithHeader(&types.Header{ block := types.NewBlockWithHeader(&types.Header{
@ -169,20 +172,20 @@ func TestBlockStorage(t *testing.T) {
TxHash: types.EmptyRootHash, TxHash: types.EmptyRootHash,
ReceiptHash: types.EmptyRootHash, ReceiptHash: types.EmptyRootHash,
}) })
if entry := GetBlock(db, block.Hash()); entry != nil { if entry := GetBlock(ca, block.Hash(), access.NoOdr); entry != nil {
t.Fatalf("Non existent block returned: %v", entry) t.Fatalf("Non existent block returned: %v", entry)
} }
if entry := GetHeader(db, block.Hash()); entry != nil { if entry := GetHeader(db, block.Hash()); entry != nil {
t.Fatalf("Non existent header returned: %v", entry) t.Fatalf("Non existent header returned: %v", entry)
} }
if entry := GetBody(db, block.Hash()); entry != nil { if entry := GetBody(ca, block.Hash(), access.NoOdr); entry != nil {
t.Fatalf("Non existent body returned: %v", entry) t.Fatalf("Non existent body returned: %v", entry)
} }
// Write and verify the block in the database // Write and verify the block in the database
if err := WriteBlock(db, block); err != nil { if err := WriteBlock(db, block); err != nil {
t.Fatalf("Failed to write block into database: %v", err) t.Fatalf("Failed to write block into database: %v", err)
} }
if entry := GetBlock(db, block.Hash()); entry == nil { if entry := GetBlock(ca, block.Hash(), access.NoOdr); entry == nil {
t.Fatalf("Stored block not found") t.Fatalf("Stored block not found")
} else if entry.Hash() != block.Hash() { } else if entry.Hash() != block.Hash() {
t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block) t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
@ -192,20 +195,20 @@ func TestBlockStorage(t *testing.T) {
} else if entry.Hash() != block.Header().Hash() { } else if entry.Hash() != block.Header().Hash() {
t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, block.Header()) t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, block.Header())
} }
if entry := GetBody(db, block.Hash()); entry == nil { if entry := GetBody(ca, block.Hash(), access.NoOdr); entry == nil {
t.Fatalf("Stored body not found") t.Fatalf("Stored body not found")
} else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(block.Transactions()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) { } else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(block.Transactions()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) {
t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, &types.Body{block.Transactions(), block.Uncles()}) t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, &types.Body{block.Transactions(), block.Uncles()})
} }
// Delete the block and verify the execution // Delete the block and verify the execution
DeleteBlock(db, block.Hash()) DeleteBlock(db, block.Hash())
if entry := GetBlock(db, block.Hash()); entry != nil { if entry := GetBlock(ca, block.Hash(), access.NoOdr); entry != nil {
t.Fatalf("Deleted block returned: %v", entry) t.Fatalf("Deleted block returned: %v", entry)
} }
if entry := GetHeader(db, block.Hash()); entry != nil { if entry := GetHeader(db, block.Hash()); entry != nil {
t.Fatalf("Deleted header returned: %v", entry) t.Fatalf("Deleted header returned: %v", entry)
} }
if entry := GetBody(db, block.Hash()); entry != nil { if entry := GetBody(ca, block.Hash(), access.NoOdr); entry != nil {
t.Fatalf("Deleted body returned: %v", entry) t.Fatalf("Deleted body returned: %v", entry)
} }
} }
@ -213,6 +216,7 @@ func TestBlockStorage(t *testing.T) {
// Tests that partial block contents don't get reassembled into full blocks. // Tests that partial block contents don't get reassembled into full blocks.
func TestPartialBlockStorage(t *testing.T) { func TestPartialBlockStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
ca := access.NewDbChainAccess(db)
block := types.NewBlockWithHeader(&types.Header{ block := types.NewBlockWithHeader(&types.Header{
Extra: []byte("test block"), Extra: []byte("test block"),
UncleHash: types.EmptyUncleHash, UncleHash: types.EmptyUncleHash,
@ -223,7 +227,7 @@ func TestPartialBlockStorage(t *testing.T) {
if err := WriteHeader(db, block.Header()); err != nil { if err := WriteHeader(db, block.Header()); err != nil {
t.Fatalf("Failed to write header into database: %v", err) t.Fatalf("Failed to write header into database: %v", err)
} }
if entry := GetBlock(db, block.Hash()); entry != nil { if entry := GetBlock(ca, block.Hash(), access.NoOdr); entry != nil {
t.Fatalf("Non existent block returned: %v", entry) t.Fatalf("Non existent block returned: %v", entry)
} }
DeleteHeader(db, block.Hash()) DeleteHeader(db, block.Hash())
@ -232,7 +236,7 @@ func TestPartialBlockStorage(t *testing.T) {
if err := WriteBody(db, block.Hash(), &types.Body{block.Transactions(), block.Uncles()}); err != nil { if err := WriteBody(db, block.Hash(), &types.Body{block.Transactions(), block.Uncles()}); err != nil {
t.Fatalf("Failed to write body into database: %v", err) t.Fatalf("Failed to write body into database: %v", err)
} }
if entry := GetBlock(db, block.Hash()); entry != nil { if entry := GetBlock(ca, block.Hash(), access.NoOdr); entry != nil {
t.Fatalf("Non existent block returned: %v", entry) t.Fatalf("Non existent block returned: %v", entry)
} }
DeleteBody(db, block.Hash()) DeleteBody(db, block.Hash())
@ -244,7 +248,7 @@ func TestPartialBlockStorage(t *testing.T) {
if err := WriteBody(db, block.Hash(), &types.Body{block.Transactions(), block.Uncles()}); err != nil { if err := WriteBody(db, block.Hash(), &types.Body{block.Transactions(), block.Uncles()}); err != nil {
t.Fatalf("Failed to write body into database: %v", err) t.Fatalf("Failed to write body into database: %v", err)
} }
if entry := GetBlock(db, block.Hash()); entry == nil { if entry := GetBlock(ca, block.Hash(), access.NoOdr); entry == nil {
t.Fatalf("Stored block not found") t.Fatalf("Stored block not found")
} else if entry.Hash() != block.Hash() { } else if entry.Hash() != block.Hash() {
t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block) t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)

View file

@ -25,6 +25,7 @@ import (
"strings" "strings"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -61,7 +62,8 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
} }
// creating with empty hash always works // creating with empty hash always works
statedb, _ := state.New(common.Hash{}, chainDb) ca := access.NewDbChainAccess(chainDb)
statedb, _ := state.New(common.Hash{}, ca, access.NullCtx)
for addr, account := range genesis.Alloc { for addr, account := range genesis.Alloc {
address := common.HexToAddress(addr) address := common.HexToAddress(addr)
statedb.AddBalance(address, common.String2Big(account.Balance)) statedb.AddBalance(address, common.String2Big(account.Balance))
@ -85,7 +87,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
Root: root, Root: root,
}, nil, nil, nil) }, nil, nil, nil)
if block := GetBlock(chainDb, block.Hash()); block != nil { if block := GetBlock(ca, block.Hash(), access.NoOdr); block != nil {
glog.V(logger.Info).Infoln("Genesis block already in chain. Writing canonical number") glog.V(logger.Info).Infoln("Genesis block already in chain. Writing canonical number")
err := WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) err := WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())
if err != nil { if err != nil {
@ -118,7 +120,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
// GenesisBlockForTesting creates a block in which addr has the given wei balance. // GenesisBlockForTesting creates a block in which addr has the given wei balance.
// The state trie of the block is written to db. the passed db needs to contain a state root // The state trie of the block is written to db. the passed db needs to contain a state root
func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block { func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block {
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db), access.NullCtx)
obj := statedb.GetOrNewStateObject(addr) obj := statedb.GetOrNewStateObject(addr)
obj.SetBalance(balance) obj.SetBalance(balance)
root, err := statedb.Commit() root, err := statedb.Commit()

View file

@ -18,6 +18,7 @@ package core
import ( import (
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
) )
@ -29,6 +30,7 @@ type Backend interface {
BlockChain() *BlockChain BlockChain() *BlockChain
TxPool() *TxPool TxPool() *TxPool
ChainDb() ethdb.Database ChainDb() ethdb.Database
ChainAccess() *access.ChainAccess
DappDb() ethdb.Database DappDb() ethdb.Database
EventMux() *event.TypeMux EventMux() *event.TypeMux
} }

View file

@ -21,6 +21,7 @@ import (
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
) )
type Account struct { type Account struct {
@ -45,7 +46,7 @@ func (self *StateDB) RawDump() World {
it := self.trie.Iterator() it := self.trie.Iterator()
for it.Next() { for it.Next() {
addr := self.trie.GetKey(it.Key) addr := self.trie.GetKey(it.Key)
stateObject := NewStateObjectFromBytes(common.BytesToAddress(addr), it.Value, self.db) stateObject := NewStateObjectFromBytes(common.BytesToAddress(addr), it.Value, self.ca, access.NullCtx)
account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.nonce, Root: common.Bytes2Hex(stateObject.Root()), CodeHash: common.Bytes2Hex(stateObject.codeHash)} account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.nonce, Root: common.Bytes2Hex(stateObject.Root()), CodeHash: common.Bytes2Hex(stateObject.codeHash)}
account.Storage = make(map[string]string) account.Storage = make(map[string]string)

View file

@ -20,6 +20,7 @@ import (
"sync" "sync"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
) )
type account struct { type account struct {
@ -39,7 +40,7 @@ type ManagedState struct {
// ManagedState returns a new managed state with the statedb as it's backing layer // ManagedState returns a new managed state with the statedb as it's backing layer
func ManageState(statedb *StateDB) *ManagedState { func ManageState(statedb *StateDB) *ManagedState {
return &ManagedState{ return &ManagedState{
StateDB: statedb.Copy(), StateDB: statedb.Copy(access.NullCtx),
accounts: make(map[string]*account), accounts: make(map[string]*account),
} }
} }

View file

@ -20,6 +20,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
) )
@ -27,7 +28,7 @@ var addr = common.BytesToAddress([]byte("test"))
func create() (*ManagedState, *account) { func create() (*ManagedState, *account) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := New(common.Hash{}, db) statedb, _ := New(common.Hash{}, access.NewDbChainAccess(db), access.NullCtx)
ms := ManageState(statedb) ms := ManageState(statedb)
so := &StateObject{address: addr, nonce: 100} so := &StateObject{address: addr, nonce: 100}
ms.StateDB.stateObjects[addr.Str()] = so ms.StateDB.stateObjects[addr.Str()] = so

View file

@ -22,8 +22,9 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/les/requests"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
@ -57,7 +58,7 @@ func (self Storage) Copy() Storage {
type StateObject struct { type StateObject struct {
// State database for storing state changes // State database for storing state changes
db ethdb.Database ca *access.ChainAccess
trie *trie.SecureTrie trie *trie.SecureTrie
// Address belonging to this account // Address belonging to this account
@ -83,14 +84,14 @@ type StateObject struct {
dirty bool dirty bool
} }
func NewStateObject(address common.Address, db ethdb.Database) *StateObject { func NewStateObject(address common.Address, ca *access.ChainAccess) *StateObject {
object := &StateObject{db: db, address: address, balance: new(big.Int), dirty: true} object := &StateObject{ca: ca, address: address, balance: new(big.Int), dirty: true}
object.trie, _ = trie.NewSecure(common.Hash{}, db) object.trie, _ = trie.NewSecure(common.Hash{}, ca.Db(), nil)
object.storage = make(Storage) object.storage = make(Storage)
return object return object
} }
func NewStateObjectFromBytes(address common.Address, data []byte, db ethdb.Database) *StateObject { func NewStateObjectFromBytes(address common.Address, data []byte, ca *access.ChainAccess, ctx *access.OdrContext) *StateObject {
var extobject struct { var extobject struct {
Nonce uint64 Nonce uint64
Balance *big.Int Balance *big.Int
@ -102,20 +103,20 @@ func NewStateObjectFromBytes(address common.Address, data []byte, db ethdb.Datab
glog.Errorf("can't decode state object %x: %v", address, err) glog.Errorf("can't decode state object %x: %v", address, err)
return nil return nil
} }
trie, err := trie.NewSecure(extobject.Root, db) trie, err := trie.NewSecure(extobject.Root, ca.Db(), requests.NewTrieAccess(ca, extobject.Root, ca.Db()))
if err != nil { if err != nil {
// TODO: bubble this up or panic // TODO: bubble this up or panic
glog.Errorf("can't create account trie with root %x: %v", extobject.Root[:], err) glog.Errorf("can't create account trie with root %x: %v", extobject.Root[:], err)
return nil return nil
} }
object := &StateObject{address: address, db: db} object := &StateObject{address: address, ca: ca}
object.nonce = extobject.Nonce object.nonce = extobject.Nonce
object.balance = extobject.Balance object.balance = extobject.Balance
object.codeHash = extobject.CodeHash object.codeHash = extobject.CodeHash
object.trie = trie object.trie = trie
object.storage = make(map[string]common.Hash) object.storage = make(map[string]common.Hash)
object.code, _ = db.Get(extobject.CodeHash) object.code = requests.RetrieveNodeData(ca, common.BytesToHash(extobject.CodeHash), ctx)
return object return object
} }
@ -128,30 +129,31 @@ func (self *StateObject) MarkForDeletion() {
} }
} }
func (c *StateObject) getAddr(addr common.Hash) common.Hash { func (c *StateObject) getAddr(addr common.Hash, ctx *access.OdrContext) common.Hash {
var ret []byte var ret []byte
rlp.DecodeBytes(c.trie.Get(addr[:]), &ret) value := c.trie.Get(addr[:], ctx)
rlp.DecodeBytes(value, &ret)
return common.BytesToHash(ret) return common.BytesToHash(ret)
} }
func (c *StateObject) setAddr(addr []byte, value common.Hash) { func (c *StateObject) setAddr(addr []byte, value common.Hash, ctx *access.OdrContext) {
v, err := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00")) v, err := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
if err != nil { if err != nil {
// if RLPing failed we better panic and not fail silently. This would be considered a consensus issue // if RLPing failed we better panic and not fail silently. This would be considered a consensus issue
panic(err) panic(err)
} }
c.trie.Update(addr, v) c.trie.Update(addr, v, ctx)
} }
func (self *StateObject) Storage() Storage { func (self *StateObject) Storage() Storage {
return self.storage return self.storage
} }
func (self *StateObject) GetState(key common.Hash) common.Hash { func (self *StateObject) GetState(key common.Hash, ctx *access.OdrContext) common.Hash {
strkey := key.Str() strkey := key.Str()
value, exists := self.storage[strkey] value, exists := self.storage[strkey]
if !exists { if !exists {
value = self.getAddr(key) value = self.getAddr(key, ctx)
if (value != common.Hash{}) { if (value != common.Hash{}) {
self.storage[strkey] = value self.storage[strkey] = value
} }
@ -166,14 +168,14 @@ func (self *StateObject) SetState(k, value common.Hash) {
} }
// Update updates the current cached storage to the trie // Update updates the current cached storage to the trie
func (self *StateObject) Update() { func (self *StateObject) Update(ctx *access.OdrContext) {
for key, value := range self.storage { for key, value := range self.storage {
if (value == common.Hash{}) { if (value == common.Hash{}) {
self.trie.Delete([]byte(key)) self.trie.Delete([]byte(key), ctx)
continue continue
} }
self.setAddr([]byte(key), value) self.setAddr([]byte(key), value, ctx)
} }
} }
@ -206,7 +208,7 @@ func (c *StateObject) St() Storage {
func (c *StateObject) ReturnGas(gas, price *big.Int) {} func (c *StateObject) ReturnGas(gas, price *big.Int) {}
func (self *StateObject) Copy() *StateObject { func (self *StateObject) Copy() *StateObject {
stateObject := NewStateObject(self.Address(), self.db) stateObject := NewStateObject(self.Address(), self.ca)
stateObject.balance.Set(self.balance) stateObject.balance.Set(self.balance)
stateObject.codeHash = common.CopyBytes(self.codeHash) stateObject.codeHash = common.CopyBytes(self.codeHash)
stateObject.nonce = self.nonce stateObject.nonce = self.nonce

View file

@ -24,6 +24,7 @@ import (
checker "gopkg.in/check.v1" checker "gopkg.in/check.v1"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
) )
@ -77,12 +78,12 @@ func (s *StateSuite) TestDump(c *checker.C) {
func (s *StateSuite) SetUpTest(c *checker.C) { func (s *StateSuite) SetUpTest(c *checker.C) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
s.state, _ = New(common.Hash{}, db) s.state, _ = New(common.Hash{}, access.NewDbChainAccess(db), access.NullCtx)
} }
func TestNull(t *testing.T) { func TestNull(t *testing.T) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
state, _ := New(common.Hash{}, db) state, _ := New(common.Hash{}, access.NewDbChainAccess(db), access.NullCtx)
address := common.HexToAddress("0x823140710bf13990e4500136726d8b55") address := common.HexToAddress("0x823140710bf13990e4500136726d8b55")
state.CreateAccount(address) state.CreateAccount(address)
@ -105,7 +106,7 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
// set inital state object value // set inital state object value
s.state.SetState(stateobjaddr, storageaddr, data1) s.state.SetState(stateobjaddr, storageaddr, data1)
// get snapshot of current state // get snapshot of current state
snapshot := s.state.Copy() snapshot := s.state.Copy(access.NullCtx)
// set new state object value // set new state object value
s.state.SetState(stateobjaddr, storageaddr, data2) s.state.SetState(stateobjaddr, storageaddr, data2)
@ -122,7 +123,7 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
// printing/logging in tests (-check.vv does not work) // printing/logging in tests (-check.vv does not work)
func TestSnapshot2(t *testing.T) { func TestSnapshot2(t *testing.T) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
state, _ := New(common.Hash{}, db) state, _ := New(common.Hash{}, access.NewDbChainAccess(db), access.NullCtx)
stateobjaddr0 := toAddr([]byte("so0")) stateobjaddr0 := toAddr([]byte("so0"))
stateobjaddr1 := toAddr([]byte("so1")) stateobjaddr1 := toAddr([]byte("so1"))
@ -161,7 +162,7 @@ func TestSnapshot2(t *testing.T) {
t.Fatalf("deleted object not nil when getting") t.Fatalf("deleted object not nil when getting")
} }
snapshot := state.Copy() snapshot := state.Copy(access.NullCtx)
state.Set(snapshot) state.Set(snapshot)
so0Restored := state.GetStateObject(stateobjaddr0) so0Restored := state.GetStateObject(stateobjaddr0)

View file

@ -22,6 +22,8 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/les/requests"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
@ -38,8 +40,9 @@ var StartingNonce uint64
// * Contracts // * Contracts
// * Accounts // * Accounts
type StateDB struct { type StateDB struct {
db ethdb.Database ca *access.ChainAccess
trie *trie.SecureTrie trie *trie.SecureTrie
ctx *access.OdrContext
stateObjects map[string]*StateObject stateObjects map[string]*StateObject
@ -52,15 +55,17 @@ type StateDB struct {
} }
// Create a new state from a given trie // Create a new state from a given trie
func New(root common.Hash, db ethdb.Database) (*StateDB, error) { // When ODR is used, context is specified when creating/copying a state.
tr, err := trie.NewSecure(root, db) func New(root common.Hash, ca *access.ChainAccess, ctx *access.OdrContext) (*StateDB, error) {
tr, err := trie.NewSecure(root, ca.Db(), requests.NewTrieAccess(ca, root, ca.Db()))
if err != nil { if err != nil {
glog.Errorf("can't create state trie with root %x: %v", root[:], err) glog.Errorf("can't create state trie with root %x: %v", root[:], err)
return nil, err return nil, err
} }
return &StateDB{ return &StateDB{
db: db, ca: ca,
trie: tr, trie: tr,
ctx: ctx,
stateObjects: make(map[string]*StateObject), stateObjects: make(map[string]*StateObject),
refund: new(big.Int), refund: new(big.Int),
logs: make(map[common.Hash]vm.Logs), logs: make(map[common.Hash]vm.Logs),
@ -141,7 +146,7 @@ func (self *StateDB) GetCode(addr common.Address) []byte {
func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash { func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
stateObject := self.GetStateObject(a) stateObject := self.GetStateObject(a)
if stateObject != nil { if stateObject != nil {
return stateObject.GetState(b) return stateObject.GetState(b, self.ctx)
} }
return common.Hash{} return common.Hash{}
@ -208,10 +213,10 @@ func (self *StateDB) UpdateStateObject(stateObject *StateObject) {
//addr := stateObject.Address() //addr := stateObject.Address()
if len(stateObject.CodeHash()) > 0 { if len(stateObject.CodeHash()) > 0 {
self.db.Put(stateObject.CodeHash(), stateObject.code) self.ca.Db().Put(stateObject.CodeHash(), stateObject.code)
} }
addr := stateObject.Address() addr := stateObject.Address()
self.trie.Update(addr[:], stateObject.RlpEncode()) self.trie.Update(addr[:], stateObject.RlpEncode(), self.ctx)
} }
// Delete the given state object and delete it from the state trie // Delete the given state object and delete it from the state trie
@ -219,8 +224,7 @@ func (self *StateDB) DeleteStateObject(stateObject *StateObject) {
stateObject.deleted = true stateObject.deleted = true
addr := stateObject.Address() addr := stateObject.Address()
self.trie.Delete(addr[:]) self.trie.Delete(addr[:], self.ctx)
//delete(self.stateObjects, addr.Str())
} }
// Retrieve a state object given my the address. Nil if not found // Retrieve a state object given my the address. Nil if not found
@ -234,12 +238,12 @@ func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObje
return stateObject return stateObject
} }
data := self.trie.Get(addr[:]) data := self.trie.Get(addr[:], self.ctx)
if len(data) == 0 { if len(data) == 0 {
return nil return nil
} }
stateObject = NewStateObjectFromBytes(addr, []byte(data), self.db) stateObject = NewStateObjectFromBytes(addr, []byte(data), self.ca, self.ctx)
self.SetStateObject(stateObject) self.SetStateObject(stateObject)
return stateObject return stateObject
@ -265,7 +269,7 @@ func (self *StateDB) newStateObject(addr common.Address) *StateObject {
glog.Infof("(+) %x\n", addr) glog.Infof("(+) %x\n", addr)
} }
stateObject := NewStateObject(addr, self.db) stateObject := NewStateObject(addr, self.ca)
stateObject.SetNonce(StartingNonce) stateObject.SetNonce(StartingNonce)
self.stateObjects[addr.Str()] = stateObject self.stateObjects[addr.Str()] = stateObject
@ -295,9 +299,15 @@ func (self *StateDB) CreateAccount(addr common.Address) vm.Account {
// Setting, copying of the state methods // Setting, copying of the state methods
// //
func (self *StateDB) Copy() *StateDB { // Copy state, keep the original ODR context
func (self *StateDB) CopyWithCtx() *StateDB {
return self.Copy(self.ctx)
}
// Copy with a new context
func (self *StateDB) Copy(ctx *access.OdrContext) *StateDB {
// ignore error - we assume state-to-be-copied always exists // ignore error - we assume state-to-be-copied always exists
state, _ := New(common.Hash{}, self.db) state, _ := New(common.Hash{}, self.ca, ctx)
state.trie = self.trie state.trie = self.trie
for k, stateObject := range self.stateObjects { for k, stateObject := range self.stateObjects {
state.stateObjects[k] = stateObject.Copy() state.stateObjects[k] = stateObject.Copy()
@ -337,7 +347,7 @@ func (s *StateDB) IntermediateRoot() common.Hash {
if stateObject.remove { if stateObject.remove {
s.DeleteStateObject(stateObject) s.DeleteStateObject(stateObject)
} else { } else {
stateObject.Update() stateObject.Update(s.ctx)
s.UpdateStateObject(stateObject) s.UpdateStateObject(stateObject)
} }
stateObject.dirty = false stateObject.dirty = false
@ -348,14 +358,14 @@ func (s *StateDB) IntermediateRoot() common.Hash {
// Commit commits all state changes to the database. // Commit commits all state changes to the database.
func (s *StateDB) Commit() (root common.Hash, err error) { func (s *StateDB) Commit() (root common.Hash, err error) {
return s.commit(s.db) return s.commit(s.ca.Db())
} }
// CommitBatch commits all state changes to a write batch but does not // CommitBatch commits all state changes to a write batch but does not
// execute the batch. It is used to validate state changes against // execute the batch. It is used to validate state changes against
// the root hash stored in a block. // the root hash stored in a block.
func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) { func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) {
batch = s.db.NewBatch() batch = s.ca.Db().NewBatch()
root, _ = s.commit(batch) root, _ = s.commit(batch)
return root, batch return root, batch
} }
@ -370,12 +380,12 @@ func (s *StateDB) commit(db trie.DatabaseWriter) (common.Hash, error) {
s.DeleteStateObject(stateObject) s.DeleteStateObject(stateObject)
} else { } else {
// Write any storage changes in the state object to its trie. // Write any storage changes in the state object to its trie.
stateObject.Update() stateObject.Update(s.ctx)
// Commit the trie of the object to the batch. // Commit the trie of the object to the batch.
// This updates the trie root internally, so // This updates the trie root internally, so
// getting the root hash of the storage trie // getting the root hash of the storage trie
// through UpdateStateObject is fast. // through UpdateStateObject is fast.
if _, err := stateObject.trie.CommitTo(db); err != nil { if _, err := stateObject.trie.CommitTo(db, s.ctx); err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
// Update the object in the account trie. // Update the object in the account trie.
@ -383,7 +393,7 @@ func (s *StateDB) commit(db trie.DatabaseWriter) (common.Hash, error) {
} }
stateObject.dirty = false stateObject.dirty = false
} }
return s.trie.CommitTo(db) return s.trie.CommitTo(db, s.ctx)
} }
func (self *StateDB) Refunds() *big.Int { func (self *StateDB) Refunds() *big.Int {

View file

@ -22,6 +22,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
) )
@ -38,7 +39,7 @@ type testAccount struct {
func makeTestState() (ethdb.Database, common.Hash, []*testAccount) { func makeTestState() (ethdb.Database, common.Hash, []*testAccount) {
// Create an empty state // Create an empty state
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
state, _ := New(common.Hash{}, db) state, _ := New(common.Hash{}, access.NewDbChainAccess(db), access.NullCtx)
// Fill it with some arbitrary data // Fill it with some arbitrary data
accounts := []*testAccount{} accounts := []*testAccount{}
@ -68,7 +69,7 @@ func makeTestState() (ethdb.Database, common.Hash, []*testAccount) {
// checkStateAccounts cross references a reconstructed state with an expected // checkStateAccounts cross references a reconstructed state with an expected
// account array. // account array.
func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accounts []*testAccount) { func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accounts []*testAccount) {
state, _ := New(root, db) state, _ := New(root, access.NewDbChainAccess(db), access.NullCtx)
for i, acc := range accounts { for i, acc := range accounts {
if balance := state.GetBalance(acc.address); balance.Cmp(acc.balance) != 0 { if balance := state.GetBalance(acc.address); balance.Cmp(acc.balance) != 0 {

View file

@ -22,6 +22,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -36,7 +37,7 @@ func transaction(nonce uint64, gaslimit *big.Int, key *ecdsa.PrivateKey) *types.
func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db), access.NullCtx)
var m event.TypeMux var m event.TypeMux
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()
@ -163,7 +164,7 @@ func TestTransactionChainFork(t *testing.T) {
addr := crypto.PubkeyToAddress(key.PublicKey) addr := crypto.PubkeyToAddress(key.PublicKey)
resetState := func() { resetState := func() {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db), access.NullCtx)
pool.currentState = func() (*state.StateDB, error) { return statedb, nil } pool.currentState = func() (*state.StateDB, error) { return statedb, nil }
currentState, _ := pool.currentState() currentState, _ := pool.currentState()
currentState.AddBalance(addr, big.NewInt(100000000000000)) currentState.AddBalance(addr, big.NewInt(100000000000000))
@ -189,7 +190,7 @@ func TestTransactionDoubleNonce(t *testing.T) {
addr := crypto.PubkeyToAddress(key.PublicKey) addr := crypto.PubkeyToAddress(key.PublicKey)
resetState := func() { resetState := func() {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db), access.NullCtx)
pool.currentState = func() (*state.StateDB, error) { return statedb, nil } pool.currentState = func() (*state.StateDB, error) { return statedb, nil }
currentState, _ := pool.currentState() currentState, _ := pool.currentState()
currentState.AddBalance(addr, big.NewInt(100000000000000)) currentState.AddBalance(addr, big.NewInt(100000000000000))

View file

@ -20,6 +20,8 @@ import (
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/les/requests"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
@ -29,8 +31,8 @@ import (
) )
var ( var (
receiptsPre = []byte("receipts-")
blockReceiptsPre = []byte("receipts-block-") blockReceiptsPre = []byte("receipts-block-")
receiptsPre = []byte("receipts-")
) )
// PutTransactions stores the transactions in the given database // PutTransactions stores the transactions in the given database
@ -119,9 +121,13 @@ func DeleteReceipt(db ethdb.Database, txHash common.Hash) {
} }
// GetReceipt returns a receipt by hash // GetReceipt returns a receipt by hash
func GetReceipt(db ethdb.Database, txHash common.Hash) *types.Receipt { func GetReceipt(ca *access.ChainAccess, txHash common.Hash, ctx *access.OdrContext) *types.Receipt {
data, _ := db.Get(append(receiptsPre, txHash[:]...)) data, _ := ca.Db().Get(append(receiptsPre, txHash[:]...))
if len(data) == 0 { if len(data) == 0 {
if ctx != nil {
// we need a txHash -> blockHash mapping or and ODR request by txHash to do this
glog.V(logger.Error).Infoln("GetReceipt ODR not yet supported")
}
return nil return nil
} }
var receipt types.ReceiptForStorage var receipt types.ReceiptForStorage
@ -134,21 +140,10 @@ func GetReceipt(db ethdb.Database, txHash common.Hash) *types.Receipt {
// GetBlockReceipts returns the receipts generated by the transactions // GetBlockReceipts returns the receipts generated by the transactions
// included in block's given hash. // included in block's given hash.
func GetBlockReceipts(db ethdb.Database, hash common.Hash) types.Receipts { func GetBlockReceipts(ca *access.ChainAccess, hash common.Hash, ctx *access.OdrContext) types.Receipts {
data, _ := db.Get(append(blockReceiptsPre, hash[:]...)) r := requests.NewReceiptsAccess(ca.Db(), hash, GetHeader, PutReceipts, PutBlockReceipts)
if len(data) == 0 { ca.Retrieve(r, ctx)
return nil return r.GetReceipts()
}
rs := []*types.ReceiptForStorage{}
if err := rlp.DecodeBytes(data, &rs); err != nil {
glog.V(logger.Error).Infof("invalid receipt array RLP for hash %x: %v", hash, err)
return nil
}
receipts := make(types.Receipts, len(rs))
for i, receipt := range rs {
receipts[i] = (*types.Receipt)(receipt)
}
return receipts
} }
// PutBlockReceipts stores the block's transactions associated receipts // PutBlockReceipts stores the block's transactions associated receipts

View file

@ -326,6 +326,13 @@ func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Ext
func (b *Block) Header() *Header { return CopyHeader(b.header) } func (b *Block) Header() *Header { return CopyHeader(b.header) }
func (b *Block) SafeNumber() *big.Int {
if b == nil {
return big.NewInt(0)
}
return b.Number()
}
func (b *Block) HashNoNonce() common.Hash { func (b *Block) HashNoNonce() common.Hash {
return b.header.HashNoNonce() return b.header.HashNoNonce()
} }

View file

@ -20,6 +20,7 @@ import (
"bytes" "bytes"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
) )
@ -35,7 +36,7 @@ func DeriveSha(list DerivableList) common.Hash {
for i := 0; i < list.Len(); i++ { for i := 0; i < list.Len(); i++ {
keybuf.Reset() keybuf.Reset()
rlp.Encode(keybuf, uint(i)) rlp.Encode(keybuf, uint(i))
trie.Update(keybuf.Bytes(), list.GetRlp(i)) trie.Update(keybuf.Bytes(), list.GetRlp(i), access.NoOdr)
} }
return trie.Hash() return trie.Hash()
} }

View file

@ -20,6 +20,7 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
@ -59,7 +60,7 @@ func (self *VMEnv) SetDepth(i int) { self.depth = i }
func (self *VMEnv) VmType() vm.Type { return self.typ } func (self *VMEnv) VmType() vm.Type { return self.typ }
func (self *VMEnv) SetVmType(t vm.Type) { self.typ = t } func (self *VMEnv) SetVmType(t vm.Type) { self.typ = t }
func (self *VMEnv) GetHash(n uint64) common.Hash { func (self *VMEnv) GetHash(n uint64) common.Hash {
for block := self.chain.GetBlock(self.header.ParentHash); block != nil; block = self.chain.GetBlock(block.ParentHash()) { for block := self.chain.GetBlock(self.header.ParentHash, access.NoOdr); block != nil; block = self.chain.GetBlock(block.ParentHash(), access.NoOdr) {
if block.NumberU64() == n { if block.NumberU64() == n {
return block.Hash() return block.Hash()
} }
@ -76,7 +77,7 @@ func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
} }
func (self *VMEnv) MakeSnapshot() vm.Database { func (self *VMEnv) MakeSnapshot() vm.Database {
return self.state.Copy() return self.state.CopyWithCtx()
} }
func (self *VMEnv) SetSnapshot(copy vm.Database) { func (self *VMEnv) SetSnapshot(copy vm.Database) {

View file

@ -38,12 +38,14 @@ import (
"github.com/ethereum/go-ethereum/common/httpclient" "github.com/ethereum/go-ethereum/common/httpclient"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/miner"
@ -60,6 +62,8 @@ const (
autoDAGcheckInterval = 10 * time.Hour autoDAGcheckInterval = 10 * time.Hour
autoDAGepochHeight = epochLength / 2 autoDAGepochHeight = epochLength / 2
useLightProtocol = true
) )
var ( var (
@ -96,6 +100,7 @@ type Config struct {
GenesisBlock *types.Block // used by block tests GenesisBlock *types.Block // used by block tests
FastSync bool FastSync bool
Olympic bool Olympic bool
Mode Mode
BlockChainVersion int BlockChainVersion int
SkipBcVersionCheck bool // e.g. blockchain export SkipBcVersionCheck bool // e.g. blockchain export
@ -231,6 +236,8 @@ type Ethereum struct {
chainDb ethdb.Database // Block chain database chainDb ethdb.Database // Block chain database
dappDb ethdb.Database // Dapp database dappDb ethdb.Database // Dapp database
chainAccess *access.ChainAccess // blockchain access layer
//*** SERVICES *** //*** SERVICES ***
// State manager for processing new blocks and managing the over all states // State manager for processing new blocks and managing the over all states
blockProcessor *core.BlockProcessor blockProcessor *core.BlockProcessor
@ -240,6 +247,7 @@ type Ethereum struct {
whisper *whisper.Whisper whisper *whisper.Whisper
pow *ethash.Ethash pow *ethash.Ethash
protocolManager *ProtocolManager protocolManager *ProtocolManager
lightProtocolManager *les.ProtocolManager
SolcPath string SolcPath string
solc *compiler.Solidity solc *compiler.Solidity
@ -251,6 +259,7 @@ type Ethereum struct {
GpobaseCorrectionFactor int GpobaseCorrectionFactor int
httpclient *httpclient.HTTPClient httpclient *httpclient.HTTPClient
Mode Mode
net *p2p.Server net *p2p.Server
eventMux *event.TypeMux eventMux *event.TypeMux
@ -297,10 +306,10 @@ func New(config *Config) (*Ethereum, error) {
if err := upgradeChainDatabase(chainDb); err != nil { if err := upgradeChainDatabase(chainDb); err != nil {
return nil, err return nil, err
} }
if err := addMipmapBloomBins(chainDb); err != nil { chainAccess := access.NewChainAccess(chainDb, config.Mode == LightMode)
if err := addMipmapBloomBins(chainAccess); err != nil {
return nil, err return nil, err
} }
dappDb, err := newdb(filepath.Join(config.DataDir, "dapp")) dappDb, err := newdb(filepath.Join(config.DataDir, "dapp"))
if err != nil { if err != nil {
if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] { if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] {
@ -366,6 +375,7 @@ func New(config *Config) (*Ethereum, error) {
eth := &Ethereum{ eth := &Ethereum{
shutdownChan: make(chan bool), shutdownChan: make(chan bool),
chainDb: chainDb, chainDb: chainDb,
chainAccess: chainAccess,
dappDb: dappDb, dappDb: dappDb,
eventMux: &event.TypeMux{}, eventMux: &event.TypeMux{},
accountManager: config.AccountManager, accountManager: config.AccountManager,
@ -385,6 +395,7 @@ func New(config *Config) (*Ethereum, error) {
GpobaseStepUp: config.GpobaseStepUp, GpobaseStepUp: config.GpobaseStepUp,
GpobaseCorrectionFactor: config.GpobaseCorrectionFactor, GpobaseCorrectionFactor: config.GpobaseCorrectionFactor,
httpclient: httpclient.New(config.DocRoot), httpclient: httpclient.New(config.DocRoot),
Mode: config.Mode,
} }
if config.PowTest { if config.PowTest {
@ -397,22 +408,36 @@ func New(config *Config) (*Ethereum, error) {
eth.pow = ethash.New() eth.pow = ethash.New()
} }
//genesis := core.GenesisBlock(uint64(config.GenesisNonce), stateDb) //genesis := core.GenesisBlock(uint64(config.GenesisNonce), stateDb)
eth.blockchain, err = core.NewBlockChain(chainDb, eth.pow, eth.EventMux()) eth.blockchain, err = core.NewBlockChain(chainAccess, eth.pow, eth.EventMux())
if err != nil { if err != nil {
if err == core.ErrNoGenesis { if err == core.ErrNoGenesis {
return nil, fmt.Errorf(`Genesis block not found. Please supply a genesis block with the "--genesis /path/to/file" argument`) return nil, fmt.Errorf(`Genesis block not found. Please supply a genesis block with the "--genesis /path/to/file" argument`)
} }
return nil, err return nil, err
} }
newPool := core.NewTxPool(eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit) newPool := core.NewTxPool(eth.EventMux(), eth.blockchain.StateNoOdr, eth.blockchain.GasLimit)
eth.txPool = newPool eth.txPool = newPool
eth.blockProcessor = core.NewBlockProcessor(chainDb, eth.pow, eth.blockchain, eth.EventMux()) eth.blockProcessor = core.NewBlockProcessor(chainAccess, eth.pow, eth.blockchain, eth.EventMux())
eth.blockchain.SetProcessor(eth.blockProcessor) eth.blockchain.SetProcessor(eth.blockProcessor)
if eth.protocolManager, err = NewProtocolManager(config.FastSync, config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil {
if config.Mode != LightMode {
if eth.protocolManager, err = NewProtocolManager(config.Mode, config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainAccess); err != nil {
return nil, err return nil, err
} }
}
if useLightProtocol {
if eth.lightProtocolManager, err = les.NewProtocolManager(les.Mode(config.Mode), config.NetworkId, eth.eventMux, eth.pow, eth.blockchain, chainAccess); err != nil {
return nil, err
}
}
if config.Mode == LightMode {
eth.miner = miner.NewDummy(eth, eth.EventMux(), eth.pow)
} else {
eth.miner = miner.New(eth, eth.EventMux(), eth.pow) eth.miner = miner.New(eth, eth.EventMux(), eth.pow)
}
eth.miner.SetGasPrice(config.GasPrice) eth.miner.SetGasPrice(config.GasPrice)
eth.miner.SetExtra(config.ExtraData) eth.miner.SetExtra(config.ExtraData)
@ -425,7 +450,14 @@ func New(config *Config) (*Ethereum, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
protocols := append([]p2p.Protocol{}, eth.protocolManager.SubProtocols...) protocols := []p2p.Protocol{}
if eth.protocolManager != nil {
protocols = append(protocols, eth.protocolManager.SubProtocols...)
}
if eth.lightProtocolManager != nil {
protocols = append(protocols, eth.lightProtocolManager.SubProtocols...)
}
if config.Shh { if config.Shh {
protocols = append(protocols, eth.whisper.Protocol()) protocols = append(protocols, eth.whisper.Protocol())
} }
@ -493,16 +525,29 @@ func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper } func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper }
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
func (s *Ethereum) ChainAccess() *access.ChainAccess { return s.chainAccess }
func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb } func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb }
func (s *Ethereum) IsListening() bool { return true } // Always listening func (s *Ethereum) IsListening() bool { return true } // Always listening
func (s *Ethereum) PeerCount() int { return s.net.PeerCount() } func (s *Ethereum) PeerCount() int { return s.net.PeerCount() }
func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() } func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() }
func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers } func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers }
func (s *Ethereum) ClientVersion() string { return s.clientVersion } func (s *Ethereum) ClientVersion() string { return s.clientVersion }
func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } func (s *Ethereum) EthVersion() int {
if s.protocolManager == nil {
return 0
} else {
return int(s.protocolManager.SubProtocols[0].Version)
}
}
func (s *Ethereum) NetVersion() int { return s.netVersionId } func (s *Ethereum) NetVersion() int { return s.netVersionId }
func (s *Ethereum) ShhVersion() int { return s.shhVersionId } func (s *Ethereum) ShhVersion() int { return s.shhVersionId }
func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } func (s *Ethereum) Downloader() *downloader.Downloader {
if s.protocolManager == nil {
return nil
} else {
return s.protocolManager.downloader
}
}
// Start the ethereum // Start the ethereum
func (s *Ethereum) Start() error { func (s *Ethereum) Start() error {
@ -522,7 +567,13 @@ func (s *Ethereum) Start() error {
s.StartAutoDAG() s.StartAutoDAG()
} }
if s.protocolManager != nil {
s.protocolManager.Start() s.protocolManager.Start()
}
if s.lightProtocolManager != nil {
s.lightProtocolManager.Start()
}
if s.whisper != nil { if s.whisper != nil {
s.whisper.Start() s.whisper.Start()
@ -554,9 +605,14 @@ func (self *Ethereum) AddPeer(nodeURL string) error {
func (s *Ethereum) Stop() { func (s *Ethereum) Stop() {
s.net.Stop() s.net.Stop()
s.blockchain.Stop() s.blockchain.Stop()
if s.protocolManager != nil {
s.protocolManager.Stop() s.protocolManager.Stop()
}
s.txPool.Stop() s.txPool.Stop()
s.eventMux.Stop() s.eventMux.Stop()
if s.lightProtocolManager != nil {
s.lightProtocolManager.Stop()
}
if s.whisper != nil { if s.whisper != nil {
s.whisper.Stop() s.whisper.Stop()
} }
@ -594,7 +650,7 @@ func (self *Ethereum) StartAutoDAG() {
select { select {
case <-timer: case <-timer:
glog.V(logger.Info).Infof("checking DAG (ethash dir: %s)", ethash.DefaultDir) glog.V(logger.Info).Infof("checking DAG (ethash dir: %s)", ethash.DefaultDir)
currentBlock := self.BlockChain().CurrentBlock().NumberU64() currentBlock := self.BlockChain().LastBlockNumberU64()
thisEpoch := currentBlock / epochLength thisEpoch := currentBlock / epochLength
if nextEpoch <= thisEpoch { if nextEpoch <= thisEpoch {
if currentBlock%epochLength > autoDAGepochHeight { if currentBlock%epochLength > autoDAGepochHeight {
@ -732,9 +788,10 @@ func upgradeChainDatabase(db ethdb.Database) error {
return nil return nil
} }
func addMipmapBloomBins(db ethdb.Database) (err error) { func addMipmapBloomBins(ca *access.ChainAccess) (err error) {
const mipmapVersion uint = 2 const mipmapVersion uint = 2
db := ca.Db()
// check if the version is set. We ignore data for now since there's // check if the version is set. We ignore data for now since there's
// only one version so we can easily ignore it for now // only one version so we can easily ignore it for now
var data []byte var data []byte
@ -756,7 +813,7 @@ func addMipmapBloomBins(db ethdb.Database) (err error) {
return return
} }
}() }()
latestBlock := core.GetBlock(db, core.GetHeadBlockHash(db)) latestBlock := core.GetBlock(ca, core.GetHeadBlockHash(db), access.NoOdr)
if latestBlock == nil { // clean database if latestBlock == nil { // clean database
return return
} }
@ -768,7 +825,7 @@ func addMipmapBloomBins(db ethdb.Database) (err error) {
if (hash == common.Hash{}) { if (hash == common.Hash{}) {
return fmt.Errorf("chain db corrupted. Could not find block %d.", i) return fmt.Errorf("chain db corrupted. Could not find block %d.", i)
} }
core.WriteMipmapBloom(db, i, core.GetBlockReceipts(db, hash)) core.WriteMipmapBloom(db, i, core.GetBlockReceipts(ca, hash, access.NoOdr))
} }
glog.V(logger.Info).Infoln("upgrade completed in", time.Since(tstart)) glog.V(logger.Info).Infoln("upgrade completed in", time.Since(tstart))
return nil return nil

View file

@ -6,6 +6,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -50,7 +51,7 @@ func TestMipmapUpgrade(t *testing.T) {
} }
} }
err := addMipmapBloomBins(db) err := addMipmapBloomBins(access.NewDbChainAccess(db))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -44,6 +44,7 @@ var (
MaxBodyFetch = 128 // Amount of block bodies to be fetched per retrieval request MaxBodyFetch = 128 // Amount of block bodies to be fetched per retrieval request
MaxReceiptFetch = 256 // Amount of transaction receipts to allow fetching per request MaxReceiptFetch = 256 // Amount of transaction receipts to allow fetching per request
MaxStateFetch = 384 // Amount of node state values to allow fetching per request MaxStateFetch = 384 // Amount of node state values to allow fetching per request
MaxProofsFetch = 64 // Amount of merkle proofs to allow fetching per request
hashTTL = 5 * time.Second // [eth/61] Time it takes for a hash request to time out hashTTL = 5 * time.Second // [eth/61] Time it takes for a hash request to time out
blockSoftTTL = 3 * time.Second // [eth/61] Request completion threshold for increasing or decreasing a peer's bandwidth blockSoftTTL = 3 * time.Second // [eth/61] Request completion threshold for increasing or decreasing a peer's bandwidth
@ -156,13 +157,13 @@ type Downloader struct {
} }
// New creates a new downloader to fetch hashes and blocks from remote peers. // New creates a new downloader to fetch hashes and blocks from remote peers.
func New(stateDb ethdb.Database, mux *event.TypeMux, hasHeader headerCheckFn, hasBlock blockCheckFn, getHeader headerRetrievalFn, func New(mode SyncMode, stateDb ethdb.Database, mux *event.TypeMux, hasHeader headerCheckFn, hasBlock blockCheckFn, getHeader headerRetrievalFn,
getBlock blockRetrievalFn, headHeader headHeaderRetrievalFn, headBlock headBlockRetrievalFn, headFastBlock headFastBlockRetrievalFn, getBlock blockRetrievalFn, headHeader headHeaderRetrievalFn, headBlock headBlockRetrievalFn, headFastBlock headFastBlockRetrievalFn,
commitHeadBlock headBlockCommitterFn, getTd tdRetrievalFn, insertHeaders headerChainInsertFn, insertBlocks blockChainInsertFn, commitHeadBlock headBlockCommitterFn, getTd tdRetrievalFn, insertHeaders headerChainInsertFn, insertBlocks blockChainInsertFn,
insertReceipts receiptChainInsertFn, rollback chainRollbackFn, dropPeer peerDropFn) *Downloader { insertReceipts receiptChainInsertFn, rollback chainRollbackFn, dropPeer peerDropFn) *Downloader {
return &Downloader{ return &Downloader{
mode: FullSync, mode: mode,
mux: mux, mux: mux,
queue: newQueue(stateDb), queue: newQueue(stateDb),
peers: newPeerSet(), peers: newPeerSet(),
@ -225,6 +226,10 @@ func (d *Downloader) RegisterPeer(id string, version int, head common.Hash,
getRelHeaders relativeHeaderFetcherFn, getAbsHeaders absoluteHeaderFetcherFn, getBlockBodies blockBodyFetcherFn, getRelHeaders relativeHeaderFetcherFn, getAbsHeaders absoluteHeaderFetcherFn, getBlockBodies blockBodyFetcherFn,
getReceipts receiptFetcherFn, getNodeData stateFetcherFn) error { getReceipts receiptFetcherFn, getNodeData stateFetcherFn) error {
if d.mode == NoSync {
return nil
}
glog.V(logger.Detail).Infoln("Registering peer", id) glog.V(logger.Detail).Infoln("Registering peer", id)
if err := d.peers.Register(newPeer(id, version, head, getRelHashes, getAbsHashes, getBlocks, getRelHeaders, getAbsHeaders, getBlockBodies, getReceipts, getNodeData)); err != nil { if err := d.peers.Register(newPeer(id, version, head, getRelHashes, getAbsHashes, getBlocks, getRelHeaders, getAbsHeaders, getBlockBodies, getReceipts, getNodeData)); err != nil {
glog.V(logger.Error).Infoln("Register failed:", err) glog.V(logger.Error).Infoln("Register failed:", err)
@ -237,6 +242,10 @@ func (d *Downloader) RegisterPeer(id string, version int, head common.Hash,
// the specified peer. An effort is also made to return any pending fetches into // the specified peer. An effort is also made to return any pending fetches into
// the queue. // the queue.
func (d *Downloader) UnregisterPeer(id string) error { func (d *Downloader) UnregisterPeer(id string) error {
if d.mode == NoSync {
return nil
}
glog.V(logger.Detail).Infoln("Unregistering peer", id) glog.V(logger.Detail).Infoln("Unregistering peer", id)
if err := d.peers.Unregister(id); err != nil { if err := d.peers.Unregister(id); err != nil {
glog.V(logger.Error).Infoln("Unregister failed:", err) glog.V(logger.Error).Infoln("Unregister failed:", err)
@ -249,6 +258,10 @@ func (d *Downloader) UnregisterPeer(id string) error {
// Synchronise tries to sync up our local block chain with a remote peer, both // Synchronise tries to sync up our local block chain with a remote peer, both
// adding various sanity checks as well as wrapping it with various log entries. // adding various sanity checks as well as wrapping it with various log entries.
func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode SyncMode) error { func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode SyncMode) error {
if d.mode == NoSync {
return nil
}
glog.V(logger.Detail).Infof("Attempting synchronisation: %v, head [%x…], TD %v", id, head[:4], td) glog.V(logger.Detail).Infof("Attempting synchronisation: %v, head [%x…], TD %v", id, head[:4], td)
err := d.synchronise(id, head, td, mode) err := d.synchronise(id, head, td, mode)
@ -276,6 +289,10 @@ func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode
// it will use the best peer possible and synchronize if it's TD is higher than our own. If any of the // it will use the best peer possible and synchronize if it's TD is higher than our own. If any of the
// checks fail an error will be returned. This method is synchronous // checks fail an error will be returned. This method is synchronous
func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode SyncMode) error { func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode SyncMode) error {
if d.mode == NoSync {
return nil
}
// Mock out the synchonisation if testing // Mock out the synchonisation if testing
if d.synchroniseMock != nil { if d.synchroniseMock != nil {
return d.synchroniseMock(id, hash) return d.synchroniseMock(id, hash)
@ -331,6 +348,10 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode
// syncWithPeer starts a block synchronization based on the hash chain from the // syncWithPeer starts a block synchronization based on the hash chain from the
// specified peer and head hash. // specified peer and head hash.
func (d *Downloader) syncWithPeer(p *peer, hash common.Hash, td *big.Int) (err error) { func (d *Downloader) syncWithPeer(p *peer, hash common.Hash, td *big.Int) (err error) {
if d.mode == NoSync {
return nil
}
d.mux.Post(StartEvent{}) d.mux.Post(StartEvent{})
defer func() { defer func() {
// reset on error // reset on error
@ -459,6 +480,10 @@ func (d *Downloader) syncWithPeer(p *peer, hash common.Hash, td *big.Int) (err e
// cancel cancels all of the operations and resets the queue. It returns true // cancel cancels all of the operations and resets the queue. It returns true
// if the cancel operation was completed. // if the cancel operation was completed.
func (d *Downloader) cancel() { func (d *Downloader) cancel() {
if d.mode == NoSync {
return
}
// Close the current cancel channel // Close the current cancel channel
d.cancelLock.Lock() d.cancelLock.Lock()
if d.cancelCh != nil { if d.cancelCh != nil {
@ -484,6 +509,10 @@ func (d *Downloader) Terminate() {
// fetchHeight61 retrieves the head block of the remote peer to aid in estimating // fetchHeight61 retrieves the head block of the remote peer to aid in estimating
// the total time a pending synchronisation would take. // the total time a pending synchronisation would take.
func (d *Downloader) fetchHeight61(p *peer) (uint64, error) { func (d *Downloader) fetchHeight61(p *peer) (uint64, error) {
if d.mode == NoSync {
return 0, nil
}
glog.V(logger.Debug).Infof("%v: retrieving remote chain height", p) glog.V(logger.Debug).Infof("%v: retrieving remote chain height", p)
// Request the advertised remote head block and wait for the response // Request the advertised remote head block and wait for the response
@ -531,6 +560,10 @@ func (d *Downloader) fetchHeight61(p *peer) (uint64, error) {
// In the rare scenario when we ended up on a long reorganization (i.e. none of // In the rare scenario when we ended up on a long reorganization (i.e. none of
// the head blocks match), we do a binary search to find the common ancestor. // the head blocks match), we do a binary search to find the common ancestor.
func (d *Downloader) findAncestor61(p *peer) (uint64, error) { func (d *Downloader) findAncestor61(p *peer) (uint64, error) {
if d.mode == NoSync {
return 0, nil
}
glog.V(logger.Debug).Infof("%v: looking for common ancestor", p) glog.V(logger.Debug).Infof("%v: looking for common ancestor", p)
// Request out head blocks to short circuit ancestor location // Request out head blocks to short circuit ancestor location
@ -652,6 +685,10 @@ func (d *Downloader) findAncestor61(p *peer) (uint64, error) {
// fetchHashes61 keeps retrieving hashes from the requested number, until no more // fetchHashes61 keeps retrieving hashes from the requested number, until no more
// are returned, potentially throttling on the way. // are returned, potentially throttling on the way.
func (d *Downloader) fetchHashes61(p *peer, td *big.Int, from uint64) error { func (d *Downloader) fetchHashes61(p *peer, td *big.Int, from uint64) error {
if d.mode == NoSync {
return nil
}
glog.V(logger.Debug).Infof("%v: downloading hashes from #%d", p, from) glog.V(logger.Debug).Infof("%v: downloading hashes from #%d", p, from)
// Create a timeout timer, and the associated hash fetcher // Create a timeout timer, and the associated hash fetcher
@ -758,6 +795,10 @@ func (d *Downloader) fetchHashes61(p *peer, td *big.Int, from uint64) error {
// peers, reserving a chunk of blocks for each, waiting for delivery and also // peers, reserving a chunk of blocks for each, waiting for delivery and also
// periodically checking for timeouts. // periodically checking for timeouts.
func (d *Downloader) fetchBlocks61(from uint64) error { func (d *Downloader) fetchBlocks61(from uint64) error {
if d.mode == NoSync {
return nil
}
glog.V(logger.Debug).Infof("Downloading blocks from #%d", from) glog.V(logger.Debug).Infof("Downloading blocks from #%d", from)
defer glog.V(logger.Debug).Infof("Block download terminated") defer glog.V(logger.Debug).Infof("Block download terminated")
@ -916,6 +957,10 @@ func (d *Downloader) fetchBlocks61(from uint64) error {
// fetchHeight retrieves the head header of the remote peer to aid in estimating // fetchHeight retrieves the head header of the remote peer to aid in estimating
// the total time a pending synchronisation would take. // the total time a pending synchronisation would take.
func (d *Downloader) fetchHeight(p *peer) (uint64, error) { func (d *Downloader) fetchHeight(p *peer) (uint64, error) {
if d.mode == NoSync {
return 0, nil
}
glog.V(logger.Debug).Infof("%v: retrieving remote chain height", p) glog.V(logger.Debug).Infof("%v: retrieving remote chain height", p)
// Request the advertised remote head block and wait for the response // Request the advertised remote head block and wait for the response
@ -963,10 +1008,17 @@ func (d *Downloader) fetchHeight(p *peer) (uint64, error) {
// In the rare scenario when we ended up on a long reorganization (i.e. none of // In the rare scenario when we ended up on a long reorganization (i.e. none of
// the head links match), we do a binary search to find the common ancestor. // the head links match), we do a binary search to find the common ancestor.
func (d *Downloader) findAncestor(p *peer) (uint64, error) { func (d *Downloader) findAncestor(p *peer) (uint64, error) {
if d.mode == NoSync {
return 0, nil
}
glog.V(logger.Debug).Infof("%v: looking for common ancestor", p) glog.V(logger.Debug).Infof("%v: looking for common ancestor", p)
// Request our head headers to short circuit ancestor location // Request our head headers to short circuit ancestor location
head := d.headHeader().Number.Uint64() var head uint64
if header := d.headHeader(); header != nil {
head = header.Number.Uint64()
}
if d.mode == FullSync { if d.mode == FullSync {
head = d.headBlock().NumberU64() head = d.headBlock().NumberU64()
} else if d.mode == FastSync { } else if d.mode == FastSync {
@ -1092,6 +1144,10 @@ func (d *Downloader) findAncestor(p *peer) (uint64, error) {
// The queue parameter can be used to switch between queuing headers for block // The queue parameter can be used to switch between queuing headers for block
// body download too, or directly import as pure header chains. // body download too, or directly import as pure header chains.
func (d *Downloader) fetchHeaders(p *peer, td *big.Int, from uint64) error { func (d *Downloader) fetchHeaders(p *peer, td *big.Int, from uint64) error {
if d.mode == NoSync {
return nil
}
glog.V(logger.Debug).Infof("%v: downloading headers from #%d", p, from) glog.V(logger.Debug).Infof("%v: downloading headers from #%d", p, from)
defer glog.V(logger.Debug).Infof("%v: header download terminated", p) defer glog.V(logger.Debug).Infof("%v: header download terminated", p)
@ -1107,10 +1163,10 @@ func (d *Downloader) fetchHeaders(p *peer, td *big.Int, from uint64) error {
for i, header := range rollback { for i, header := range rollback {
hashes[i] = header.Hash() hashes[i] = header.Hash()
} }
lh, lfb, lb := d.headHeader().Number, d.headFastBlock().Number(), d.headBlock().Number() lh, lfb, lb := d.headHeader().Number, d.headFastBlock().SafeNumber(), d.headBlock().SafeNumber()
d.rollback(hashes) d.rollback(hashes)
glog.V(logger.Warn).Infof("Rolled back %d headers (LH: %d->%d, FB: %d->%d, LB: %d->%d)", glog.V(logger.Warn).Infof("Rolled back %d headers (LH: %d->%d, FB: %d->%d, LB: %d->%d)",
len(hashes), lh, d.headHeader().Number, lfb, d.headFastBlock().Number(), lb, d.headBlock().Number()) len(hashes), lh, d.headHeader().Number, lfb, d.headFastBlock().SafeNumber(), lb, d.headBlock().SafeNumber())
// If we're already past the pivot point, this could be an attack, disable fast sync // If we're already past the pivot point, this could be an attack, disable fast sync
if rollback[len(rollback)-1].Number.Uint64() > pivot { if rollback[len(rollback)-1].Number.Uint64() > pivot {
@ -1276,6 +1332,10 @@ func (d *Downloader) fetchHeaders(p *peer, td *big.Int, from uint64) error {
// available peers, reserving a chunk of blocks for each, waiting for delivery // available peers, reserving a chunk of blocks for each, waiting for delivery
// and also periodically checking for timeouts. // and also periodically checking for timeouts.
func (d *Downloader) fetchBodies(from uint64) error { func (d *Downloader) fetchBodies(from uint64) error {
if d.mode == NoSync {
return nil
}
glog.V(logger.Debug).Infof("Downloading block bodies from #%d", from) glog.V(logger.Debug).Infof("Downloading block bodies from #%d", from)
var ( var (
@ -1324,6 +1384,10 @@ func (d *Downloader) fetchReceipts(from uint64) error {
// available peers, reserving a chunk of nodes for each, waiting for delivery and // available peers, reserving a chunk of nodes for each, waiting for delivery and
// also periodically checking for timeouts. // also periodically checking for timeouts.
func (d *Downloader) fetchNodeData() error { func (d *Downloader) fetchNodeData() error {
if d.mode == NoSync {
return nil
}
glog.V(logger.Debug).Infof("Downloading node state data") glog.V(logger.Debug).Infof("Downloading node state data")
var ( var (
@ -1378,7 +1442,11 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv
fetchHook func([]*types.Header), fetch func(*peer, *fetchRequest) error, cancel func(*fetchRequest), capacity func(*peer) int, fetchHook func([]*types.Header), fetch func(*peer, *fetchRequest) error, cancel func(*fetchRequest), capacity func(*peer) int,
idle func() ([]*peer, int), setIdle func(*peer), kind string) error { idle func() ([]*peer, int), setIdle func(*peer), kind string) error {
// Create a ticker to detect expired retrieval tasks if d.mode == NoSync {
return nil
}
// Create a ticker to detect expired retreival tasks
ticker := time.NewTicker(100 * time.Millisecond) ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop() defer ticker.Stop()
@ -1678,6 +1746,9 @@ func (d *Downloader) DeliverNodeData(id string, data [][]byte) (err error) {
// deliver injects a new batch of data received from a remote node. // deliver injects a new batch of data received from a remote node.
func (d *Downloader) deliver(id string, destCh chan dataPack, packet dataPack, inMeter, dropMeter metrics.Meter) (err error) { func (d *Downloader) deliver(id string, destCh chan dataPack, packet dataPack, inMeter, dropMeter metrics.Meter) (err error) {
if d.mode == NoSync {
return nil
}
// Update the delivery metrics for both good and failed deliveries // Update the delivery metrics for both good and failed deliveries
inMeter.Mark(int64(packet.Items())) inMeter.Mark(int64(packet.Items()))
defer func() { defer func() {

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -150,7 +151,7 @@ func newTester() *downloadTester {
peerChainTds: make(map[string]map[common.Hash]*big.Int), peerChainTds: make(map[string]map[common.Hash]*big.Int),
} }
tester.stateDb, _ = ethdb.NewMemDatabase() tester.stateDb, _ = ethdb.NewMemDatabase()
tester.downloader = New(tester.stateDb, new(event.TypeMux), tester.hasHeader, tester.hasBlock, tester.getHeader, tester.downloader = New(FullSync, tester.stateDb, new(event.TypeMux), tester.hasHeader, tester.hasBlock, tester.getHeader,
tester.getBlock, tester.headHeader, tester.headBlock, tester.headFastBlock, tester.commitHeadBlock, tester.getTd, tester.getBlock, tester.headHeader, tester.headBlock, tester.headFastBlock, tester.commitHeadBlock, tester.getTd,
tester.insertHeaders, tester.insertBlocks, tester.insertReceipts, tester.rollback, tester.dropPeer) tester.insertHeaders, tester.insertBlocks, tester.insertReceipts, tester.rollback, tester.dropPeer)
@ -253,7 +254,7 @@ func (dl *downloadTester) headFastBlock() *types.Block {
func (dl *downloadTester) commitHeadBlock(hash common.Hash) error { func (dl *downloadTester) commitHeadBlock(hash common.Hash) error {
// For now only check that the state trie is correct // For now only check that the state trie is correct
if block := dl.getBlock(hash); block != nil { if block := dl.getBlock(hash); block != nil {
_, err := trie.NewSecure(block.Root(), dl.stateDb) _, err := trie.NewSecure(block.Root(), dl.stateDb, nil)
return err return err
} }
return fmt.Errorf("non existent block: %x", hash[:4]) return fmt.Errorf("non existent block: %x", hash[:4])
@ -682,7 +683,7 @@ func assertOwnForkedChain(t *testing.T, tester *downloadTester, common int, leng
index = len(tester.ownHashes) - lengths[len(lengths)-1] + int(tester.downloader.queue.fastSyncPivot) index = len(tester.ownHashes) - lengths[len(lengths)-1] + int(tester.downloader.queue.fastSyncPivot)
} }
if index > 0 { if index > 0 {
if statedb, err := state.New(tester.ownHeaders[tester.ownHashes[index]].Root, tester.stateDb); statedb == nil || err != nil { if statedb, err := state.New(tester.ownHeaders[tester.ownHashes[index]].Root, access.NewDbChainAccess(tester.stateDb), access.NullCtx); statedb == nil || err != nil {
t.Fatalf("state reconstruction failed: %v", err) t.Fatalf("state reconstruction failed: %v", err)
} }
} }

View file

@ -23,4 +23,5 @@ const (
FullSync SyncMode = iota // Synchronise the entire blockchain history from full blocks FullSync SyncMode = iota // Synchronise the entire blockchain history from full blocks
FastSync // Quickly download the headers, full sync only at the chain head FastSync // Quickly download the headers, full sync only at the chain head
LightSync // Download only the headers and terminate afterwards LightSync // Download only the headers and terminate afterwards
NoSync // Dummy downloader, only one of the eth/les downloaders is actually working
) )

View file

@ -21,6 +21,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -32,6 +33,7 @@ type AccountChange struct {
// Filtering interface // Filtering interface
type Filter struct { type Filter struct {
ca *access.ChainAccess
db ethdb.Database db ethdb.Database
begin, end int64 begin, end int64
addresses []common.Address addresses []common.Address
@ -44,8 +46,12 @@ type Filter struct {
// Create a new filter which uses a bloom filter on blocks to figure out whether a particular block // Create a new filter which uses a bloom filter on blocks to figure out whether a particular block
// is interesting or not. // is interesting or not.
func New(db ethdb.Database) *Filter { func New(ca *access.ChainAccess) *Filter {
return &Filter{db: db} return &Filter{ca: ca, db: ca.Db()}
}
func NewWithDb(db ethdb.Database) *Filter {
return &Filter{ca: access.NewDbChainAccess(db), db: db}
} }
// Set the earliest and latest block for filtering. // Set the earliest and latest block for filtering.
@ -69,7 +75,7 @@ func (self *Filter) SetTopics(topics [][]common.Hash) {
// Run filters logs with the current parameters set // Run filters logs with the current parameters set
func (self *Filter) Find() vm.Logs { func (self *Filter) Find() vm.Logs {
latestBlock := core.GetBlock(self.db, core.GetHeadBlockHash(self.db)) latestBlock := core.GetBlock(self.ca, core.GetHeadBlockHash(self.db), access.NoOdr)
var beginBlockNo uint64 = uint64(self.begin) var beginBlockNo uint64 = uint64(self.begin)
if self.begin == -1 { if self.begin == -1 {
beginBlockNo = latestBlock.NumberU64() beginBlockNo = latestBlock.NumberU64()
@ -124,7 +130,7 @@ func (self *Filter) getLogs(start, end uint64) (logs vm.Logs) {
for i := start; i <= end; i++ { for i := start; i <= end; i++ {
hash := core.GetCanonicalHash(self.db, i) hash := core.GetCanonicalHash(self.db, i)
if hash != (common.Hash{}) { if hash != (common.Hash{}) {
block = core.GetBlock(self.db, hash) block = core.GetBlock(self.ca, hash, access.NoOdr)
} else { // block not found } else { // block not found
return logs return logs
} }
@ -134,7 +140,7 @@ func (self *Filter) getLogs(start, end uint64) (logs vm.Logs) {
if self.bloomFilter(block) { if self.bloomFilter(block) {
// Get the logs of the block // Get the logs of the block
var ( var (
receipts = core.GetBlockReceipts(self.db, block.Hash()) receipts = core.GetBlockReceipts(self.ca, block.Hash(), access.NoOdr)
unfiltered vm.Logs unfiltered vm.Logs
) )
for _, receipt := range receipts { for _, receipt := range receipts {
@ -142,6 +148,7 @@ func (self *Filter) getLogs(start, end uint64) (logs vm.Logs) {
} }
logs = append(logs, self.FilterLogs(unfiltered)...) logs = append(logs, self.FilterLogs(unfiltered)...)
} }
block = core.GetBlock(self.ca, block.ParentHash(), access.NoOdr)
} }
return logs return logs

View file

@ -84,7 +84,7 @@ func BenchmarkMipmaps(b *testing.B) {
} }
b.ResetTimer() b.ResetTimer()
filter := New(db) filter := NewWithDb(db)
filter.SetAddresses([]common.Address{addr1, addr2, addr3, addr4}) filter.SetAddresses([]common.Address{addr1, addr2, addr3, addr4})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
filter.SetEndBlock(-1) filter.SetEndBlock(-1)
@ -185,7 +185,7 @@ func TestFilters(t *testing.T) {
} }
} }
filter := New(db) filter := NewWithDb(db)
filter.SetAddresses([]common.Address{addr}) filter.SetAddresses([]common.Address{addr})
filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2, hash3, hash4}}) filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2, hash3, hash4}})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
@ -196,7 +196,7 @@ func TestFilters(t *testing.T) {
t.Error("expected 4 log, got", len(logs)) t.Error("expected 4 log, got", len(logs))
} }
filter = New(db) filter = NewWithDb(db)
filter.SetAddresses([]common.Address{addr}) filter.SetAddresses([]common.Address{addr})
filter.SetTopics([][]common.Hash{[]common.Hash{hash3}}) filter.SetTopics([][]common.Hash{[]common.Hash{hash3}})
filter.SetBeginBlock(900) filter.SetBeginBlock(900)
@ -209,7 +209,7 @@ func TestFilters(t *testing.T) {
t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0])
} }
filter = New(db) filter = NewWithDb(db)
filter.SetAddresses([]common.Address{addr}) filter.SetAddresses([]common.Address{addr})
filter.SetTopics([][]common.Hash{[]common.Hash{hash3}}) filter.SetTopics([][]common.Hash{[]common.Hash{hash3}})
filter.SetBeginBlock(990) filter.SetBeginBlock(990)
@ -222,7 +222,7 @@ func TestFilters(t *testing.T) {
t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0])
} }
filter = New(db) filter = NewWithDb(db)
filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2}}) filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2}})
filter.SetBeginBlock(1) filter.SetBeginBlock(1)
filter.SetEndBlock(10) filter.SetEndBlock(10)
@ -233,7 +233,7 @@ func TestFilters(t *testing.T) {
} }
failHash := common.BytesToHash([]byte("fail")) failHash := common.BytesToHash([]byte("fail"))
filter = New(db) filter = NewWithDb(db)
filter.SetTopics([][]common.Hash{[]common.Hash{failHash}}) filter.SetTopics([][]common.Hash{[]common.Hash{failHash}})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
filter.SetEndBlock(-1) filter.SetEndBlock(-1)
@ -244,7 +244,7 @@ func TestFilters(t *testing.T) {
} }
failAddr := common.BytesToAddress([]byte("failmenow")) failAddr := common.BytesToAddress([]byte("failmenow"))
filter = New(db) filter = NewWithDb(db)
filter.SetAddresses([]common.Address{failAddr}) filter.SetAddresses([]common.Address{failAddr})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
filter.SetEndBlock(-1) filter.SetEndBlock(-1)
@ -254,7 +254,7 @@ func TestFilters(t *testing.T) {
t.Error("expected 0 log, got", len(logs)) t.Error("expected 0 log, got", len(logs))
} }
filter = New(db) filter = NewWithDb(db)
filter.SetTopics([][]common.Hash{[]common.Hash{failHash}, []common.Hash{hash1}}) filter.SetTopics([][]common.Hash{[]common.Hash{failHash}, []common.Hash{hash1}})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
filter.SetEndBlock(-1) filter.SetEndBlock(-1)

View file

@ -22,6 +22,7 @@ import (
"sync" "sync"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
@ -74,6 +75,9 @@ func NewGasPriceOracle(eth *Ethereum) *GasPriceOracle {
} }
func (gpo *GasPriceOracle) init() { func (gpo *GasPriceOracle) init() {
if gpo.eth.Mode == LightMode {
return
}
gpo.initOnce.Do(func() { gpo.initOnce.Do(func() {
gpo.processPastBlocks(gpo.eth.BlockChain()) gpo.processPastBlocks(gpo.eth.BlockChain())
go gpo.listenLoop() go gpo.listenLoop()
@ -92,7 +96,7 @@ func (self *GasPriceOracle) processPastBlocks(chain *core.BlockChain) {
} }
self.firstProcessed = uint64(first) self.firstProcessed = uint64(first)
for i := first; i <= last; i++ { for i := first; i <= last; i++ {
block := chain.GetBlockByNumber(uint64(i)) block := chain.GetBlockByNumber(uint64(i), access.NoOdr)
if block != nil { if block != nil {
self.processBlock(block) self.processBlock(block)
} }

View file

@ -26,10 +26,10 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/fetcher" "github.com/ethereum/go-ethereum/eth/fetcher"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
@ -59,9 +59,10 @@ type ProtocolManager struct {
networkId int networkId int
fastSync bool fastSync bool
mode Mode
txpool txPool txpool txPool
blockchain *core.BlockChain blockchain *core.BlockChain
chaindb ethdb.Database chainAccess *access.ChainAccess
downloader *downloader.Downloader downloader *downloader.Downloader
fetcher *fetcher.Fetcher fetcher *fetcher.Fetcher
@ -86,20 +87,21 @@ type ProtocolManager struct {
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable // NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
// with the ethereum network. // with the ethereum network.
func NewProtocolManager(fastSync bool, networkId int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { func NewProtocolManager(mode Mode, networkId int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, ca *access.ChainAccess) (*ProtocolManager, error) {
// Figure out whether to allow fast sync or not // Figure out whether to allow fast sync or not
if fastSync && blockchain.CurrentBlock().NumberU64() > 0 { if mode == FullMode && blockchain.CurrentBlock().NumberU64() > 0 {
glog.V(logger.Info).Infof("blockchain not empty, fast sync disabled") glog.V(logger.Info).Infof("blockchain not empty, fast sync disabled")
fastSync = false mode = ArchiveMode
} }
// Create the protocol manager with the base fields // Create the protocol manager with the base fields
manager := &ProtocolManager{ manager := &ProtocolManager{
networkId: networkId, networkId: networkId,
fastSync: fastSync, fastSync: mode == FullMode,
mode: mode,
eventMux: mux, eventMux: mux,
txpool: txpool, txpool: txpool,
blockchain: blockchain, blockchain: blockchain,
chaindb: chaindb, chainAccess: ca,
peers: newPeerSet(), peers: newPeerSet(),
newPeerCh: make(chan *peer, 1), newPeerCh: make(chan *peer, 1),
txsyncCh: make(chan *txsync), txsyncCh: make(chan *txsync),
@ -109,7 +111,7 @@ func NewProtocolManager(fastSync bool, networkId int, mux *event.TypeMux, txpool
manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions)) manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions))
for i, version := range ProtocolVersions { for i, version := range ProtocolVersions {
// Skip protocol version if incompatible with the mode of operation // Skip protocol version if incompatible with the mode of operation
if fastSync && version < eth63 { if manager.fastSync && version < eth63 {
continue continue
} }
// Compatible; initialise the sub-protocol // Compatible; initialise the sub-protocol
@ -138,17 +140,27 @@ func NewProtocolManager(fastSync bool, networkId int, mux *event.TypeMux, txpool
return nil, errIncompatibleConfig return nil, errIncompatibleConfig
} }
// Construct the different synchronisation mechanisms // Construct the different synchronisation mechanisms
manager.downloader = downloader.New(chaindb, manager.eventMux, blockchain.HasHeader, blockchain.HasBlock, blockchain.GetHeader, blockchain.GetBlock, var syncMode downloader.SyncMode
blockchain.CurrentHeader, blockchain.CurrentBlock, blockchain.CurrentFastBlock, blockchain.FastSyncCommitHead, blockchain.GetTd, switch mode {
case ArchiveMode:
syncMode = downloader.FullSync
case FullMode:
syncMode = downloader.FastSync
case LightMode:
syncMode = downloader.NoSync
}
manager.downloader = downloader.New(syncMode, ca.Db(), manager.eventMux, blockchain.HasHeader, blockchain.HasBlock, blockchain.GetHeader,
blockchain.GetBlockNoOdr, blockchain.CurrentHeader, blockchain.CurrentBlock, blockchain.CurrentFastBlock, blockchain.FastSyncCommitHead, blockchain.GetTd,
blockchain.InsertHeaderChain, blockchain.InsertChain, blockchain.InsertReceiptChain, blockchain.Rollback, manager.removePeer) blockchain.InsertHeaderChain, blockchain.InsertChain, blockchain.InsertReceiptChain, blockchain.Rollback, manager.removePeer)
validator := func(block *types.Block, parent *types.Block) error { validator := func(block *types.Block, parent *types.Block) error {
return core.ValidateHeader(pow, block.Header(), parent.Header(), true, false) return core.ValidateHeader(pow, block.Header(), parent.Header(), true, false)
} }
heighter := func() uint64 { heighter := func() uint64 {
return blockchain.CurrentBlock().NumberU64() return blockchain.LastBlockNumberU64()
} }
manager.fetcher = fetcher.New(blockchain.GetBlock, validator, manager.BroadcastBlock, heighter, blockchain.InsertChain, manager.removePeer) manager.fetcher = fetcher.New(blockchain.GetBlockNoOdr, validator, manager.BroadcastBlock, heighter, blockchain.InsertChain, manager.removePeer)
return manager, nil return manager, nil
} }
@ -233,6 +245,7 @@ func (pm *ProtocolManager) handle(p *peer) error {
p.RequestHeadersByNumber, p.RequestBodies, p.RequestReceipts, p.RequestNodeData); err != nil { p.RequestHeadersByNumber, p.RequestBodies, p.RequestReceipts, p.RequestNodeData); err != nil {
return err return err
} }
// Propagate existing transactions. new transactions appearing // Propagate existing transactions. new transactions appearing
// after this will be sent via broadcasts. // after this will be sent via broadcasts.
pm.syncTransactions(p) pm.syncTransactions(p)
@ -292,7 +305,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
request.Amount = uint64(downloader.MaxHashFetch) request.Amount = uint64(downloader.MaxHashFetch)
} }
// Calculate the last block that should be retrieved, and short circuit if unavailable // Calculate the last block that should be retrieved, and short circuit if unavailable
last := pm.blockchain.GetBlockByNumber(request.Number + request.Amount - 1) last := pm.blockchain.GetBlockByNumber(request.Number+request.Amount-1, access.NoOdr)
if last == nil { if last == nil {
last = pm.blockchain.CurrentBlock() last = pm.blockchain.CurrentBlock()
request.Amount = last.NumberU64() - request.Number + 1 request.Amount = last.NumberU64() - request.Number + 1
@ -342,7 +355,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrDecode, "msg %v: %v", msg, err) return errResp(ErrDecode, "msg %v: %v", msg, err)
} }
// Retrieve the requested block, stopping if enough was found // Retrieve the requested block, stopping if enough was found
if block := pm.blockchain.GetBlock(hash); block != nil { if block := pm.blockchain.GetBlock(hash, access.NoOdr); block != nil {
blocks = append(blocks, block) blocks = append(blocks, block)
bytes += block.Size() bytes += block.Size()
} }
@ -468,7 +481,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrDecode, "msg %v: %v", msg, err) return errResp(ErrDecode, "msg %v: %v", msg, err)
} }
// Retrieve the requested block body, stopping if enough was found // Retrieve the requested block body, stopping if enough was found
if data := pm.blockchain.GetBodyRLP(hash); len(data) != 0 { if data := pm.blockchain.GetBodyRLP(hash, access.NoOdr); len(data) != 0 {
bodies = append(bodies, data) bodies = append(bodies, data)
bytes += len(data) bytes += len(data)
} }
@ -517,7 +530,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrDecode, "msg %v: %v", msg, err) return errResp(ErrDecode, "msg %v: %v", msg, err)
} }
// Retrieve the requested state entry, stopping if enough was found // Retrieve the requested state entry, stopping if enough was found
if entry, err := pm.chaindb.Get(hash.Bytes()); err == nil { if entry, err := pm.chainAccess.Db().Get(hash.Bytes()); err == nil {
data = append(data, entry) data = append(data, entry)
bytes += len(entry) bytes += len(entry)
} }
@ -555,7 +568,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrDecode, "msg %v: %v", msg, err) return errResp(ErrDecode, "msg %v: %v", msg, err)
} }
// Retrieve the requested block's receipts, skipping if unknown to us // Retrieve the requested block's receipts, skipping if unknown to us
results := core.GetBlockReceipts(pm.chaindb, hash) results := core.GetBlockReceipts(pm.chainAccess, hash, access.NoOdr)
if results == nil { if results == nil {
if header := pm.blockchain.GetHeader(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { if header := pm.blockchain.GetHeader(hash); header == nil || header.ReceiptHash != types.EmptyRootHash {
continue continue
@ -649,7 +662,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
// Update the peers total difficulty if needed, schedule a download if gapped // Update the peers total difficulty if needed, schedule a download if gapped
if request.TD.Cmp(p.Td()) > 0 { if request.TD.Cmp(p.Td()) > 0 {
p.SetTd(request.TD) p.SetTd(request.TD)
td := pm.blockchain.GetTd(pm.blockchain.CurrentBlock().Hash()) td := pm.blockchain.GetTd(pm.blockchain.LastBlockHash())
if request.TD.Cmp(new(big.Int).Add(td, request.Block.Difficulty())) > 0 { if request.TD.Cmp(new(big.Int).Add(td, request.Block.Difficulty())) > 0 {
go pm.synchronise(p) go pm.synchronise(p)
} }
@ -686,7 +699,7 @@ func (pm *ProtocolManager) BroadcastBlock(block *types.Block, propagate bool) {
if propagate { if propagate {
// Calculate the TD of the block (it's not imported yet, so block.Td is not valid) // Calculate the TD of the block (it's not imported yet, so block.Td is not valid)
var td *big.Int var td *big.Int
if parent := pm.blockchain.GetBlock(block.ParentHash()); parent != nil { if parent := pm.blockchain.GetBlock(block.ParentHash(), access.NoOdr); parent != nil {
td = new(big.Int).Add(block.Difficulty(), pm.blockchain.GetTd(block.ParentHash())) td = new(big.Int).Add(block.Difficulty(), pm.blockchain.GetTd(block.ParentHash()))
} else { } else {
glog.V(logger.Error).Infof("propagating dangling block #%d [%x]", block.NumberU64(), hash[:4]) glog.V(logger.Error).Infof("propagating dangling block #%d [%x]", block.NumberU64(), hash[:4])

View file

@ -8,6 +8,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -36,7 +37,11 @@ func TestProtocolCompatibility(t *testing.T) {
for i, tt := range tests { for i, tt := range tests {
ProtocolVersions = []uint{tt.version} ProtocolVersions = []uint{tt.version}
pm, err := newTestProtocolManager(tt.fastSync, 0, nil, nil) mode := ArchiveMode
if tt.fastSync {
mode = FullMode
}
pm, err := newTestProtocolManager(mode, 0, nil, nil)
if pm != nil { if pm != nil {
defer pm.Stop() defer pm.Stop()
} }
@ -64,9 +69,9 @@ func testGetBlockHashes(t *testing.T, protocol int) {
}{ }{
{common.Hash{}, 1, 0}, // Make sure non existent hashes don't return results {common.Hash{}, 1, 0}, // Make sure non existent hashes don't return results
{pm.blockchain.Genesis().Hash(), 1, 0}, // There are no hashes to retrieve up from the genesis {pm.blockchain.Genesis().Hash(), 1, 0}, // There are no hashes to retrieve up from the genesis
{pm.blockchain.GetBlockByNumber(5).Hash(), 5, 5}, // All the hashes including the genesis requested {pm.blockchain.GetBlockByNumber(5, access.NoOdr).Hash(), 5, 5}, // All the hashes including the genesis requested
{pm.blockchain.GetBlockByNumber(5).Hash(), 10, 5}, // More hashes than available till the genesis requested {pm.blockchain.GetBlockByNumber(5, access.NoOdr).Hash(), 10, 5}, // More hashes than available till the genesis requested
{pm.blockchain.GetBlockByNumber(100).Hash(), 10, 10}, // All hashes available from the middle of the chain {pm.blockchain.GetBlockByNumber(100, access.NoOdr).Hash(), 10, 10}, // All hashes available from the middle of the chain
{pm.blockchain.CurrentBlock().Hash(), 10, 10}, // All hashes available from the head of the chain {pm.blockchain.CurrentBlock().Hash(), 10, 10}, // All hashes available from the head of the chain
{pm.blockchain.CurrentBlock().Hash(), limit, limit}, // Request the maximum allowed hash count {pm.blockchain.CurrentBlock().Hash(), limit, limit}, // Request the maximum allowed hash count
{pm.blockchain.CurrentBlock().Hash(), limit + 1, limit}, // Request more than the maximum allowed hash count {pm.blockchain.CurrentBlock().Hash(), limit + 1, limit}, // Request more than the maximum allowed hash count
@ -76,9 +81,9 @@ func testGetBlockHashes(t *testing.T, protocol int) {
// Assemble the hash response we would like to receive // Assemble the hash response we would like to receive
resp := make([]common.Hash, tt.result) resp := make([]common.Hash, tt.result)
if len(resp) > 0 { if len(resp) > 0 {
from := pm.blockchain.GetBlock(tt.origin).NumberU64() - 1 from := pm.blockchain.GetBlock(tt.origin, access.NoOdr).NumberU64() - 1
for j := 0; j < len(resp); j++ { for j := 0; j < len(resp); j++ {
resp[j] = pm.blockchain.GetBlockByNumber(uint64(int(from) - j)).Hash() resp[j] = pm.blockchain.GetBlockByNumber(uint64(int(from)-j), access.NoOdr).Hash()
} }
} }
// Send the hash request and verify the response // Send the hash request and verify the response
@ -120,7 +125,7 @@ func testGetBlockHashesFromNumber(t *testing.T, protocol int) {
// Assemble the hash response we would like to receive // Assemble the hash response we would like to receive
resp := make([]common.Hash, tt.result) resp := make([]common.Hash, tt.result)
for j := 0; j < len(resp); j++ { for j := 0; j < len(resp); j++ {
resp[j] = pm.blockchain.GetBlockByNumber(tt.origin + uint64(j)).Hash() resp[j] = pm.blockchain.GetBlockByNumber(tt.origin+uint64(j), access.NoOdr).Hash()
} }
// Send the hash request and verify the response // Send the hash request and verify the response
p2p.Send(peer.app, 0x08, getBlockHashesFromNumberData{tt.origin, uint64(tt.number)}) p2p.Send(peer.app, 0x08, getBlockHashesFromNumberData{tt.origin, uint64(tt.number)})
@ -157,11 +162,11 @@ func testGetBlocks(t *testing.T, protocol int) {
// Existing and non-existing blocks interleaved should not cause problems // Existing and non-existing blocks interleaved should not cause problems
{0, []common.Hash{ {0, []common.Hash{
common.Hash{}, common.Hash{},
pm.blockchain.GetBlockByNumber(1).Hash(), pm.blockchain.GetBlockByNumber(1, access.NoOdr).Hash(),
common.Hash{}, common.Hash{},
pm.blockchain.GetBlockByNumber(10).Hash(), pm.blockchain.GetBlockByNumber(10, access.NoOdr).Hash(),
common.Hash{}, common.Hash{},
pm.blockchain.GetBlockByNumber(100).Hash(), pm.blockchain.GetBlockByNumber(100, access.NoOdr).Hash(),
common.Hash{}, common.Hash{},
}, []bool{false, true, false, true, false, true, false}, 3}, }, []bool{false, true, false, true, false, true, false}, 3},
} }
@ -177,7 +182,7 @@ func testGetBlocks(t *testing.T, protocol int) {
if !seen[num] { if !seen[num] {
seen[num] = true seen[num] = true
block := pm.blockchain.GetBlockByNumber(uint64(num)) block := pm.blockchain.GetBlockByNumber(uint64(num), access.NoOdr)
hashes = append(hashes, block.Hash()) hashes = append(hashes, block.Hash())
if len(blocks) < tt.expected { if len(blocks) < tt.expected {
blocks = append(blocks, block) blocks = append(blocks, block)
@ -189,7 +194,7 @@ func testGetBlocks(t *testing.T, protocol int) {
for j, hash := range tt.explicit { for j, hash := range tt.explicit {
hashes = append(hashes, hash) hashes = append(hashes, hash)
if tt.available[j] && len(blocks) < tt.expected { if tt.available[j] && len(blocks) < tt.expected {
blocks = append(blocks, pm.blockchain.GetBlock(hash)) blocks = append(blocks, pm.blockchain.GetBlock(hash, access.NoOdr))
} }
} }
// Send the hash request and verify the response // Send the hash request and verify the response
@ -222,48 +227,48 @@ func testGetBlockHeaders(t *testing.T, protocol int) {
}{ }{
// A single random block should be retrievable by hash and number too // A single random block should be retrievable by hash and number too
{ {
&getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, Amount: 1}, &getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash()}, Amount: 1},
[]common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, []common.Hash{pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash()},
}, { }, {
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1}, &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1},
[]common.Hash{pm.blockchain.GetBlockByNumber(limit / 2).Hash()}, []common.Hash{pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash()},
}, },
// Multiple headers should be retrievable in both directions // Multiple headers should be retrievable in both directions
{ {
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3}, &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3},
[]common.Hash{ []common.Hash{
pm.blockchain.GetBlockByNumber(limit / 2).Hash(), pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(limit/2 + 1).Hash(), pm.blockchain.GetBlockByNumber(limit/2+1, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(limit/2 + 2).Hash(), pm.blockchain.GetBlockByNumber(limit/2+2, access.NoOdr).Hash(),
}, },
}, { }, {
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true}, &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true},
[]common.Hash{ []common.Hash{
pm.blockchain.GetBlockByNumber(limit / 2).Hash(), pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(limit/2 - 1).Hash(), pm.blockchain.GetBlockByNumber(limit/2-1, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(limit/2 - 2).Hash(), pm.blockchain.GetBlockByNumber(limit/2-2, access.NoOdr).Hash(),
}, },
}, },
// Multiple headers with skip lists should be retrievable // Multiple headers with skip lists should be retrievable
{ {
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3}, &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3},
[]common.Hash{ []common.Hash{
pm.blockchain.GetBlockByNumber(limit / 2).Hash(), pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(limit/2 + 4).Hash(), pm.blockchain.GetBlockByNumber(limit/2+4, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(limit/2 + 8).Hash(), pm.blockchain.GetBlockByNumber(limit/2+8, access.NoOdr).Hash(),
}, },
}, { }, {
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true}, &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true},
[]common.Hash{ []common.Hash{
pm.blockchain.GetBlockByNumber(limit / 2).Hash(), pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(limit/2 - 4).Hash(), pm.blockchain.GetBlockByNumber(limit/2-4, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(limit/2 - 8).Hash(), pm.blockchain.GetBlockByNumber(limit/2-8, access.NoOdr).Hash(),
}, },
}, },
// The chain endpoints should be retrievable // The chain endpoints should be retrievable
{ {
&getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1}, &getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1},
[]common.Hash{pm.blockchain.GetBlockByNumber(0).Hash()}, []common.Hash{pm.blockchain.GetBlockByNumber(0, access.NoOdr).Hash()},
}, { }, {
&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64()}, Amount: 1}, &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64()}, Amount: 1},
[]common.Hash{pm.blockchain.CurrentBlock().Hash()}, []common.Hash{pm.blockchain.CurrentBlock().Hash()},
@ -277,28 +282,28 @@ func testGetBlockHeaders(t *testing.T, protocol int) {
{ {
&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3}, &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3},
[]common.Hash{ []common.Hash{
pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(), pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()-4, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()).Hash(), pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64(), access.NoOdr).Hash(),
}, },
}, { }, {
&getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true}, &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true},
[]common.Hash{ []common.Hash{
pm.blockchain.GetBlockByNumber(4).Hash(), pm.blockchain.GetBlockByNumber(4, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(0).Hash(), pm.blockchain.GetBlockByNumber(0, access.NoOdr).Hash(),
}, },
}, },
// Check that requesting more than available is handled gracefully, even if mid skip // Check that requesting more than available is handled gracefully, even if mid skip
{ {
&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3}, &getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3},
[]common.Hash{ []common.Hash{
pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 4).Hash(), pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()-4, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64() - 1).Hash(), pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()-1, access.NoOdr).Hash(),
}, },
}, { }, {
&getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true}, &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true},
[]common.Hash{ []common.Hash{
pm.blockchain.GetBlockByNumber(4).Hash(), pm.blockchain.GetBlockByNumber(4, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(1).Hash(), pm.blockchain.GetBlockByNumber(1, access.NoOdr).Hash(),
}, },
}, },
// Check that non existing headers aren't returned // Check that non existing headers aren't returned
@ -315,7 +320,7 @@ func testGetBlockHeaders(t *testing.T, protocol int) {
// Collect the headers to expect in the response // Collect the headers to expect in the response
headers := []*types.Header{} headers := []*types.Header{}
for _, hash := range tt.expect { for _, hash := range tt.expect {
headers = append(headers, pm.blockchain.GetBlock(hash).Header()) headers = append(headers, pm.blockchain.GetBlock(hash, access.NoOdr).Header())
} }
// Send the hash request and verify the response // Send the hash request and verify the response
p2p.Send(peer.app, 0x03, tt.query) p2p.Send(peer.app, 0x03, tt.query)
@ -353,11 +358,11 @@ func testGetBlockBodies(t *testing.T, protocol int) {
// Existing and non-existing blocks interleaved should not cause problems // Existing and non-existing blocks interleaved should not cause problems
{0, []common.Hash{ {0, []common.Hash{
common.Hash{}, common.Hash{},
pm.blockchain.GetBlockByNumber(1).Hash(), pm.blockchain.GetBlockByNumber(1, access.NoOdr).Hash(),
common.Hash{}, common.Hash{},
pm.blockchain.GetBlockByNumber(10).Hash(), pm.blockchain.GetBlockByNumber(10, access.NoOdr).Hash(),
common.Hash{}, common.Hash{},
pm.blockchain.GetBlockByNumber(100).Hash(), pm.blockchain.GetBlockByNumber(100, access.NoOdr).Hash(),
common.Hash{}, common.Hash{},
}, []bool{false, true, false, true, false, true, false}, 3}, }, []bool{false, true, false, true, false, true, false}, 3},
} }
@ -365,7 +370,7 @@ func testGetBlockBodies(t *testing.T, protocol int) {
for i, tt := range tests { for i, tt := range tests {
// Collect the hashes to request, and the response to expect // Collect the hashes to request, and the response to expect
hashes, seen := []common.Hash{}, make(map[int64]bool) hashes, seen := []common.Hash{}, make(map[int64]bool)
bodies := []*blockBody{} bodies := []*types.Body{}
for j := 0; j < tt.random; j++ { for j := 0; j < tt.random; j++ {
for { for {
@ -373,10 +378,10 @@ func testGetBlockBodies(t *testing.T, protocol int) {
if !seen[num] { if !seen[num] {
seen[num] = true seen[num] = true
block := pm.blockchain.GetBlockByNumber(uint64(num)) block := pm.blockchain.GetBlockByNumber(uint64(num), access.NoOdr)
hashes = append(hashes, block.Hash()) hashes = append(hashes, block.Hash())
if len(bodies) < tt.expected { if len(bodies) < tt.expected {
bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()})
} }
break break
} }
@ -385,8 +390,8 @@ func testGetBlockBodies(t *testing.T, protocol int) {
for j, hash := range tt.explicit { for j, hash := range tt.explicit {
hashes = append(hashes, hash) hashes = append(hashes, hash)
if tt.available[j] && len(bodies) < tt.expected { if tt.available[j] && len(bodies) < tt.expected {
block := pm.blockchain.GetBlock(hash) block := pm.blockchain.GetBlock(hash, access.NoOdr)
bodies = append(bodies, &blockBody{Transactions: block.Transactions(), Uncles: block.Uncles()}) bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()})
} }
} }
// Send the hash request and verify the response // Send the hash request and verify the response
@ -442,7 +447,7 @@ func testGetNodeData(t *testing.T, protocol int) {
// Fetch for now the entire chain db // Fetch for now the entire chain db
hashes := []common.Hash{} hashes := []common.Hash{}
for _, key := range pm.chaindb.(*ethdb.MemDatabase).Keys() { for _, key := range pm.chainAccess.Db().(*ethdb.MemDatabase).Keys() {
if len(key) == len(common.Hash{}) { if len(key) == len(common.Hash{}) {
hashes = append(hashes, common.BytesToHash(key)) hashes = append(hashes, common.BytesToHash(key))
} }
@ -471,10 +476,10 @@ func testGetNodeData(t *testing.T, protocol int) {
} }
accounts := []common.Address{testBankAddress, acc1Addr, acc2Addr} accounts := []common.Address{testBankAddress, acc1Addr, acc2Addr}
for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ { for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
trie, _ := state.New(pm.blockchain.GetBlockByNumber(i).Root(), statedb) trie, _ := state.New(pm.blockchain.GetBlockByNumber(i, access.NoOdr).Root(), access.NewDbChainAccess(statedb), access.NullCtx)
for j, acc := range accounts { for j, acc := range accounts {
state, _ := pm.blockchain.State() state, _ := pm.blockchain.State(access.NullCtx)
bw := state.GetBalance(acc) bw := state.GetBalance(acc)
bh := trie.GetBalance(acc) bh := trie.GetBalance(acc)
@ -534,10 +539,10 @@ func testGetReceipt(t *testing.T, protocol int) {
// Collect the hashes to request, and the response to expect // Collect the hashes to request, and the response to expect
hashes, receipts := []common.Hash{}, []types.Receipts{} hashes, receipts := []common.Hash{}, []types.Receipts{}
for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ { for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
block := pm.blockchain.GetBlockByNumber(i) block := pm.blockchain.GetBlockByNumber(i, access.NoOdr)
hashes = append(hashes, block.Hash()) hashes = append(hashes, block.Hash())
receipts = append(receipts, core.GetBlockReceipts(pm.chaindb, block.Hash())) receipts = append(receipts, core.GetBlockReceipts(pm.chainAccess, block.Hash(), access.NoOdr))
} }
// Send the hash request and verify the response // Send the hash request and verify the response
p2p.Send(peer.app, 0x0f, hashes) p2p.Send(peer.app, 0x0f, hashes)

View file

@ -11,6 +11,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -28,21 +29,22 @@ var (
// newTestProtocolManager creates a new protocol manager for testing purposes, // newTestProtocolManager creates a new protocol manager for testing purposes,
// with the given number of blocks already known, and potential notification // with the given number of blocks already known, and potential notification
// channels for different events. // channels for different events.
func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, error) { func newTestProtocolManager(mode Mode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, error) {
var ( var (
evmux = new(event.TypeMux) evmux = new(event.TypeMux)
pow = new(core.FakePow) pow = new(core.FakePow)
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
ca = access.NewDbChainAccess(db)
genesis = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{testBankAddress, testBankFunds}) genesis = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{testBankAddress, testBankFunds})
blockchain, _ = core.NewBlockChain(db, pow, evmux) blockchain, _ = core.NewBlockChain(ca, pow, evmux)
blockproc = core.NewBlockProcessor(db, pow, blockchain, evmux) blockproc = core.NewBlockProcessor(ca, pow, blockchain, evmux)
) )
blockchain.SetProcessor(blockproc) blockchain.SetProcessor(blockproc)
chain, _ := core.GenerateChain(genesis, db, blocks, generator) chain, _ := core.GenerateChain(genesis, db, blocks, generator)
if _, err := blockchain.InsertChain(chain); err != nil { if _, err := blockchain.InsertChain(chain); err != nil {
panic(err) panic(err)
} }
pm, err := NewProtocolManager(fastSync, NetworkId, evmux, &testTxPool{added: newtx}, pow, blockchain, db) pm, err := NewProtocolManager(mode, NetworkId, evmux, &testTxPool{added: newtx}, pow, blockchain, ca)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -55,7 +57,11 @@ func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core
// channels for different events. In case of an error, the constructor force- // channels for different events. In case of an error, the constructor force-
// fails the test. // fails the test.
func newTestProtocolManagerMust(t *testing.T, fastSync bool, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) *ProtocolManager { func newTestProtocolManagerMust(t *testing.T, fastSync bool, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) *ProtocolManager {
pm, err := newTestProtocolManager(fastSync, blocks, generator, newtx) mode := ArchiveMode
if fastSync {
mode = FullMode
}
pm, err := newTestProtocolManager(mode, blocks, generator, newtx)
if err != nil { if err != nil {
t.Fatalf("Failed to create protocol manager: %v", err) t.Fatalf("Failed to create protocol manager: %v", err)
} }

View file

@ -196,7 +196,7 @@ func (p *peer) SendBlockHeaders(headers []*types.Header) error {
} }
// SendBlockBodies sends a batch of block contents to the remote peer. // SendBlockBodies sends a batch of block contents to the remote peer.
func (p *peer) SendBlockBodies(bodies []*blockBody) error { func (p *peer) SendBlockBodies(bodies []*types.Body) error {
return p2p.Send(p.rw, BlockBodiesMsg, blockBodiesData(bodies)) return p2p.Send(p.rw, BlockBodiesMsg, blockBodiesData(bodies))
} }

View file

@ -26,6 +26,15 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
// Mode represents the mode of operation of the eth client.
type Mode int
const (
ArchiveMode Mode = iota // Maintain the entire blockchain history
FullMode // Maintain only a recent view of the blockchain
LightMode // Don't maintain any history, rather fetch on demand
)
// Constants to match up protocol versions and messages // Constants to match up protocol versions and messages
const ( const (
eth61 = 61 eth61 = 61
@ -201,14 +210,8 @@ type newBlockData struct {
TD *big.Int TD *big.Int
} }
// blockBody represents the data content of a single block.
type blockBody struct {
Transactions []*types.Transaction // Transactions contained within a block
Uncles []*types.Header // Uncles contained within a block
}
// blockBodiesData is the network packet for block content distribution. // blockBodiesData is the network packet for block content distribution.
type blockBodiesData []*blockBody type blockBodiesData []*types.Body
// nodeDataData is the network response packet for a node data retrieval. // nodeDataData is the network response packet for a node data retrieval.
type nodeDataData []struct { type nodeDataData []struct {

View file

@ -161,7 +161,7 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
return return
} }
// Make sure the peer's TD is higher than our own. If not drop. // Make sure the peer's TD is higher than our own. If not drop.
td := pm.blockchain.GetTd(pm.blockchain.CurrentBlock().Hash()) td := pm.blockchain.GetTd(pm.blockchain.LastBlockHash())
if peer.Td().Cmp(td) <= 0 { if peer.Td().Cmp(td) <= 0 {
return return
} }

208
les/access/chain_access.go Normal file
View file

@ -0,0 +1,208 @@
// 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 access provides a layer to handle local blockchain database and
// on-demand network retrieval
package access
import (
"errors"
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
)
var (
ErrCancel = errors.New("ODR cancelled")
errNotInDb = errors.New("object not found in database")
)
const LogLevel = logger.Debug
var (
requestTimeout = time.Millisecond * 300
retryPeers = time.Second * 1
)
type ChainAccess struct {
db ethdb.Database
odr bool // light client mode, odr enabled
lock sync.Mutex
sentReqs map[uint64]*sentReq
sentReqCnt uint64
peers *peerSet
// p2p access objects
// parameters (light/full/archive)
}
type requestFunc func(*Peer) error
type validatorFunc func(*Msg) bool
// Request waiting for an answer that satisfies its valFunc
type sentReq struct {
valFunc validatorFunc
deliverChan chan *Msg
}
// Create a ChainAccess with ODR disabled
func NewDbChainAccess(db ethdb.Database) *ChainAccess {
return NewChainAccess(db, false)
}
// Create a ChainAccess
func NewChainAccess(db ethdb.Database, odr bool) *ChainAccess {
return &ChainAccess{
db: db,
peers: newPeerSet(),
sentReqs: make(map[uint64]*sentReq),
odr: odr,
}
}
func (self *ChainAccess) Db() ethdb.Database {
return self.db
}
func (self *ChainAccess) OdrEnabled() bool {
return self.odr
}
func (self *ChainAccess) RegisterPeer(id string, version int, head common.Hash, getBlockBodies getBlockBodiesFn, getNodeData getNodeDataFn, getReceipts getReceiptsFn, getProofs getProofsFn) error {
glog.V(logger.Detail).Infoln("Registering peer", id)
if err := self.peers.Register(newPeer(id, version, head, getBlockBodies, getNodeData, getReceipts, getProofs)); err != nil {
glog.V(logger.Error).Infoln("Register failed:", err)
return err
}
return nil
}
func (self *ChainAccess) UnregisterPeer(id string) {
self.peers.Unregister(id)
}
const (
MsgBlockBodies = iota
MsgNodeData
MsgReceipts
MsgProofs
)
type Msg struct {
MsgType int
Obj interface{}
}
// ODR request interface (passed to Retrieve, functions called by Retrieve and Deliver)
// DbGet() tries to retrieve the object from the local database (object is stored by the request struct in memory if retrieved)
// DbPut() stores it in the local database
// Request(*Peer) requests it from a LES peer
// Valid(*Msg) checks if a message is a valid answer to this request and stores the retrieved object in memory
type ObjectAccess interface {
// database storage
DbGet() bool
DbPut()
// network retrieval
Request(*Peer) error
Valid(*Msg) bool // if true, keeps the retrieved object
}
// Deliver is called by the LES protocol manager to deliver ODR reply messages to waiting requests
func (self *ChainAccess) Deliver(id string, msg *Msg) (processed bool) {
self.lock.Lock()
defer self.lock.Unlock()
for i, req := range self.sentReqs {
if req.valFunc(msg) {
req.deliverChan <- msg
delete(self.sentReqs, i)
return true
}
}
return false
}
// Send a request to known peers until an answer is received or the context is cancelled
func (self *ChainAccess) networkRequest(rqFunc requestFunc, valFunc validatorFunc, ctx *OdrContext) (*Msg, error) {
req := &sentReq{
deliverChan: make(chan *Msg),
valFunc: valFunc,
}
self.lock.Lock()
reqCnt := self.sentReqCnt
self.sentReqCnt++
self.sentReqs[reqCnt] = req
self.lock.Unlock()
defer func() {
self.lock.Lock()
delete(self.sentReqs, reqCnt)
self.lock.Unlock()
}()
var msg *Msg
for {
peers := self.peers.BestPeers()
if len(peers) == 0 {
select {
case <-ctx.cancelOrTimeout:
return nil, ErrCancel
case <-time.After(retryPeers):
}
}
for _, peer := range peers {
rqFunc(peer)
select {
case <-ctx.cancelOrTimeout:
return nil, ErrCancel
case msg = <-req.deliverChan:
peer.Promote()
glog.V(LogLevel).Infof("networkRequest success")
return msg, nil
case <-time.After(requestTimeout):
peer.Demote()
glog.V(LogLevel).Infof("networkRequest timeout")
}
}
}
}
// Retrieve tries to fetch an object from the local db, then from the LES network.
// If the network retrieval was successful, it stores the object in local db.
func (self *ChainAccess) Retrieve(obj ObjectAccess, ctx *OdrContext) (err error) {
// look in db
if obj.DbGet() {
return nil
}
if ctx != nil {
// not found in db, trying the network
_, err = self.networkRequest(obj.Request, obj.Valid, ctx)
if err == nil {
// retrieved from network, store in db
obj.DbPut()
} else {
glog.V(LogLevel).Infof("networkRequest err = %v", err)
}
return
} else {
return errNotInDb
}
}

78
les/access/context.go Normal file
View file

@ -0,0 +1,78 @@
// 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 access provides a layer to handle local blockchain database and
// on-demand network retrieval
package access
import (
"sync/atomic"
"time"
)
// Context for ODR requests, can be timed out or cancelled manually
type OdrContext struct {
cancel, cancelOrTimeout chan struct{}
id *OdrChannelID
cancelled int32
}
var NullCtx = (*OdrContext)(nil) // used when creating states
var NoOdr = (*OdrContext)(nil) // used for individual requests
func NewContext(id *OdrChannelID) *OdrContext {
ctx := &OdrContext{
cancel: make(chan struct{}),
cancelOrTimeout: make(chan struct{}),
id: id,
}
go func() {
select {
case <-ctx.cancel:
case <-time.After(id.timeout):
}
ctx.setCancelled()
close(ctx.cancelOrTimeout)
}()
return ctx
}
func (self *OdrContext) Cancel() {
close(self.cancel)
}
func (self *OdrContext) setCancelled() {
atomic.StoreInt32(&self.cancelled, 1)
}
func (self *OdrContext) IsCancelled() bool {
if self == nil {
return false
}
return atomic.LoadInt32(&self.cancelled) == 1
}
// While contexts are created for each request, channel ID is a permanent
// identifier of a source from where requests can come (like an RPC channel).
// (needed for future functions like "list of my waiting requests" and
// "cancel all requests from this channel")
type OdrChannelID struct {
timeout time.Duration
}
func NewChannelID(timeout time.Duration) *OdrChannelID {
return &OdrChannelID{timeout: timeout}
}

175
les/access/peer.go Normal file
View file

@ -0,0 +1,175 @@
// 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 access provides a layer to handle local blockchain database and
// on-demand network retrieval
package access
import (
"errors"
"sync"
"sync/atomic"
"github.com/ethereum/go-ethereum/common"
)
var (
errAlreadyRegistered = errors.New("peer is already registered")
errNotRegistered = errors.New("peer is not registered")
errNoOdr = errors.New("peer cannot serve on-demand requests")
)
type ProofReq struct {
Root common.Hash
Key []byte
}
type getBlockBodiesFn func([]common.Hash) error
type getNodeDataFn func([]common.Hash) error
type getReceiptsFn func([]common.Hash) error
type getProofsFn func([]*ProofReq) error
// access.Peer stores ODR-specific information about LES peers that are able to serve requests
type Peer struct {
id string // Unique identifier of the peer
head common.Hash // Hash of the peers latest known block
rep int32 // Simple peer reputation
GetBlockBodies getBlockBodiesFn
GetNodeData getNodeDataFn
GetReceipts getReceiptsFn
GetProofs getProofsFn
version int // LES protocol version number
}
func newPeer(id string, version int, head common.Hash, getBlockBodies getBlockBodiesFn, getNodeData getNodeDataFn, getReceipts getReceiptsFn, getProofs getProofsFn) *Peer {
return &Peer{
id: id,
head: head,
GetBlockBodies: getBlockBodies,
GetNodeData: getNodeData,
GetReceipts: getReceipts,
GetProofs: getProofs,
version: version,
}
}
func (p *Peer) Id() string {
return p.id
}
func (p *Peer) Promote() {
atomic.AddInt32(&p.rep, 1)
}
// Demote decreases the peer's reputation or leaves it at 0.
func (p *Peer) Demote() {
for {
// Calculate the new reputation value
prev := atomic.LoadInt32(&p.rep)
next := prev / 2
// Try to update the old value
if atomic.CompareAndSwapInt32(&p.rep, prev, next) {
return
}
}
}
// peerSet represents the collection of active peer participating in the block
// download procedure.
type peerSet struct {
peers map[string]*Peer
lock sync.RWMutex
}
// newPeerSet creates a new peer set top track the active download sources.
func newPeerSet() *peerSet {
return &peerSet{
peers: make(map[string]*Peer),
}
}
// Register injects a new peer into the working set, or returns an error if the
// peer is already known.
func (ps *peerSet) Register(p *Peer) error {
ps.lock.Lock()
defer ps.lock.Unlock()
if _, ok := ps.peers[p.id]; ok {
return errAlreadyRegistered
}
ps.peers[p.id] = p
return nil
}
// Unregister removes a remote peer from the active set, disabling any further
// actions to/from that particular entity.
func (ps *peerSet) Unregister(id string) error {
ps.lock.Lock()
defer ps.lock.Unlock()
if _, ok := ps.peers[id]; !ok {
return errNotRegistered
}
delete(ps.peers, id)
return nil
}
// Peer retrieves the registered peer with the given id.
func (ps *peerSet) Peer(id string) *Peer {
ps.lock.RLock()
defer ps.lock.RUnlock()
return ps.peers[id]
}
// Len returns if the current number of peers in the set.
func (ps *peerSet) Len() int {
ps.lock.RLock()
defer ps.lock.RUnlock()
return len(ps.peers)
}
// AllPeers retrieves a flat list of all the peers within the set.
func (ps *peerSet) AllPeers() []*Peer {
ps.lock.RLock()
defer ps.lock.RUnlock()
list := make([]*Peer, 0, len(ps.peers))
for _, p := range ps.peers {
list = append(list, p)
}
return list
}
func (ps *peerSet) BestPeers() []*Peer {
list := ps.AllPeers()
ps.lock.RLock()
defer ps.lock.RUnlock()
for i := 0; i < len(list); i++ {
for j := i + 1; j < len(list); j++ {
if atomic.LoadInt32(&list[i].rep) < atomic.LoadInt32(&list[j].rep) {
list[i], list[j] = list[j], list[i]
}
}
}
return list
}

510
les/handler.go Normal file
View file

@ -0,0 +1,510 @@
// 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 les implements the Light Ethereum Subprotocol.
package les
import (
"errors"
"fmt"
"sync"
//"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/eth/downloader"
"github.com/ethereum/go-ethereum/les/access"
//"github.com/ethereum/go-ethereum/eth/fetcher"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
const (
softResponseLimit = 2 * 1024 * 1024 // Target maximum size of returned blocks, headers or node data.
estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header
ethVersion = 63 // equivalent eth version for the downloader
)
// errIncompatibleConfig is returned if the requested protocols and configs are
// not compatible (low protocol version restrictions and high requirements).
var errIncompatibleConfig = errors.New("incompatible configuration")
func errResp(code errCode, format string, v ...interface{}) error {
return fmt.Errorf("%v - %v", code, fmt.Sprintf(format, v...))
}
type hashFetcherFn func(common.Hash) error
type ProtocolManager struct {
mode Mode
blockchain *core.BlockChain
chainAccess *access.ChainAccess
downloader *downloader.Downloader
//fetcher *fetcher.Fetcher
peers *peerSet
SubProtocols []p2p.Protocol
eventMux *event.TypeMux
// channels for fetcher, syncer, txsyncLoop
newPeerCh chan *peer
quitSync chan struct{}
// wait group is used for graceful shutdowns during downloading
// and processing
wg sync.WaitGroup
quit bool
}
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
// with the ethereum network.
func NewProtocolManager(mode Mode, networkId int, mux *event.TypeMux, pow pow.PoW, blockchain *core.BlockChain, ca *access.ChainAccess) (*ProtocolManager, error) {
// Create the protocol manager with the base fields
manager := &ProtocolManager{
mode: mode,
eventMux: mux,
blockchain: blockchain,
chainAccess: ca,
peers: newPeerSet(),
newPeerCh: make(chan *peer, 1),
quitSync: make(chan struct{}),
}
// Initiate a sub-protocol for every implemented version we can handle
manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions))
for i, version := range ProtocolVersions {
// Skip protocol version if incompatible with the mode of operation
if minimumProtocolVersion[mode] > version {
continue
}
// Compatible, initialize the sub-protocol
version := version // Closure for the run
manager.SubProtocols = append(manager.SubProtocols, p2p.Protocol{
Name: "les",
Version: version,
Length: ProtocolLengths[i],
Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
peer := manager.newPeer(int(version), networkId, p, rw)
manager.newPeerCh <- peer
return manager.handle(peer)
},
})
}
if len(manager.SubProtocols) == 0 {
return nil, errIncompatibleConfig
}
// Construct the different synchronisation mechanisms
var syncMode downloader.SyncMode
switch mode {
case ArchiveMode:
syncMode = downloader.NoSync
case FullMode:
syncMode = downloader.NoSync
case LightMode:
syncMode = downloader.LightSync
}
glog.V(access.LogLevel).Infof("LES: create downloader")
manager.downloader = downloader.New(syncMode, ca.Db(), manager.eventMux, blockchain.HasHeader, blockchain.HasBlock, blockchain.GetHeader,
blockchain.GetBlockNoOdr, blockchain.CurrentHeader, blockchain.CurrentBlock, blockchain.CurrentFastBlock, blockchain.FastSyncCommitHead, blockchain.GetTd,
blockchain.InsertHeaderChain, blockchain.InsertChain, blockchain.InsertReceiptChain, blockchain.Rollback, manager.removePeer)
/*validator := func(block *types.Block, parent *types.Block) error {
return core.ValidateHeader(pow, block.Header(), parent.Header(), true, false)
}
heighter := func() uint64 {
return chainman.LastBlockNumberU64()
}
manager.fetcher = fetcher.New(chainman.GetBlockNoOdr, validator, nil, heighter, chainman.InsertChain, manager.removePeer)
*/
return manager, nil
}
func (pm *ProtocolManager) removePeer(id string) {
// Short circuit if the peer was already removed
peer := pm.peers.Peer(id)
if peer == nil {
return
}
glog.V(logger.Debug).Infoln("Removing peer", id)
// Unregister the peer from the downloader and Ethereum peer set
glog.V(access.LogLevel).Infof("LES: unregister peer %v", id)
pm.downloader.UnregisterPeer(id)
if err := pm.peers.Unregister(id); err != nil {
glog.V(logger.Error).Infoln("Removal failed:", err)
}
pm.chainAccess.UnregisterPeer(id)
// Hard disconnect at the networking layer
if peer != nil {
peer.Peer.Disconnect(p2p.DiscUselessPeer)
}
}
func (pm *ProtocolManager) Start() {
// start sync handler
go pm.syncer()
}
func (pm *ProtocolManager) Stop() {
// Showing a log message. During download / process this could actually
// take between 5 to 10 seconds and therefor feedback is required.
glog.V(logger.Info).Infoln("Stopping light ethereum protocol handler...")
pm.quit = true
close(pm.quitSync) // quits syncer, fetcher
// Wait for any process action
pm.wg.Wait()
glog.V(logger.Info).Infoln("Ethereum protocol handler stopped")
}
func (pm *ProtocolManager) newPeer(pv, nv int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
return newPeer(pv, nv, p, newMeteredMsgWriter(rw))
}
// handle is the callback invoked to manage the life cycle of a les peer. When
// this function terminates, the peer is disconnected.
func (pm *ProtocolManager) handle(p *peer) error {
glog.V(logger.Debug).Infof("%v: peer connected [%s]", p, p.Name())
// Execute the LES handshake
td, head, genesis := pm.blockchain.Status()
if err := p.Handshake(td, head, genesis); err != nil {
glog.V(logger.Debug).Infof("%v: handshake failed: %v", p, err)
return err
}
if rw, ok := p.rw.(*meteredMsgReadWriter); ok {
rw.Init(p.version)
}
// Register the peer locally
glog.V(logger.Detail).Infof("%v: adding peer", p)
if err := pm.peers.Register(p); err != nil {
glog.V(logger.Error).Infof("%v: addition failed: %v", p, err)
return err
}
defer pm.removePeer(p.id)
// Register the peer in the downloader. If the downloader considers it banned, we disconnect
glog.V(access.LogLevel).Infof("LES: register peer %v", p.id)
if err := pm.downloader.RegisterPeer(p.id, ethVersion, p.Head(),
nil, nil, nil, p.RequestHeadersByHash, p.RequestHeadersByNumber,
p.RequestBodies, p.RequestReceipts, p.RequestNodeData); err != nil {
return err
}
pm.chainAccess.RegisterPeer(p.id, p.version, p.Head(), p.RequestBodies, p.RequestNodeData, p.RequestReceipts, p.RequestProofs)
// main loop. handle incoming messages.
for {
if err := pm.handleMsg(p); err != nil {
glog.V(logger.Debug).Infof("%v: message handling failed: %v", p, err)
return err
}
}
return nil
}
// handleMsg is invoked whenever an inbound message is received from a remote
// peer. The remote connection is torn down upon returning any error.
func (pm *ProtocolManager) handleMsg(p *peer) error {
// Read the next message from the remote peer, and ensure it's fully consumed
msg, err := p.rw.ReadMsg()
if err != nil {
return err
}
if msg.Size > ProtocolMaxMsgSize {
return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
}
defer msg.Discard()
// Handle the message depending on its contents
switch msg.Code {
case StatusMsg:
glog.V(access.LogLevel).Infof("LES: received StatusMsg from peer %v", p.id)
// Status messages should never arrive after the handshake
return errResp(ErrExtraStatusMsg, "uncontrolled status message")
// Block header query, collect the requested headers and reply
case GetBlockHeadersMsg:
glog.V(access.LogLevel).Infof("LES: received GetBlockHeadersMsg from peer %v", p.id)
// Decode the complex header query
var query getBlockHeadersData
if err := msg.Decode(&query); err != nil {
return errResp(ErrDecode, "%v: %v", msg, err)
}
// Gather headers until the fetch or network limits is reached
var (
bytes common.StorageSize
headers []*types.Header
unknown bool
)
for !unknown && len(headers) < int(query.Amount) && bytes < softResponseLimit && len(headers) < downloader.MaxHeaderFetch {
// Retrieve the next header satisfying the query
var origin *types.Header
if query.Origin.Hash != (common.Hash{}) {
origin = pm.blockchain.GetHeader(query.Origin.Hash)
} else {
origin = pm.blockchain.GetHeaderByNumber(query.Origin.Number)
}
if origin == nil {
break
}
headers = append(headers, origin)
bytes += estHeaderRlpSize
// Advance to the next header of the query
switch {
case query.Origin.Hash != (common.Hash{}) && query.Reverse:
// Hash based traversal towards the genesis block
for i := 0; i < int(query.Skip)+1; i++ {
if header := pm.blockchain.GetHeader(query.Origin.Hash); header != nil {
query.Origin.Hash = header.ParentHash
} else {
unknown = true
break
}
}
case query.Origin.Hash != (common.Hash{}) && !query.Reverse:
// Hash based traversal towards the leaf block
if header := pm.blockchain.GetHeaderByNumber(origin.Number.Uint64() + query.Skip + 1); header != nil {
if pm.blockchain.GetBlockHashesFromHash(header.Hash(), query.Skip+1)[query.Skip] == query.Origin.Hash {
query.Origin.Hash = header.Hash()
} else {
unknown = true
}
} else {
unknown = true
}
case query.Reverse:
// Number based traversal towards the genesis block
if query.Origin.Number >= query.Skip+1 {
query.Origin.Number -= (query.Skip + 1)
} else {
unknown = true
}
case !query.Reverse:
// Number based traversal towards the leaf block
query.Origin.Number += (query.Skip + 1)
}
}
return p.SendBlockHeaders(headers)
case BlockHeadersMsg:
glog.V(access.LogLevel).Infof("LES: received BlockHeadersMsg from peer %v", p.id)
// A batch of headers arrived to one of our previous requests
var headers []*types.Header
if err := msg.Decode(&headers); err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
// Filter out any explicitly requested headers, deliver the rest to the downloader
/*filter := len(headers) == 1
if filter {
headers = pm.fetcher.FilterHeaders(headers, time.Now())
}
if len(headers) > 0 || !filter {*/
err := pm.downloader.DeliverHeaders(p.id, headers)
if err != nil {
glog.V(logger.Debug).Infoln(err)
}
//}
case GetBlockBodiesMsg:
glog.V(access.LogLevel).Infof("LES: received GetBlockBodiesMsg from peer %v", p.id)
// Decode the retrieval message
msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
if _, err := msgStream.List(); err != nil {
return err
}
// Gather blocks until the fetch or network limits is reached
var (
hash common.Hash
bytes int
bodies []rlp.RawValue
)
for bytes < softResponseLimit && len(bodies) < downloader.MaxBlockFetch {
// Retrieve the hash of the next block
if err := msgStream.Decode(&hash); err == rlp.EOL {
break
} else if err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
// Retrieve the requested block body, stopping if enough was found
if data := pm.blockchain.GetBodyRLP(hash, access.NoOdr); len(data) != 0 {
bodies = append(bodies, data)
bytes += len(data)
}
}
return p.SendBlockBodiesRLP(bodies)
case BlockBodiesMsg:
glog.V(access.LogLevel).Infof("LES: received BlockBodiesMsg from peer %v", p.id)
// A batch of block bodies arrived to one of our previous requests
var data []*types.Body
if err := msg.Decode(&data); err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
pm.chainAccess.Deliver(p.id, &access.Msg{
MsgType: access.MsgBlockBodies,
Obj: data,
})
case GetNodeDataMsg:
glog.V(access.LogLevel).Infof("LES: received GetNodeDataMsg from peer %v", p.id)
// Decode the retrieval message
msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
if _, err := msgStream.List(); err != nil {
return err
}
// Gather state data until the fetch or network limits is reached
var (
hash common.Hash
bytes int
data [][]byte
)
for bytes < softResponseLimit && len(data) < downloader.MaxStateFetch {
// Retrieve the hash of the next state entry
if err := msgStream.Decode(&hash); err == rlp.EOL {
break
} else if err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
// Retrieve the requested state entry, stopping if enough was found
if entry, err := pm.chainAccess.Db().Get(hash.Bytes()); err == nil {
data = append(data, entry)
bytes += len(entry)
}
}
return p.SendNodeData(data)
case NodeDataMsg:
glog.V(access.LogLevel).Infof("LES: received NodeDataMsg from peer %v", p.id)
// A batch of node state data arrived to one of our previous requests
var data [][]byte
if err := msg.Decode(&data); err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
pm.chainAccess.Deliver(p.id, &access.Msg{
MsgType: access.MsgNodeData,
Obj: data,
})
case GetReceiptsMsg:
glog.V(access.LogLevel).Infof("LES: received GetReceiptsMsg from peer %v", p.id)
// Decode the retrieval message
msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
if _, err := msgStream.List(); err != nil {
return err
}
// Gather state data until the fetch or network limits is reached
var (
hash common.Hash
bytes int
receipts []rlp.RawValue
)
for bytes < softResponseLimit && len(receipts) < downloader.MaxReceiptFetch {
// Retrieve the hash of the next block
if err := msgStream.Decode(&hash); err == rlp.EOL {
break
} else if err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
// Retrieve the requested block's receipts, skipping if unknown to us
results := core.GetBlockReceipts(pm.chainAccess, hash, access.NoOdr)
if results == nil {
if header := pm.blockchain.GetHeader(hash); header == nil || header.ReceiptHash != types.EmptyRootHash {
continue
}
}
// If known, encode and queue for response packet
if encoded, err := rlp.EncodeToBytes(results); err != nil {
glog.V(logger.Error).Infof("failed to encode receipt: %v", err)
} else {
receipts = append(receipts, encoded)
bytes += len(encoded)
}
}
return p.SendReceiptsRLP(receipts)
case ReceiptsMsg:
glog.V(access.LogLevel).Infof("LES: received ReceiptsMsg from peer %v", p.id)
// A batch of receipts arrived to one of our previous requests
var receipts []types.Receipts
if err := msg.Decode(&receipts); err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
pm.chainAccess.Deliver(p.id, &access.Msg{
MsgType: access.MsgReceipts,
Obj: receipts,
})
case GetProofsMsg:
glog.V(access.LogLevel).Infof("LES: received GetProofsMsg from peer %v", p.id)
// Decode the retrieval message
msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
if _, err := msgStream.List(); err != nil {
return err
}
// Gather state data until the fetch or network limits is reached
var (
req access.ProofReq
bytes int
proofs proofsData
)
for bytes < softResponseLimit && len(proofs) < downloader.MaxProofsFetch {
// Retrieve the hash of the next state entry
if err := msgStream.Decode(&req); err == rlp.EOL {
break
} else if err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
// Retrieve the requested state entry, stopping if enough was found
if trie, _ := trie.NewSecure(req.Root, pm.chainAccess.Db(), nil); trie != nil {
proof := trie.Prove(req.Key)
proofs = append(proofs, proof)
bytes += len(proof)
}
}
return p.SendProofs(proofs)
case ProofsMsg:
glog.V(access.LogLevel).Infof("LES: received ProofsMsg from peer %v", p.id)
// A batch of merkle proofs arrived to one of our previous requests
var data []trie.MerkleProof
if err := msg.Decode(&data); err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
pm.chainAccess.Deliver(p.id, &access.Msg{
MsgType: access.MsgProofs,
Obj: data,
})
default:
glog.V(access.LogLevel).Infof("LES: received unknown message with code %d from peer %v", msg.Code, p.id)
return errResp(ErrInvalidMsgCode, "%v", msg.Code)
}
return nil
}

330
les/handler_test.go Normal file
View file

@ -0,0 +1,330 @@
package les
import (
"fmt"
"math/rand"
"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/crypto"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/trie"
)
// Tests that block headers can be retrieved from a remote chain based on user queries.
func TestGetBlockHeadersLes1(t *testing.T) { testGetBlockHeaders(t, 1) }
func testGetBlockHeaders(t *testing.T, protocol int) {
pm, _ := newTestProtocolManagerMust(t, false, downloader.MaxHashFetch+15, nil)
peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close()
// Create a "random" unknown hash for testing
var unknown common.Hash
for i, _ := range unknown {
unknown[i] = byte(i)
}
// Create a batch of tests for various scenarios
limit := uint64(downloader.MaxHeaderFetch)
tests := []struct {
query *getBlockHeadersData // The query to execute for header retrieval
expect []common.Hash // The hashes of the block whose headers are expected
}{
// A single random block should be retrievable by hash and number too
{
&getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash()}, Amount: 1},
[]common.Hash{pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash()},
}, {
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1},
[]common.Hash{pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash()},
},
// Multiple headers should be retrievable in both directions
{
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3},
[]common.Hash{
pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(limit/2+1, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(limit/2+2, access.NoOdr).Hash(),
},
}, {
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true},
[]common.Hash{
pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(limit/2-1, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(limit/2-2, access.NoOdr).Hash(),
},
},
// Multiple headers with skip lists should be retrievable
{
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3},
[]common.Hash{
pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(limit/2+4, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(limit/2+8, access.NoOdr).Hash(),
},
}, {
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true},
[]common.Hash{
pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(limit/2-4, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(limit/2-8, access.NoOdr).Hash(),
},
},
// The chain endpoints should be retrievable
{
&getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1},
[]common.Hash{pm.blockchain.GetBlockByNumber(0, access.NoOdr).Hash()},
}, {
&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64()}, Amount: 1},
[]common.Hash{pm.blockchain.CurrentBlock().Hash()},
},
// Ensure protocol limits are honored
{
&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true},
pm.blockchain.GetBlockHashesFromHash(pm.blockchain.CurrentBlock().Hash(), limit),
},
// Check that requesting more than available is handled gracefully
{
&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3},
[]common.Hash{
pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()-4, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64(), access.NoOdr).Hash(),
},
}, {
&getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true},
[]common.Hash{
pm.blockchain.GetBlockByNumber(4, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(0, access.NoOdr).Hash(),
},
},
// Check that requesting more than available is handled gracefully, even if mid skip
{
&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3},
[]common.Hash{
pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()-4, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()-1, access.NoOdr).Hash(),
},
}, {
&getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true},
[]common.Hash{
pm.blockchain.GetBlockByNumber(4, access.NoOdr).Hash(),
pm.blockchain.GetBlockByNumber(1, access.NoOdr).Hash(),
},
},
// Check that non existing headers aren't returned
{
&getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1},
[]common.Hash{},
}, {
&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() + 1}, Amount: 1},
[]common.Hash{},
},
}
// Run each of the tests and verify the results against the chain
for i, tt := range tests {
// Collect the headers to expect in the response
headers := []*types.Header{}
for _, hash := range tt.expect {
headers = append(headers, pm.blockchain.GetBlock(hash, access.NoOdr).Header())
}
// Send the hash request and verify the response
p2p.Send(peer.app, GetBlockHeadersMsg, tt.query)
if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, headers); err != nil {
t.Errorf("test %d: headers mismatch: %v", i, err)
}
}
}
// Tests that block contents can be retrieved from a remote chain based on their hashes.
func TestGetBlockBodiesLes1(t *testing.T) { testGetBlockBodies(t, 1) }
func testGetBlockBodies(t *testing.T, protocol int) {
pm, _ := newTestProtocolManagerMust(t, false, downloader.MaxBlockFetch+15, nil)
peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close()
// Create a batch of tests for various scenarios
limit := downloader.MaxBlockFetch
tests := []struct {
random int // Number of blocks to fetch randomly from the chain
explicit []common.Hash // Explicitly requested blocks
available []bool // Availability of explicitly requested blocks
expected int // Total number of existing blocks to expect
}{
{1, nil, nil, 1}, // A single random block should be retrievable
{10, nil, nil, 10}, // Multiple random blocks should be retrievable
{limit, nil, nil, limit}, // The maximum possible blocks should be retrievable
{limit + 1, nil, nil, limit}, // No more than the possible block count should be returned
{0, []common.Hash{pm.blockchain.Genesis().Hash()}, []bool{true}, 1}, // The genesis block should be retrievable
{0, []common.Hash{pm.blockchain.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable
{0, []common.Hash{common.Hash{}}, []bool{false}, 0}, // A non existent block should not be returned
// Existing and non-existing blocks interleaved should not cause problems
{0, []common.Hash{
common.Hash{},
pm.blockchain.GetBlockByNumber(1, access.NoOdr).Hash(),
common.Hash{},
pm.blockchain.GetBlockByNumber(10, access.NoOdr).Hash(),
common.Hash{},
pm.blockchain.GetBlockByNumber(100, access.NoOdr).Hash(),
common.Hash{},
}, []bool{false, true, false, true, false, true, false}, 3},
}
// Run each of the tests and verify the results against the chain
for i, tt := range tests {
// Collect the hashes to request, and the response to expect
hashes, seen := []common.Hash{}, make(map[int64]bool)
bodies := []*types.Body{}
for j := 0; j < tt.random; j++ {
for {
num := rand.Int63n(int64(pm.blockchain.CurrentBlock().NumberU64()))
if !seen[num] {
seen[num] = true
block := pm.blockchain.GetBlockByNumber(uint64(num), access.NoOdr)
hashes = append(hashes, block.Hash())
if len(bodies) < tt.expected {
bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()})
}
break
}
}
}
for j, hash := range tt.explicit {
hashes = append(hashes, hash)
if tt.available[j] && len(bodies) < tt.expected {
block := pm.blockchain.GetBlock(hash, access.NoOdr)
bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()})
}
}
// Send the hash request and verify the response
p2p.Send(peer.app, GetBlockBodiesMsg, hashes)
if err := p2p.ExpectMsg(peer.app, BlockBodiesMsg, bodies); err != nil {
t.Errorf("test %d: bodies mismatch: %v", i, err)
}
}
}
// Tests that the node state database can be retrieved based on hashes.
func TestGetNodeDataLes1(t *testing.T) { testGetNodeData(t, 1) }
func testGetNodeData(t *testing.T, protocol int) {
// Assemble the test environment
pm, _ := newTestProtocolManagerMust(t, false, 4, testChainGen)
peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close()
// Fetch for now the entire chain db
hashes := []common.Hash{}
for _, key := range pm.chainAccess.Db().(*ethdb.MemDatabase).Keys() {
if len(key) == len(common.Hash{}) {
hashes = append(hashes, common.BytesToHash(key))
}
}
p2p.Send(peer.app, GetNodeDataMsg, hashes)
msg, err := peer.app.ReadMsg()
if err != nil {
t.Fatalf("failed to read node data response: %v", err)
}
if msg.Code != NodeDataMsg {
t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, 0x0c)
}
var data [][]byte
if err := msg.Decode(&data); err != nil {
t.Fatalf("failed to decode response node data: %v", err)
}
// Verify that all hashes correspond to the requested data, and reconstruct a state tree
for i, want := range hashes {
if hash := crypto.Sha3Hash(data[i]); hash != want {
fmt.Errorf("data hash mismatch: have %x, want %x", hash, want)
}
}
statedb, _ := ethdb.NewMemDatabase()
for i := 0; i < len(data); i++ {
statedb.Put(hashes[i].Bytes(), data[i])
}
accounts := []common.Address{testBankAddress, acc1Addr, acc2Addr}
for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
trie, _ := state.New(pm.blockchain.GetBlockByNumber(i, access.NoOdr).Root(), access.NewDbChainAccess(statedb), access.NullCtx)
for j, acc := range accounts {
state, _ := pm.blockchain.State(access.NullCtx)
bw := state.GetBalance(acc)
bh := trie.GetBalance(acc)
if (bw != nil && bh == nil) || (bw == nil && bh != nil) {
t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw)
}
if bw != nil && bh != nil && bw.Cmp(bw) != 0 {
t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw)
}
}
}
}
// Tests that the transaction receipts can be retrieved based on hashes.
func TestGetReceiptLes1(t *testing.T) { testGetReceipt(t, 1) }
func testGetReceipt(t *testing.T, protocol int) {
// Assemble the test environment
pm, _ := newTestProtocolManagerMust(t, false, 4, testChainGen)
peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close()
// Collect the hashes to request, and the response to expect
hashes, receipts := []common.Hash{}, []types.Receipts{}
for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
block := pm.blockchain.GetBlockByNumber(i, access.NoOdr)
hashes = append(hashes, block.Hash())
receipts = append(receipts, core.GetBlockReceipts(pm.chainAccess, block.Hash(), access.NoOdr))
}
// Send the hash request and verify the response
p2p.Send(peer.app, GetReceiptsMsg, hashes)
if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, receipts); err != nil {
t.Errorf("receipts mismatch: %v", err)
}
}
// Tests that trie merkle proofs can be retrieved
func TestGetProofsLes1(t *testing.T) { testGetReceipt(t, 1) }
func testGetProofs(t *testing.T, protocol int) {
// Assemble the test environment
pm, ca := newTestProtocolManagerMust(t, false, 4, testChainGen)
db := ca.Db()
peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close()
var proofreqs []access.ProofReq
var proofs []trie.MerkleProof
accounts := []common.Address{testBankAddress, acc1Addr, acc2Addr, common.Address{}}
for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
root := pm.blockchain.GetBlockByNumber(i, access.NoOdr).Root()
trie, _ := trie.NewSecure(root, db, nil)
for _, acc := range accounts {
req := access.ProofReq{
Root: root,
Key: acc[:],
}
proofreqs = append(proofreqs, req)
proof := trie.Prove(acc[:])
proofs = append(proofs, proof)
}
}
// Send the proof request and verify the response
p2p.Send(peer.app, GetProofsMsg, proofreqs)
if err := p2p.ExpectMsg(peer.app, ProofsMsg, proofs); err != nil {
t.Errorf("receipts mismatch: %v", err)
}
}

240
les/helper_test.go Normal file
View file

@ -0,0 +1,240 @@
// This file contains some shares testing functionality, common to multiple
// different files and modules being tested.
package les
import (
"crypto/rand"
"math/big"
"sync"
"testing"
"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/event"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/params"
)
var (
testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
testBankFunds = big.NewInt(1000000)
acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
acc1Addr = crypto.PubkeyToAddress(acc1Key.PublicKey)
acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey)
)
func testChainGen(i int, block *core.BlockGen) {
switch i {
case 0:
// In block 1, the test bank sends account #1 some ether.
tx, _ := types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil).SignECDSA(testBankKey)
block.AddTx(tx)
case 1:
// In block 2, the test bank sends some more ether to account #1.
// acc1Addr passes it on to account #2.
// acc1Addr creates a test contract.
tx1, _ := types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(testBankKey)
nonce := block.TxNonce(acc1Addr)
tx2, _ := types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(acc1Key)
nonce++
tx3, _ := types.NewContractCreation(nonce, big.NewInt(0), big.NewInt(100000), big.NewInt(0), testContractCode).SignECDSA(acc1Key)
testContractAddr = crypto.CreateAddress(acc1Addr, nonce)
block.AddTx(tx1)
block.AddTx(tx2)
block.AddTx(tx3)
case 2:
// Block 3 is empty but was mined by account #2.
block.SetCoinbase(acc2Addr)
block.SetExtra([]byte("yeehaw"))
data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001")
tx, _ := types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), big.NewInt(100000), nil, data).SignECDSA(testBankKey)
block.AddTx(tx)
case 3:
// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
b2 := block.PrevBlock(1).Header()
b2.Extra = []byte("foo")
block.AddUncle(b2)
b3 := block.PrevBlock(2).Header()
b3.Extra = []byte("foo")
block.AddUncle(b3)
data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002")
tx, _ := types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), big.NewInt(100000), nil, data).SignECDSA(testBankKey)
block.AddTx(tx)
}
}
// newTestProtocolManager creates a new protocol manager for testing purposes,
// with the given number of blocks already known, and potential notification
// channels for different events.
func newTestProtocolManager(mode Mode, blocks int, generator func(int, *core.BlockGen)) (*ProtocolManager, *access.ChainAccess, error) {
var (
evmux = new(event.TypeMux)
pow = new(core.FakePow)
db, _ = ethdb.NewMemDatabase()
ca = access.NewChainAccess(db, mode == LightMode)
genesis = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{testBankAddress, testBankFunds})
blockchain, _ = core.NewBlockChain(ca, pow, evmux)
blockproc = core.NewBlockProcessor(ca, pow, blockchain, evmux)
)
blockchain.SetProcessor(blockproc)
chain, _ := core.GenerateChain(genesis, db, blocks, generator)
if _, err := blockchain.InsertChain(chain); err != nil {
panic(err)
}
pm, err := NewProtocolManager(mode, NetworkId, evmux, pow, blockchain, ca)
if err != nil {
return nil, nil, err
}
pm.Start()
return pm, ca, nil
}
// newTestProtocolManagerMust creates a new protocol manager for testing purposes,
// with the given number of blocks already known, and potential notification
// channels for different events. In case of an error, the constructor force-
// fails the test.
func newTestProtocolManagerMust(t *testing.T, lightSync bool, blocks int, generator func(int, *core.BlockGen)) (*ProtocolManager, *access.ChainAccess) {
mode := ArchiveMode
if lightSync {
mode = LightMode
}
pm, ca, err := newTestProtocolManager(mode, blocks, generator)
if err != nil {
t.Fatalf("Failed to create protocol manager: %v", err)
}
return pm, ca
}
// testTxPool is a fake, helper transaction pool for testing purposes
type testTxPool struct {
pool []*types.Transaction // Collection of all transactions
added chan<- []*types.Transaction // Notification channel for new transactions
lock sync.RWMutex // Protects the transaction pool
}
// AddTransactions appends a batch of transactions to the pool, and notifies any
// listeners if the addition channel is non nil
func (p *testTxPool) AddTransactions(txs []*types.Transaction) {
p.lock.Lock()
defer p.lock.Unlock()
p.pool = append(p.pool, txs...)
if p.added != nil {
p.added <- txs
}
}
// GetTransactions returns all the transactions known to the pool
func (p *testTxPool) GetTransactions() types.Transactions {
p.lock.RLock()
defer p.lock.RUnlock()
txs := make([]*types.Transaction, len(p.pool))
copy(txs, p.pool)
return txs
}
// newTestTransaction create a new dummy transaction.
func newTestTransaction(from *crypto.Key, nonce uint64, datasize int) *types.Transaction {
tx := types.NewTransaction(nonce, common.Address{}, big.NewInt(0), big.NewInt(100000), big.NewInt(0), make([]byte, datasize))
tx, _ = tx.SignECDSA(from.PrivateKey)
return tx
}
// testPeer is a simulated peer to allow testing direct network calls.
type testPeer struct {
net p2p.MsgReadWriter // Network layer reader/writer to simulate remote messaging
app *p2p.MsgPipeRW // Application layer reader/writer to simulate the local side
*peer
}
// newTestPeer creates a new peer registered at the given protocol manager.
func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*testPeer, <-chan error) {
// Create a message pipe to communicate through
app, net := p2p.MsgPipe()
// Generate a random id and create the peer
var id discover.NodeID
rand.Read(id[:])
peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)
// Start the peer on a new thread
errc := make(chan error, 1)
go func() {
pm.newPeerCh <- peer
errc <- pm.handle(peer)
}()
tp := &testPeer{
app: app,
net: net,
peer: peer,
}
// Execute any implicitly requested handshakes and return
if shake {
td, head, genesis := pm.blockchain.Status()
tp.handshake(nil, td, head, genesis)
}
return tp, errc
}
func newTestPeerPair(name string, version int, pm, pm2 *ProtocolManager) (*peer, <-chan error, *peer, <-chan error) {
// Create a message pipe to communicate through
app, net := p2p.MsgPipe()
// Generate a random id and create the peer
var id discover.NodeID
rand.Read(id[:])
peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)
peer2 := pm2.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), app)
// Start the peer on a new thread
errc := make(chan error, 1)
errc2 := make(chan error, 1)
go func() {
pm.newPeerCh <- peer
errc <- pm.handle(peer)
}()
go func() {
pm2.newPeerCh <- peer2
errc2 <- pm2.handle(peer2)
}()
return peer, errc, peer2, errc2
}
// handshake simulates a trivial handshake that expects the same state from the
// remote side as we are simulating locally.
func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, genesis common.Hash) {
msg := &statusData{
ProtocolVersion: uint32(p.version),
NetworkId: uint32(NetworkId),
TD: td,
CurrentBlock: head,
GenesisBlock: genesis,
}
if err := p2p.ExpectMsg(p.app, StatusMsg, msg); err != nil {
t.Fatalf("status recv: %v", err)
}
if err := p2p.Send(p.app, StatusMsg, msg); err != nil {
t.Fatalf("status send: %v", err)
}
}
// close terminates the local side of the peer, notifying the remote protocol
// manager of termination.
func (p *testPeer) close() {
p.app.Close()
}

111
les/metrics.go Normal file
View file

@ -0,0 +1,111 @@
// 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 les
import (
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/p2p"
)
var (
/* propTxnInPacketsMeter = metrics.NewMeter("eth/prop/txns/in/packets")
propTxnInTrafficMeter = metrics.NewMeter("eth/prop/txns/in/traffic")
propTxnOutPacketsMeter = metrics.NewMeter("eth/prop/txns/out/packets")
propTxnOutTrafficMeter = metrics.NewMeter("eth/prop/txns/out/traffic")
propHashInPacketsMeter = metrics.NewMeter("eth/prop/hashes/in/packets")
propHashInTrafficMeter = metrics.NewMeter("eth/prop/hashes/in/traffic")
propHashOutPacketsMeter = metrics.NewMeter("eth/prop/hashes/out/packets")
propHashOutTrafficMeter = metrics.NewMeter("eth/prop/hashes/out/traffic")
propBlockInPacketsMeter = metrics.NewMeter("eth/prop/blocks/in/packets")
propBlockInTrafficMeter = metrics.NewMeter("eth/prop/blocks/in/traffic")
propBlockOutPacketsMeter = metrics.NewMeter("eth/prop/blocks/out/packets")
propBlockOutTrafficMeter = metrics.NewMeter("eth/prop/blocks/out/traffic")
reqHashInPacketsMeter = metrics.NewMeter("eth/req/hashes/in/packets")
reqHashInTrafficMeter = metrics.NewMeter("eth/req/hashes/in/traffic")
reqHashOutPacketsMeter = metrics.NewMeter("eth/req/hashes/out/packets")
reqHashOutTrafficMeter = metrics.NewMeter("eth/req/hashes/out/traffic")
reqBlockInPacketsMeter = metrics.NewMeter("eth/req/blocks/in/packets")
reqBlockInTrafficMeter = metrics.NewMeter("eth/req/blocks/in/traffic")
reqBlockOutPacketsMeter = metrics.NewMeter("eth/req/blocks/out/packets")
reqBlockOutTrafficMeter = metrics.NewMeter("eth/req/blocks/out/traffic")
reqHeaderInPacketsMeter = metrics.NewMeter("eth/req/headers/in/packets")
reqHeaderInTrafficMeter = metrics.NewMeter("eth/req/headers/in/traffic")
reqHeaderOutPacketsMeter = metrics.NewMeter("eth/req/headers/out/packets")
reqHeaderOutTrafficMeter = metrics.NewMeter("eth/req/headers/out/traffic")
reqBodyInPacketsMeter = metrics.NewMeter("eth/req/bodies/in/packets")
reqBodyInTrafficMeter = metrics.NewMeter("eth/req/bodies/in/traffic")
reqBodyOutPacketsMeter = metrics.NewMeter("eth/req/bodies/out/packets")
reqBodyOutTrafficMeter = metrics.NewMeter("eth/req/bodies/out/traffic")
reqStateInPacketsMeter = metrics.NewMeter("eth/req/states/in/packets")
reqStateInTrafficMeter = metrics.NewMeter("eth/req/states/in/traffic")
reqStateOutPacketsMeter = metrics.NewMeter("eth/req/states/out/packets")
reqStateOutTrafficMeter = metrics.NewMeter("eth/req/states/out/traffic")
reqReceiptInPacketsMeter = metrics.NewMeter("eth/req/receipts/in/packets")
reqReceiptInTrafficMeter = metrics.NewMeter("eth/req/receipts/in/traffic")
reqReceiptOutPacketsMeter = metrics.NewMeter("eth/req/receipts/out/packets")
reqReceiptOutTrafficMeter = metrics.NewMeter("eth/req/receipts/out/traffic")*/
miscInPacketsMeter = metrics.NewMeter("les/misc/in/packets")
miscInTrafficMeter = metrics.NewMeter("les/misc/in/traffic")
miscOutPacketsMeter = metrics.NewMeter("les/misc/out/packets")
miscOutTrafficMeter = metrics.NewMeter("les/misc/out/traffic")
)
// meteredMsgReadWriter is a wrapper around a p2p.MsgReadWriter, capable of
// accumulating the above defined metrics based on the data stream contents.
type meteredMsgReadWriter struct {
p2p.MsgReadWriter // Wrapped message stream to meter
version int // Protocol version to select correct meters
}
// newMeteredMsgWriter wraps a p2p MsgReadWriter with metering support. If the
// metrics system is disabled, this fucntion returns the original object.
func newMeteredMsgWriter(rw p2p.MsgReadWriter) p2p.MsgReadWriter {
if !metrics.Enabled {
return rw
}
return &meteredMsgReadWriter{MsgReadWriter: rw}
}
// Init sets the protocol version used by the stream to know which meters to
// increment in case of overlapping message ids between protocol versions.
func (rw *meteredMsgReadWriter) Init(version int) {
rw.version = version
}
func (rw *meteredMsgReadWriter) ReadMsg() (p2p.Msg, error) {
// Read the message and short circuit in case of an error
msg, err := rw.MsgReadWriter.ReadMsg()
if err != nil {
return msg, err
}
// Account for the data traffic
packets, traffic := miscInPacketsMeter, miscInTrafficMeter
packets.Mark(1)
traffic.Mark(int64(msg.Size))
return msg, err
}
func (rw *meteredMsgReadWriter) WriteMsg(msg p2p.Msg) error {
// Account for the data traffic
packets, traffic := miscOutPacketsMeter, miscOutTrafficMeter
packets.Mark(1)
traffic.Mark(int64(msg.Size))
// Send the packet to the p2p layer
return rw.MsgReadWriter.WriteMsg(msg)
}

169
les/odr_test.go Normal file
View file

@ -0,0 +1,169 @@
package les
import (
"bytes"
"math/big"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
var (
testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056")
testContractAddr common.Address
)
/*
contract test {
uint256[100] data;
function Put(uint256 addr, uint256 value) {
data[addr] = value;
}
function Get(uint256 addr) constant returns (uint256 value) {
return data[addr];
}
}
*/
type odrTestFn func(ca *access.ChainAccess, bc *core.BlockChain, ctx *access.OdrContext, bhash common.Hash) []byte
func TestOdrGetBlockLes1(t *testing.T) { testOdr(t, 1, 1, odrGetBlock) }
func odrGetBlock(ca *access.ChainAccess, bc *core.BlockChain, ctx *access.OdrContext, bhash common.Hash) []byte {
block := bc.GetBlock(bhash, ctx)
if block == nil {
return nil
}
rlp, _ := rlp.EncodeToBytes(block)
return rlp
}
func TestOdrGetReceiptsLes1(t *testing.T) { testOdr(t, 1, 1, odrGetReceipts) }
func odrGetReceipts(ca *access.ChainAccess, bc *core.BlockChain, ctx *access.OdrContext, bhash common.Hash) []byte {
receipts := core.GetBlockReceipts(ca, bhash, ctx)
if receipts == nil {
return nil
}
rlp, _ := rlp.EncodeToBytes(receipts)
return rlp
}
func TestOdrAccountsLes1(t *testing.T) { testOdr(t, 1, 1, odrAccounts) }
func odrAccounts(ca *access.ChainAccess, bc *core.BlockChain, ctx *access.OdrContext, bhash common.Hash) []byte {
dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678")
acc := []common.Address{testBankAddress, acc1Addr, acc2Addr, dummyAddr}
trie.ClearGlobalCache()
var res []byte
for _, addr := range acc {
header := bc.GetHeader(bhash)
st, err := state.New(header.Root, ca, ctx)
if err == nil {
bal := st.GetBalance(addr)
rlp, _ := rlp.EncodeToBytes(bal)
res = append(res, rlp...)
}
}
return res
}
func TestOdrContractCallLes1(t *testing.T) { testOdr(t, 1, 2, odrContractCall) }
// callmsg is the message type used for call transations.
type callmsg struct {
from *state.StateObject
to *common.Address
gas, gasPrice *big.Int
value *big.Int
data []byte
}
// accessor boilerplate to implement core.Message
func (m callmsg) From() (common.Address, error) { return m.from.Address(), nil }
func (m callmsg) Nonce() uint64 { return m.from.Nonce() }
func (m callmsg) To() *common.Address { return m.to }
func (m callmsg) GasPrice() *big.Int { return m.gasPrice }
func (m callmsg) Gas() *big.Int { return m.gas }
func (m callmsg) Value() *big.Int { return m.value }
func (m callmsg) Data() []byte { return m.data }
func odrContractCall(ca *access.ChainAccess, bc *core.BlockChain, ctx *access.OdrContext, bhash common.Hash) []byte {
data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000")
var res []byte
for i := 0; i < 3; i++ {
data[35] = byte(i)
header := bc.GetHeader(bhash)
statedb, err := state.New(header.Root, ca, ctx)
if err == nil {
from := statedb.GetOrNewStateObject(testBankAddress)
from.SetBalance(common.MaxBig)
msg := callmsg{
from: from,
gas: big.NewInt(100000),
gasPrice: big.NewInt(0),
value: big.NewInt(0),
data: data,
to: &testContractAddr,
}
vmenv := core.NewEnv(statedb, bc, msg, header)
gp := new(core.GasPool).AddGas(common.MaxBig)
ret, _, _ := core.ApplyMessage(vmenv, msg, gp)
res = append(res, ret...)
}
}
return res
}
func testOdr(t *testing.T, protocol int, expFail uint64, fn odrTestFn) {
// Assemble the test environment
pm, ca := newTestProtocolManagerMust(t, false, 4, testChainGen)
lpm, lca := newTestProtocolManagerMust(t, true, 0, nil)
_, _, lpeer, _ := newTestPeerPair("peer", protocol, pm, lpm)
time.Sleep(time.Millisecond * 100)
lpm.synchronise(lpeer)
cid := access.NewChannelID(time.Millisecond * 200)
test := func(expFail uint64) {
for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
bhash := core.GetCanonicalHash(ca.Db(), i)
b1 := fn(ca, pm.blockchain, access.NoOdr, bhash)
b2 := fn(lca, lpm.blockchain, access.NewContext(cid), bhash)
eq := bytes.Equal(b1, b2)
exp := i < expFail
if exp && !eq {
t.Errorf("odr mismatch")
}
if !exp && eq {
t.Errorf("unexpected odr match")
}
}
}
// temporarily remove peer to test odr fails
lca.UnregisterPeer(lpeer.id)
// expect retrievals to fail (except genesis block) without a les peer
test(expFail)
lca.RegisterPeer(lpeer.id, lpeer.version, lpeer.Head(), lpeer.RequestBodies, lpeer.RequestNodeData, lpeer.RequestReceipts, lpeer.RequestProofs)
// expect all retrievals to pass
test(5)
lca.UnregisterPeer(lpeer.id)
// still expect all retrievals to pass, now data should be cached locally
test(5)
}

282
les/peer.go Normal file
View file

@ -0,0 +1,282 @@
// 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 les implements the Light Ethereum Subprotocol.
package les
import (
"errors"
"fmt"
"math/big"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rlp"
"gopkg.in/fatih/set.v0"
)
var (
errAlreadyRegistered = errors.New("peer is already registered")
errNotRegistered = errors.New("peer is not registered")
)
const (
maxKnownBlocks = 1024 // Maximum block hashes to keep in the known list (prevent DOS)
)
type peer struct {
*p2p.Peer
rw p2p.MsgReadWriter
version int // Protocol version negotiated
network int // Network ID being on
id string
head common.Hash
td *big.Int
lock sync.RWMutex
knownBlocks *set.Set // Set of block hashes known to be known by this peer
}
func newPeer(version, network int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
id := p.ID()
return &peer{
Peer: p,
rw: rw,
version: version,
network: network,
id: fmt.Sprintf("%x", id[:8]),
knownBlocks: set.New(),
}
}
// Head retrieves a copy of the current head (most recent) hash of the peer.
func (p *peer) Head() (hash common.Hash) {
p.lock.RLock()
defer p.lock.RUnlock()
copy(hash[:], p.head[:])
return hash
}
// Td retrieves the current total difficulty of a peer.
func (p *peer) Td() *big.Int {
p.lock.RLock()
defer p.lock.RUnlock()
return new(big.Int).Set(p.td)
}
// SendBlockHeaders sends a batch of block headers to the remote peer.
func (p *peer) SendBlockHeaders(headers []*types.Header) error {
return p2p.Send(p.rw, BlockHeadersMsg, headers)
}
// SendBlockBodiesRLP sends a batch of block contents to the remote peer from
// an already RLP encoded format.
func (p *peer) SendBlockBodiesRLP(bodies []rlp.RawValue) error {
return p2p.Send(p.rw, BlockBodiesMsg, bodies)
}
// SendNodeDataRLP sends a batch of arbitrary internal data, corresponding to the
// hashes requested.
func (p *peer) SendNodeData(data [][]byte) error {
return p2p.Send(p.rw, NodeDataMsg, data)
}
// SendReceiptsRLP sends a batch of transaction receipts, corresponding to the
// ones requested from an already RLP encoded format.
func (p *peer) SendReceiptsRLP(receipts []rlp.RawValue) error {
return p2p.Send(p.rw, ReceiptsMsg, receipts)
}
// SendProofs sends a batch of merkle proofs, corresponding to the ones requested.
func (p *peer) SendProofs(proofs proofsData) error {
return p2p.Send(p.rw, ProofsMsg, proofs)
}
// RequestHeadersByHash fetches a batch of blocks' headers corresponding to the
// specified header query, based on the hash of an origin block.
func (p *peer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool) error {
glog.V(logger.Debug).Infof("%v fetching %d headers from %x, skipping %d (reverse = %v)", p, amount, origin[:4], skip, reverse)
return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse})
}
// RequestHeadersByNumber fetches a batch of blocks' headers corresponding to the
// specified header query, based on the number of an origin block.
func (p *peer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error {
glog.V(logger.Debug).Infof("%v fetching %d headers from #%d, skipping %d (reverse = %v)", p, amount, origin, skip, reverse)
return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Number: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse})
}
// RequestBodies fetches a batch of blocks' bodies corresponding to the hashes
// specified.
func (p *peer) RequestBodies(hashes []common.Hash) error {
glog.V(logger.Debug).Infof("%v fetching %d block bodies", p, len(hashes))
return p2p.Send(p.rw, GetBlockBodiesMsg, hashes)
}
// RequestNodeData fetches a batch of arbitrary data from a node's known state
// data, corresponding to the specified hashes.
func (p *peer) RequestNodeData(hashes []common.Hash) error {
glog.V(logger.Debug).Infof("%v fetching %v state data", p, len(hashes))
return p2p.Send(p.rw, GetNodeDataMsg, hashes)
}
// RequestReceipts fetches a batch of transaction receipts from a remote node.
func (p *peer) RequestReceipts(hashes []common.Hash) error {
glog.V(logger.Debug).Infof("%v fetching %v receipts", p, len(hashes))
return p2p.Send(p.rw, GetReceiptsMsg, hashes)
}
// RequestProofs fetches a batch of merkle proofs from a remote node.
func (p *peer) RequestProofs(reqs []*access.ProofReq) error {
glog.V(logger.Debug).Infof("%v fetching %v proofs", p, len(reqs))
return p2p.Send(p.rw, GetProofsMsg, reqs)
}
// Handshake executes the les protocol handshake, negotiating version number,
// network IDs, difficulties, head and genesis blocks.
func (p *peer) Handshake(td *big.Int, head common.Hash, genesis common.Hash) error {
// Send out own handshake in a new thread
errc := make(chan error, 1)
go func() {
errc <- p2p.Send(p.rw, StatusMsg, &statusData{
ProtocolVersion: uint32(p.version),
NetworkId: uint32(p.network),
TD: td,
CurrentBlock: head,
GenesisBlock: genesis,
})
}()
// In the mean time retrieve the remote status message
msg, err := p.rw.ReadMsg()
if err != nil {
return err
}
if msg.Code != StatusMsg {
return errResp(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg)
}
if msg.Size > ProtocolMaxMsgSize {
return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
}
// Decode the handshake and make sure everything matches
var status statusData
if err := msg.Decode(&status); err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
if status.GenesisBlock != genesis {
return errResp(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock, genesis)
}
if int(status.NetworkId) != p.network {
return errResp(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, p.network)
}
if int(status.ProtocolVersion) != p.version {
return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, p.version)
}
// Configure the remote peer, and sanity check out handshake too
p.td, p.head = status.TD, status.CurrentBlock
return <-errc
}
// String implements fmt.Stringer.
func (p *peer) String() string {
return fmt.Sprintf("Peer %s [%s]", p.id,
fmt.Sprintf("les/%d", p.version),
)
}
// peerSet represents the collection of active peers currently participating in
// the Light Ethereum sub-protocol.
type peerSet struct {
peers map[string]*peer
lock sync.RWMutex
}
// newPeerSet creates a new peer set to track the active participants.
func newPeerSet() *peerSet {
return &peerSet{
peers: make(map[string]*peer),
}
}
// Register injects a new peer into the working set, or returns an error if the
// peer is already known.
func (ps *peerSet) Register(p *peer) error {
ps.lock.Lock()
defer ps.lock.Unlock()
if _, ok := ps.peers[p.id]; ok {
return errAlreadyRegistered
}
ps.peers[p.id] = p
return nil
}
// Unregister removes a remote peer from the active set, disabling any further
// actions to/from that particular entity.
func (ps *peerSet) Unregister(id string) error {
ps.lock.Lock()
defer ps.lock.Unlock()
if _, ok := ps.peers[id]; !ok {
return errNotRegistered
}
delete(ps.peers, id)
return nil
}
// Peer retrieves the registered peer with the given id.
func (ps *peerSet) Peer(id string) *peer {
ps.lock.RLock()
defer ps.lock.RUnlock()
return ps.peers[id]
}
// Len returns if the current number of peers in the set.
func (ps *peerSet) Len() int {
ps.lock.RLock()
defer ps.lock.RUnlock()
return len(ps.peers)
}
// BestPeer retrieves the known peer with the currently highest total difficulty.
func (ps *peerSet) BestPeer() *peer {
ps.lock.RLock()
defer ps.lock.RUnlock()
var (
bestPeer *peer
bestTd *big.Int
)
for _, p := range ps.peers {
if td := p.Td(); bestPeer == nil || td.Cmp(bestTd) > 0 {
bestPeer, bestTd = p, td
}
}
return bestPeer
}

197
les/protocol.go Normal file
View file

@ -0,0 +1,197 @@
// 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 les implements the Light Ethereum Subprotocol.
package les
import (
"fmt"
"io"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
// Mode represents the mode of operation of the light client.
type Mode int
const (
ArchiveMode Mode = iota // Maintain the entire blockchain history
FullMode // Maintain only a recent view of the blockchain
LightMode // Don't maintain any history, rather fetch on demand
)
// Constants to match up protocol versions and messages
const (
lpv1 = 1
)
// minimumProtocolVersion is the minimum version of the protocol les must run to
// support the desired mode of operation.
var minimumProtocolVersion = map[Mode]uint{
ArchiveMode: lpv1,
FullMode: lpv1,
LightMode: lpv1,
}
// Supported versions of the les protocol (first is primary).
var ProtocolVersions = []uint{lpv1}
// Number of implemented message corresponding to different protocol versions.
var ProtocolLengths = []uint64{19}
const (
NetworkId = 1
ProtocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message
)
// les protocol message codes
const (
// Protocol messages belonging to LPV1
StatusMsg = 0x00
NewBlockHashesMsg = 0x01
GetBlockHeadersMsg = 0x02
BlockHeadersMsg = 0x03
GetBlockBodiesMsg = 0x04
BlockBodiesMsg = 0x05
GetNodeDataMsg = 0x06
NodeDataMsg = 0x07
GetReceiptsMsg = 0x08
ReceiptsMsg = 0x09
GetProofsMsg = 0x0a
ProofsMsg = 0x0b
)
type errCode int
const (
ErrMsgTooLarge = iota
ErrDecode
ErrInvalidMsgCode
ErrProtocolVersionMismatch
ErrNetworkIdMismatch
ErrGenesisBlockMismatch
ErrNoStatusMsg
ErrExtraStatusMsg
ErrSuspendedPeer
)
func (e errCode) String() string {
return errorToString[int(e)]
}
// XXX change once legacy code is out
var errorToString = map[int]string{
ErrMsgTooLarge: "Message too long",
ErrDecode: "Invalid message",
ErrInvalidMsgCode: "Invalid message code",
ErrProtocolVersionMismatch: "Protocol version mismatch",
ErrNetworkIdMismatch: "NetworkId mismatch",
ErrGenesisBlockMismatch: "Genesis block mismatch",
ErrNoStatusMsg: "No status message",
ErrExtraStatusMsg: "Extra status message",
ErrSuspendedPeer: "Suspended peer",
}
type chainManager interface {
GetBlockHashesFromHash(hash common.Hash, amount uint64) (hashes []common.Hash)
GetBlock(hash common.Hash) (block *types.Block)
Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash)
}
// statusData is the network packet for the status message.
type statusData struct {
ProtocolVersion uint32
NetworkId uint32
TD *big.Int
CurrentBlock common.Hash
GenesisBlock common.Hash
}
// newBlockHashesData is the network packet for the block announcements.
type newBlockHashesData []struct {
Hash common.Hash // Hash of one particular block being announced
Number uint64 // Number of one particular block being announced
}
// getBlockHashesData is the network packet for the hash based hash retrieval.
type getBlockHashesData struct {
Hash common.Hash
Amount uint64
}
// getBlockHeadersData represents a block header query.
type getBlockHeadersData struct {
Origin hashOrNumber // Block from which to retrieve headers
Amount uint64 // Maximum number of headers to retrieve
Skip uint64 // Blocks to skip between consecutive headers
Reverse bool // Query direction (false = rising towards latest, true = falling towards genesis)
}
// hashOrNumber is a combined field for specifying an origin block.
type hashOrNumber struct {
Hash common.Hash // Block hash from which to retrieve headers (excludes Number)
Number uint64 // Block hash from which to retrieve headers (excludes Hash)
}
// EncodeRLP is a specialized encoder for hashOrNumber to encode only one of the
// two contained union fields.
func (hn *hashOrNumber) EncodeRLP(w io.Writer) error {
if hn.Hash == (common.Hash{}) {
return rlp.Encode(w, hn.Number)
}
if hn.Number != 0 {
return fmt.Errorf("both origin hash (%x) and number (%d) provided", hn.Hash, hn.Number)
}
return rlp.Encode(w, hn.Hash)
}
// DecodeRLP is a specialized decoder for hashOrNumber to decode the contents
// into either a block hash or a block number.
func (hn *hashOrNumber) DecodeRLP(s *rlp.Stream) error {
_, size, _ := s.Kind()
origin, err := s.Raw()
if err == nil {
switch {
case size == 32:
err = rlp.DecodeBytes(origin, &hn.Hash)
case size <= 8:
err = rlp.DecodeBytes(origin, &hn.Number)
default:
err = fmt.Errorf("invalid input size %d for origin", size)
}
}
return err
}
// newBlockData is the network packet for the block propagation message.
type newBlockData struct {
Block *types.Block
TD *big.Int
}
// blockBodiesData is the network packet for block content distribution.
type blockBodiesData []*types.Body
// nodeDataData is the network response packet for a node data retrieval.
type nodeDataData []struct {
Value []byte
}
type proofsData []trie.MerkleProof

90
les/request_test.go Normal file
View file

@ -0,0 +1,90 @@
package les
import (
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/les/requests"
)
var testBankSecureTrieKey = secAddr(testBankAddress)
func secAddr(addr common.Address) []byte {
sha := sha3.NewKeccak256()
sha.Write(addr[:])
return sha.Sum(nil)
}
type accessTestFn func(ca *access.ChainAccess, bc *core.BlockChain, bhash common.Hash) access.ObjectAccess
func TestBlockAccessLes1(t *testing.T) { testAccess(t, 1, true, tfBlockAccess) }
func tfBlockAccess(ca *access.ChainAccess, bc *core.BlockChain, bhash common.Hash) access.ObjectAccess {
return requests.NewBlockAccess(ca.Db(), bhash, core.GetHeader)
}
func TestReceiptsAccessLes1(t *testing.T) { testAccess(t, 1, true, tfReceiptsAccess) }
func tfReceiptsAccess(ca *access.ChainAccess, bc *core.BlockChain, bhash common.Hash) access.ObjectAccess {
return requests.NewReceiptsAccess(ca.Db(), bhash, core.GetHeader, core.PutReceipts, core.PutBlockReceipts)
}
func TestTrieEntryAccessLes1(t *testing.T) { testAccess(t, 1, false, tfTrieEntryAccess) }
func tfTrieEntryAccess(ca *access.ChainAccess, bc *core.BlockChain, bhash common.Hash) access.ObjectAccess {
return requests.NewTrieEntryAccess(bc.GetHeader(bhash).Root, ca.Db(), testBankSecureTrieKey)
}
func TestNodeDataAccessLes1(t *testing.T) { testAccess(t, 1, true, tfNodeDataAccess) }
func tfNodeDataAccess(ca *access.ChainAccess, bc *core.BlockChain, bhash common.Hash) access.ObjectAccess {
return requests.NewNodeDataAccess(ca.Db(), bc.GetHeader(bhash).Root)
}
func testAccess(t *testing.T, protocol int, shouldCache bool, fn accessTestFn) {
// Assemble the test environment
pm, ca := newTestProtocolManagerMust(t, false, 4, testChainGen)
lpm, lca := newTestProtocolManagerMust(t, true, 0, nil)
_, _, lpeer, _ := newTestPeerPair("peer", protocol, pm, lpm)
time.Sleep(time.Millisecond * 100)
lpm.synchronise(lpeer)
cid := access.NewChannelID(time.Millisecond * 200)
test := func(expFail uint64) {
for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
bhash := core.GetCanonicalHash(ca.Db(), i)
req := fn(lca, lpm.blockchain, bhash)
err := lca.Retrieve(req, access.NewContext(cid))
got := err == nil
exp := i < expFail
if exp && !got {
t.Errorf("object retrieval failed")
}
if !exp && got {
t.Errorf("unexpected object retrieval success")
}
}
}
// temporarily remove peer to test odr fails
lca.UnregisterPeer(lpeer.id)
// expect retrievals to fail (except genesis block) without a les peer
if shouldCache {
test(1)
} else {
test(0)
}
lca.RegisterPeer(lpeer.id, lpeer.version, lpeer.Head(), lpeer.RequestBodies, lpeer.RequestNodeData, lpeer.RequestReceipts, lpeer.RequestProofs)
// expect all retrievals to pass
test(5)
lca.UnregisterPeer(lpeer.id)
if shouldCache {
// still expect all retrievals to pass, now data should be cached locally
test(5)
}
}

View file

@ -0,0 +1,180 @@
// 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 requests
import (
"bytes"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
)
var (
blockReceiptsPre = []byte("receipts-block-")
blockPrefix = []byte("block-")
bodySuffix = []byte("-body")
)
// ODR request type for block bodies, see access.ObjectAccess interface
type BlockAccess struct {
db ethdb.Database
blockHash common.Hash
rlp []byte
getHeader getHeaderFn
}
type getHeaderFn func(db ethdb.Database, hash common.Hash) *types.Header
func NewBlockAccess(db ethdb.Database, blockHash common.Hash, getHeader getHeaderFn) *BlockAccess {
return &BlockAccess{db: db, blockHash: blockHash, getHeader: getHeader}
}
func (self *BlockAccess) GetRlp() []byte {
return self.rlp
}
func (self *BlockAccess) Request(peer *access.Peer) error {
glog.V(access.LogLevel).Infof("ODR: requesting body of block %08x from peer %v", self.blockHash[:4], peer.Id())
return peer.GetBlockBodies([]common.Hash{self.blockHash})
}
func (self *BlockAccess) Valid(msg *access.Msg) bool {
glog.V(access.LogLevel).Infof("ODR: validating body of block %08x", self.blockHash[:4])
if msg.MsgType != access.MsgBlockBodies {
glog.V(access.LogLevel).Infof("ODR: invalid message type")
return false
}
bodies := msg.Obj.([]*types.Body)
if len(bodies) != 1 {
glog.V(access.LogLevel).Infof("ODR: invalid number of entries: %d", len(bodies))
return false
}
body := bodies[0]
header := self.getHeader(self.db, self.blockHash)
if header == nil {
glog.V(access.LogLevel).Infof("ODR: header not found for block %08x", self.blockHash[:4])
return false
}
txHash := types.DeriveSha(types.Transactions(body.Transactions))
if header.TxHash != txHash {
glog.V(access.LogLevel).Infof("ODR: header.TxHash %08x does not match received txHash %08x", header.TxHash[:4], txHash[:4])
return false
}
uncleHash := types.CalcUncleHash(body.Uncles)
if header.UncleHash != uncleHash {
glog.V(access.LogLevel).Infof("ODR: header.UncleHash %08x does not match received uncleHash %08x", header.UncleHash[:4], uncleHash[:4])
return false
}
data, err := rlp.EncodeToBytes(body)
if err != nil {
glog.V(access.LogLevel).Infof("ODR: body RLP encode error: %v", err)
return false
}
self.rlp = data
glog.V(access.LogLevel).Infof("ODR: validation successful")
return true
}
func (self *BlockAccess) DbGet() bool {
self.rlp, _ = self.db.Get(append(append(blockPrefix, self.blockHash[:]...), bodySuffix...))
glog.V(access.LogLevel).Infof("ODR: get body %08x len = %d", self.blockHash[:4], len(self.rlp))
return len(self.rlp) != 0
}
func (self *BlockAccess) DbPut() {
self.db.Put(append(append(blockPrefix, self.blockHash[:]...), bodySuffix...), self.rlp)
glog.V(access.LogLevel).Infof("ODR: put body %08x len = %d", self.blockHash[:4], len(self.rlp))
}
// ODR request type for block receipts by block hash, see access.ObjectAccess interface
type ReceiptsAccess struct {
db ethdb.Database
blockHash common.Hash
receipts types.Receipts
getHeader getHeaderFn
putReceipts putReceiptsFn
putBlockReceipts putBlockReceiptsFn
}
type putReceiptsFn func(db ethdb.Database, receipts types.Receipts) error
type putBlockReceiptsFn func(db ethdb.Database, hash common.Hash, receipts types.Receipts) error
func NewReceiptsAccess(db ethdb.Database, blockHash common.Hash, getHeader getHeaderFn, putReceipts putReceiptsFn, putBlockReceipts putBlockReceiptsFn) *ReceiptsAccess {
return &ReceiptsAccess{db: db, blockHash: blockHash, getHeader: getHeader, putReceipts: putReceipts, putBlockReceipts: putBlockReceipts}
}
func (self *ReceiptsAccess) GetReceipts() types.Receipts {
return self.receipts
}
func (self *ReceiptsAccess) Request(peer *access.Peer) error {
glog.V(access.LogLevel).Infof("ODR: requesting receipts for block %08x from peer %v", self.blockHash[:4], peer.Id())
return peer.GetReceipts([]common.Hash{self.blockHash})
}
func (self *ReceiptsAccess) Valid(msg *access.Msg) bool {
glog.V(access.LogLevel).Infof("ODR: validating receipts for block %08x", self.blockHash[:4])
if msg.MsgType != access.MsgReceipts {
glog.V(access.LogLevel).Infof("ODR: invalid message type")
return false
}
receipts := msg.Obj.([]types.Receipts)
if len(receipts) != 1 {
glog.V(access.LogLevel).Infof("ODR: invalid number of entries: %d", len(receipts))
return false
}
hash := types.DeriveSha(receipts[0])
header := self.getHeader(self.db, self.blockHash)
if header == nil {
glog.V(access.LogLevel).Infof("ODR: header not found for block %08x", self.blockHash[:4])
return false
}
if !bytes.Equal(header.ReceiptHash[:], hash[:]) {
glog.V(access.LogLevel).Infof("ODR: header receipts hash %08x does not match calculated RLP hash %08x", header.ReceiptHash[:4], hash[:4])
return false
}
self.receipts = receipts[0]
glog.V(access.LogLevel).Infof("ODR: validation successful")
return true
}
func (self *ReceiptsAccess) DbGet() bool {
data, _ := self.db.Get(append(blockReceiptsPre, self.blockHash[:]...))
if len(data) == 0 {
return false
}
rs := []*types.ReceiptForStorage{}
if err := rlp.DecodeBytes(data, &rs); err != nil {
glog.V(logger.Error).Infof("invalid receipt array RLP for hash %x: %v", self.blockHash, err)
return false
}
self.receipts = make(types.Receipts, len(rs))
for i, receipt := range rs {
self.receipts[i] = (*types.Receipt)(receipt)
}
return true
}
func (self *ReceiptsAccess) DbPut() {
self.putBlockReceipts(self.db, self.blockHash, self.receipts)
self.putReceipts(self.db, self.receipts)
}

170
les/requests/trie_access.go Normal file
View file

@ -0,0 +1,170 @@
// 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 state provides a caching layer atop the Ethereum state trie.
package requests
import (
"bytes"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/trie"
)
type TrieAccess struct {
ca *access.ChainAccess
root common.Hash
trieDb trie.Database
}
func NewTrieAccess(ca *access.ChainAccess, root common.Hash, trieDb trie.Database) *TrieAccess {
return &TrieAccess{
ca: ca,
root: root,
trieDb: trieDb,
}
}
func (self *TrieAccess) RetrieveKey(key []byte, ctx *access.OdrContext) bool {
//fmt.Println("request trie %v key %v", self.root, key)
r := NewTrieEntryAccess(self.root, self.trieDb, key)
return self.ca.Retrieve(r, ctx) == nil
}
func (self *TrieAccess) OdrEnabled() bool {
return self.ca.OdrEnabled()
}
// ODR request type for state/storage trie entries, see access.ObjectAccess interface
type TrieEntryAccess struct {
root common.Hash
trieDb trie.Database
key, value []byte
proof trie.MerkleProof
skipLevels int // set by DbGet() if unsuccessful
}
func NewTrieEntryAccess(root common.Hash, trieDb trie.Database, key []byte) *TrieEntryAccess {
return &TrieEntryAccess{root: root, trieDb: trieDb, key: key}
}
func (self *TrieEntryAccess) Request(peer *access.Peer) error {
glog.V(access.LogLevel).Infof("ODR: requesting trie root %08x key %08x from peer %v", self.root[:4], self.key[:4], peer.Id())
req := &access.ProofReq{
Root: self.root,
Key: self.key,
}
return peer.GetProofs([]*access.ProofReq{req})
}
func (self *TrieEntryAccess) Valid(msg *access.Msg) bool {
glog.V(access.LogLevel).Infof("ODR: validating trie root %08x key %08x", self.root[:4], self.key[:4])
if msg.MsgType != access.MsgProofs {
glog.V(access.LogLevel).Infof("ODR: invalid message type")
return false
}
proofs := msg.Obj.([]trie.MerkleProof)
if len(proofs) != 1 {
glog.V(access.LogLevel).Infof("ODR: invalid number of entries: %d", len(proofs))
return false
}
value, err := trie.VerifyProof(self.root, self.key, proofs[0])
if err != nil {
glog.V(access.LogLevel).Infof("ODR: merkle proof verification error: %v", err)
return false
}
self.proof = proofs[0]
self.value = value
glog.V(access.LogLevel).Infof("ODR: validation successful")
return true
}
func (self *TrieEntryAccess) DbGet() bool {
return false // not used
}
func (self *TrieEntryAccess) DbPut() {
trie.StoreProof(self.trieDb, self.proof)
}
// ODR request type for node data (used for retrieving contract code), see access.ObjectAccess interface
type NodeDataAccess struct {
db ethdb.Database
hash common.Hash
data []byte
}
func NewNodeDataAccess(db ethdb.Database, hash common.Hash) *NodeDataAccess {
return &NodeDataAccess{db: db, hash: hash}
}
func (self *NodeDataAccess) Request(peer *access.Peer) error {
glog.V(access.LogLevel).Infof("ODR: requesting node data for hash %08x from peer %v", self.hash[:4], peer.Id())
return peer.GetNodeData([]common.Hash{self.hash})
}
func (self *NodeDataAccess) Valid(msg *access.Msg) bool {
glog.V(access.LogLevel).Infof("ODR: validating node data for hash %08x", self.hash[:4])
if msg.MsgType != access.MsgNodeData {
glog.V(access.LogLevel).Infof("ODR: invalid message type")
return false
}
reply := msg.Obj.([][]byte)
if len(reply) != 1 {
glog.V(access.LogLevel).Infof("ODR: invalid number of entries: %d", len(reply))
return false
}
data := reply[0]
hash := crypto.Sha3Hash(data)
if bytes.Compare(self.hash[:], hash[:]) != 0 {
glog.V(access.LogLevel).Infof("ODR: requested hash %08x does not match received data hash %08x", self.hash[:4], hash[:4])
return false
}
self.data = data
glog.V(access.LogLevel).Infof("ODR: validation successful")
return true
}
func (self *NodeDataAccess) DbGet() bool {
data, _ := self.db.Get(self.hash[:])
if len(data) == 0 {
return false
}
self.data = data
return true
}
func (self *NodeDataAccess) DbPut() {
self.db.Put(self.hash[:], self.data)
}
var sha3_nil = sha3.NewKeccak256().Sum(nil)
func RetrieveNodeData(ca *access.ChainAccess, hash common.Hash, ctx *access.OdrContext) []byte {
//fmt.Println("request node data %v", hash)
if bytes.Compare(hash[:], sha3_nil) == 0 {
return nil
}
r := NewNodeDataAccess(ca.Db(), hash)
ca.Retrieve(r, ctx)
return r.data
}

72
les/sync.go Normal file
View file

@ -0,0 +1,72 @@
// 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 les
import (
"time"
"github.com/ethereum/go-ethereum/eth/downloader"
)
const (
forceSyncCycle = 10 * time.Second // Time interval to force syncs, even if few peers are available
minDesiredPeerCount = 5 // Amount of peers desired to start syncing
)
// syncer is responsible for periodically synchronising with the network, both
// downloading hashes and blocks as well as handling the announcement handler.
func (pm *ProtocolManager) syncer() {
// Start and ensure cleanup of sync mechanisms
//pm.fetcher.Start()
//defer pm.fetcher.Stop()
defer pm.downloader.Terminate()
// Wait for different events to fire synchronisation operations
forceSync := time.Tick(forceSyncCycle)
for {
select {
case <-pm.newPeerCh:
// Make sure we have peers to select from, then sync
if pm.peers.Len() < minDesiredPeerCount {
break
}
go pm.synchronise(pm.peers.BestPeer())
case <-forceSync:
// Force a sync even if not enough peers are present
go pm.synchronise(pm.peers.BestPeer())
case <-pm.quitSync:
return
}
}
}
// synchronise tries to sync up our local block chain with a remote peer.
func (pm *ProtocolManager) synchronise(peer *peer) {
// Short circuit if no peers are available
if peer == nil {
return
}
// Make sure the peer's TD is higher than our own. If not drop.
td := pm.blockchain.GetTd(pm.blockchain.LastBlockHash())
if peer.Td().Cmp(td) <= 0 {
return
}
// Otherwise try to sync with the downloader
pm.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), downloader.LightSync)
}

View file

@ -58,6 +58,13 @@ func New(eth core.Backend, mux *event.TypeMux, pow pow.PoW) *Miner {
return miner return miner
} }
// dummy miner for light client mode
func NewDummy(eth core.Backend, mux *event.TypeMux, pow pow.PoW) *Miner {
miner := &Miner{eth: eth, mux: mux, pow: pow, worker: newDummyWorker(common.Address{}, eth), canStart: 1}
return miner
}
// update keeps track of the downloader events. Please be aware that this is a one shot type of update loop. // update keeps track of the downloader events. Please be aware that this is a one shot type of update loop.
// It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and // It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and
// the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks // the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
@ -147,6 +148,27 @@ func newWorker(coinbase common.Address, eth core.Backend) *worker {
return worker return worker
} }
// dummy worker for light client mode
func newDummyWorker(coinbase common.Address, eth core.Backend) *worker {
worker := &worker{
eth: eth,
mux: eth.EventMux(),
chainDb: eth.ChainDb(),
recv: make(chan *Result, resultQueueSize),
gasPrice: new(big.Int),
chain: eth.BlockChain(),
proc: eth.BlockProcessor(),
possibleUncles: make(map[common.Hash]*types.Block),
coinbase: coinbase,
txQueue: make(map[common.Hash]*types.Transaction),
quit: make(chan struct{}),
agents: make(map[Agent]struct{}),
fullValidation: false,
}
return worker
}
func (self *worker) setEtherbase(addr common.Address) { func (self *worker) setEtherbase(addr common.Address) {
self.mu.Lock() self.mu.Lock()
defer self.mu.Unlock() defer self.mu.Unlock()
@ -285,7 +307,7 @@ func (self *worker) wait() {
go self.mux.Post(core.NewMinedBlockEvent{block}) go self.mux.Post(core.NewMinedBlockEvent{block})
} else { } else {
work.state.Commit() work.state.Commit()
parent := self.chain.GetBlock(block.ParentHash()) parent := self.chain.GetBlock(block.ParentHash(), access.NoOdr)
if parent == nil { if parent == nil {
glog.V(logger.Error).Infoln("Invalid block found during mining") glog.V(logger.Error).Infoln("Invalid block found during mining")
continue continue
@ -326,7 +348,7 @@ func (self *worker) wait() {
// check staleness and display confirmation // check staleness and display confirmation
var stale, confirm string var stale, confirm string
canonBlock := self.chain.GetBlockByNumber(block.NumberU64()) canonBlock := self.chain.GetBlockByNumber(block.NumberU64(), access.NoOdr)
if canonBlock != nil && canonBlock.Hash() != block.Hash() { if canonBlock != nil && canonBlock.Hash() != block.Hash() {
stale = "stale " stale = "stale "
} else { } else {
@ -359,7 +381,7 @@ func (self *worker) push(work *Work) {
// makeCurrent creates a new environment for the current cycle. // makeCurrent creates a new environment for the current cycle.
func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error { func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error {
state, err := state.New(parent.Root(), self.eth.ChainDb()) state, err := state.New(parent.Root(), self.eth.ChainAccess(), access.NullCtx)
if err != nil { if err != nil {
return err return err
} }
@ -422,7 +444,7 @@ func (self *worker) isBlockLocallyMined(current *Work, deepBlockNum uint64) bool
} }
//Does the block at {deepBlockNum} send earnings to my coinbase? //Does the block at {deepBlockNum} send earnings to my coinbase?
var block = self.chain.GetBlockByNumber(deepBlockNum) var block = self.chain.GetBlockByNumber(deepBlockNum, access.NoOdr)
return block != nil && block.Coinbase() == self.coinbase return block != nil && block.Coinbase() == self.coinbase
} }
@ -636,7 +658,7 @@ func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *b
} }
func (env *Work) commitTransaction(tx *types.Transaction, proc *core.BlockProcessor, gp *core.GasPool) error { func (env *Work) commitTransaction(tx *types.Transaction, proc *core.BlockProcessor, gp *core.GasPool) error {
snap := env.state.Copy() snap := env.state.Copy(access.NullCtx)
receipt, _, err := proc.ApplyTransaction(gp, env.state, env.header, tx, env.header.GasUsed, true) receipt, _, err := proc.ApplyTransaction(gp, env.state, env.header, tx, env.header.GasUsed, true)
if err != nil { if err != nil {
env.state.Set(snap) env.state.Set(snap)

View file

@ -282,7 +282,7 @@ func (self *adminApi) SleepBlocks(req *shared.Request) (interface{}, error) {
timer = time.NewTimer(time.Duration(args.Timeout) * time.Second).C timer = time.NewTimer(time.Duration(args.Timeout) * time.Second).C
} }
height = new(big.Int).Add(self.xeth.CurrentBlock().Number(), big.NewInt(args.N)) height = new(big.Int).Add(self.xeth.CurrentHeader().Number, big.NewInt(args.N))
height, err = sleepBlocks(self.xeth.UpdateState(), height, timer) height, err = sleepBlocks(self.xeth.UpdateState(), height, timer)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -22,6 +22,7 @@ import (
"time" "time"
"github.com/ethereum/ethash" "github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
@ -119,7 +120,7 @@ func (self *debugApi) DumpBlock(req *shared.Request) (interface{}, error) {
return nil, fmt.Errorf("block #%d not found", args.BlockNumber) return nil, fmt.Errorf("block #%d not found", args.BlockNumber)
} }
stateDb, err := state.New(block.Root(), self.ethereum.ChainDb()) stateDb, err := state.New(block.Root(), self.ethereum.ChainAccess(), access.NullCtx)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -19,12 +19,12 @@ package api
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"math/big"
"fmt" "fmt"
"math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/natspec" "github.com/ethereum/go-ethereum/common/natspec"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/shared" "github.com/ethereum/go-ethereum/rpc/shared"
@ -144,7 +144,12 @@ func (self *ethApi) Hashrate(req *shared.Request) (interface{}, error) {
} }
func (self *ethApi) BlockNumber(req *shared.Request) (interface{}, error) { func (self *ethApi) BlockNumber(req *shared.Request) (interface{}, error) {
num := self.xeth.CurrentBlock().Number() var num *big.Int
if self.xeth.CurrentHeader() != nil {
num = self.xeth.CurrentHeader().Number
} else {
num = big.NewInt(0)
}
return newHexNum(num.Bytes()), nil return newHexNum(num.Bytes()), nil
} }
@ -153,8 +158,8 @@ func (self *ethApi) GetBalance(req *shared.Request) (interface{}, error) {
if err := self.codec.Decode(req.Params, &args); err != nil { if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
res := self.xeth.WithCtx(req.GetCtx()).AtStateNum(args.BlockNumber).BalanceAt(args.Address)
return self.xeth.AtStateNum(args.BlockNumber).BalanceAt(args.Address), nil return res, nil
} }
func (self *ethApi) ProtocolVersion(req *shared.Request) (interface{}, error) { func (self *ethApi) ProtocolVersion(req *shared.Request) (interface{}, error) {
@ -170,6 +175,9 @@ func (self *ethApi) IsMining(req *shared.Request) (interface{}, error) {
} }
func (self *ethApi) IsSyncing(req *shared.Request) (interface{}, error) { func (self *ethApi) IsSyncing(req *shared.Request) (interface{}, error) {
if self.ethereum.Downloader() == nil {
return false, nil
}
origin, current, height := self.ethereum.Downloader().Progress() origin, current, height := self.ethereum.Downloader().Progress()
if current < height { if current < height {
return map[string]interface{}{ return map[string]interface{}{
@ -191,7 +199,7 @@ func (self *ethApi) GetStorage(req *shared.Request) (interface{}, error) {
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
return self.xeth.AtStateNum(args.BlockNumber).State().SafeGet(args.Address).Storage(), nil return self.xeth.WithCtx(req.GetCtx()).AtStateNum(args.BlockNumber).State().SafeGet(args.Address).Storage(), nil
} }
func (self *ethApi) GetStorageAt(req *shared.Request) (interface{}, error) { func (self *ethApi) GetStorageAt(req *shared.Request) (interface{}, error) {
@ -200,7 +208,7 @@ func (self *ethApi) GetStorageAt(req *shared.Request) (interface{}, error) {
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
return self.xeth.AtStateNum(args.BlockNumber).StorageAt(args.Address, args.Key), nil return self.xeth.WithCtx(req.GetCtx()).AtStateNum(args.BlockNumber).StorageAt(args.Address, args.Key), nil
} }
func (self *ethApi) GetTransactionCount(req *shared.Request) (interface{}, error) { func (self *ethApi) GetTransactionCount(req *shared.Request) (interface{}, error) {
@ -209,7 +217,7 @@ func (self *ethApi) GetTransactionCount(req *shared.Request) (interface{}, error
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
count := self.xeth.AtStateNum(args.BlockNumber).TxCountAt(args.Address) count := self.xeth.WithCtx(req.GetCtx()).AtStateNum(args.BlockNumber).TxCountAt(args.Address)
return fmt.Sprintf("%#x", count), nil return fmt.Sprintf("%#x", count), nil
} }
@ -218,7 +226,7 @@ func (self *ethApi) GetBlockTransactionCountByHash(req *shared.Request) (interfa
if err := self.codec.Decode(req.Params, &args); err != nil { if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
block := self.xeth.EthBlockByHash(args.Hash) block := self.xeth.WithCtx(req.GetCtx()).EthBlockByHash(args.Hash)
if block == nil { if block == nil {
return nil, nil return nil, nil
} }
@ -231,7 +239,7 @@ func (self *ethApi) GetBlockTransactionCountByNumber(req *shared.Request) (inter
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
block := self.xeth.EthBlockByNumber(args.BlockNumber) block := self.xeth.WithCtx(req.GetCtx()).EthBlockByNumber(args.BlockNumber)
if block == nil { if block == nil {
return nil, nil return nil, nil
} }
@ -244,7 +252,7 @@ func (self *ethApi) GetUncleCountByBlockHash(req *shared.Request) (interface{},
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
block := self.xeth.EthBlockByHash(args.Hash) block := self.xeth.WithCtx(req.GetCtx()).EthBlockByHash(args.Hash)
if block == nil { if block == nil {
return nil, nil return nil, nil
} }
@ -257,7 +265,7 @@ func (self *ethApi) GetUncleCountByBlockNumber(req *shared.Request) (interface{}
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
block := self.xeth.EthBlockByNumber(args.BlockNumber) block := self.xeth.WithCtx(req.GetCtx()).EthBlockByNumber(args.BlockNumber)
if block == nil { if block == nil {
return nil, nil return nil, nil
} }
@ -269,7 +277,7 @@ func (self *ethApi) GetData(req *shared.Request) (interface{}, error) {
if err := self.codec.Decode(req.Params, &args); err != nil { if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
v := self.xeth.AtStateNum(args.BlockNumber).CodeAtBytes(args.Address) v := self.xeth.WithCtx(req.GetCtx()).AtStateNum(args.BlockNumber).CodeAtBytes(args.Address)
return newHexData(v), nil return newHexData(v), nil
} }
@ -337,7 +345,7 @@ func (self *ethApi) GetNatSpec(req *shared.Request) (interface{}, error) {
} }
func (self *ethApi) EstimateGas(req *shared.Request) (interface{}, error) { func (self *ethApi) EstimateGas(req *shared.Request) (interface{}, error) {
_, gas, err := self.doCall(req.Params) _, gas, err := self.doCall(req.Params, req.GetCtx())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -351,7 +359,7 @@ func (self *ethApi) EstimateGas(req *shared.Request) (interface{}, error) {
} }
func (self *ethApi) Call(req *shared.Request) (interface{}, error) { func (self *ethApi) Call(req *shared.Request) (interface{}, error) {
v, _, err := self.doCall(req.Params) v, _, err := self.doCall(req.Params, req.GetCtx())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -368,13 +376,13 @@ func (self *ethApi) Flush(req *shared.Request) (interface{}, error) {
return nil, shared.NewNotImplementedError(req.Method) return nil, shared.NewNotImplementedError(req.Method)
} }
func (self *ethApi) doCall(params json.RawMessage) (string, string, error) { func (self *ethApi) doCall(params json.RawMessage, ctx *access.OdrContext) (string, string, error) {
args := new(CallArgs) args := new(CallArgs)
if err := self.codec.Decode(params, &args); err != nil { if err := self.codec.Decode(params, &args); err != nil {
return "", "", err return "", "", err
} }
return self.xeth.AtStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data) return self.xeth.WithCtx(ctx).AtStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data)
} }
func (self *ethApi) GetBlockByHash(req *shared.Request) (interface{}, error) { func (self *ethApi) GetBlockByHash(req *shared.Request) (interface{}, error) {
@ -382,7 +390,7 @@ func (self *ethApi) GetBlockByHash(req *shared.Request) (interface{}, error) {
if err := self.codec.Decode(req.Params, &args); err != nil { if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
block := self.xeth.EthBlockByHash(args.BlockHash) block := self.xeth.WithCtx(req.GetCtx()).EthBlockByHash(args.BlockHash)
if block == nil { if block == nil {
return nil, nil return nil, nil
} }
@ -395,7 +403,7 @@ func (self *ethApi) GetBlockByNumber(req *shared.Request) (interface{}, error) {
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
block := self.xeth.EthBlockByNumber(args.BlockNumber) block := self.xeth.WithCtx(req.GetCtx()).EthBlockByNumber(args.BlockNumber)
if block == nil { if block == nil {
return nil, nil return nil, nil
} }
@ -408,7 +416,7 @@ func (self *ethApi) GetTransactionByHash(req *shared.Request) (interface{}, erro
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
tx, bhash, bnum, txi := self.xeth.EthTransactionByHash(args.Hash) tx, bhash, bnum, txi := self.xeth.EthTransactionByHash(args.Hash) //ODR
if tx != nil { if tx != nil {
v := NewTransactionRes(tx) v := NewTransactionRes(tx)
// if the blockhash is 0, assume this is a pending transaction // if the blockhash is 0, assume this is a pending transaction
@ -428,7 +436,7 @@ func (self *ethApi) GetTransactionByBlockHashAndIndex(req *shared.Request) (inte
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
raw := self.xeth.EthBlockByHash(args.Hash) raw := self.xeth.WithCtx(req.GetCtx()).EthBlockByHash(args.Hash)
if raw == nil { if raw == nil {
return nil, nil return nil, nil
} }
@ -446,7 +454,7 @@ func (self *ethApi) GetTransactionByBlockNumberAndIndex(req *shared.Request) (in
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
raw := self.xeth.EthBlockByNumber(args.BlockNumber) raw := self.xeth.WithCtx(req.GetCtx()).EthBlockByNumber(args.BlockNumber)
if raw == nil { if raw == nil {
return nil, nil return nil, nil
} }
@ -464,7 +472,7 @@ func (self *ethApi) GetUncleByBlockHashAndIndex(req *shared.Request) (interface{
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
raw := self.xeth.EthBlockByHash(args.Hash) raw := self.xeth.WithCtx(req.GetCtx()).EthBlockByHash(args.Hash)
if raw == nil { if raw == nil {
return nil, nil return nil, nil
} }
@ -482,7 +490,7 @@ func (self *ethApi) GetUncleByBlockNumberAndIndex(req *shared.Request) (interfac
return nil, shared.NewDecodeParamError(err.Error()) return nil, shared.NewDecodeParamError(err.Error())
} }
raw := self.xeth.EthBlockByNumber(args.BlockNumber) raw := self.xeth.WithCtx(req.GetCtx()).EthBlockByNumber(args.BlockNumber)
if raw == nil { if raw == nil {
return nil, nil return nil, nil
} }
@ -662,7 +670,7 @@ func (self *ethApi) GetTransactionReceipt(req *shared.Request) (interface{}, err
txhash := common.BytesToHash(common.FromHex(args.Hash)) txhash := common.BytesToHash(common.FromHex(args.Hash))
tx, bhash, bnum, txi := self.xeth.EthTransactionByHash(args.Hash) tx, bhash, bnum, txi := self.xeth.EthTransactionByHash(args.Hash)
rec := self.xeth.GetTxReceipt(txhash) rec := self.xeth.GetTxReceipt(txhash) //ODR
// We could have an error of "not found". Should disambiguate // We could have an error of "not found". Should disambiguate
// if err != nil { // if err != nil {
// return err, nil // return err, nil

View file

@ -19,12 +19,12 @@ package comms
import ( import (
"io" "io"
"net" "net"
"fmt" "fmt"
"strings" "strings"
"strconv" "strconv"
"time"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/codec"
@ -69,6 +69,8 @@ func handle(id int, conn net.Conn, api shared.EthereumApi, c codec.Codec) {
codec.Close() codec.Close()
}() }()
channelID := access.NewChannelID(time.Second)
for { for {
requests, isBatch, err := codec.ReadRequest() requests, isBatch, err := codec.ReadRequest()
if err == io.EOF { if err == io.EOF {
@ -78,11 +80,22 @@ func handle(id int, conn net.Conn, api shared.EthereumApi, c codec.Codec) {
return return
} }
ctx := access.NewContext(channelID)
if isBatch { if isBatch {
responses := make([]*interface{}, len(requests)) responses := make([]*interface{}, len(requests))
responseCount := 0 responseCount := 0
var res interface{}
var err error
for _, req := range requests { for _, req := range requests {
res, err := api.Execute(req) if !ctx.IsCancelled() {
req.SetCtx(ctx)
res, err = api.Execute(req)
}
if ctx.IsCancelled() {
res = nil
err = access.ErrCancel
}
if req.Id != nil { if req.Id != nil {
rpcResponse := shared.NewRpcResponse(req.Id, req.Jsonrpc, res, err) rpcResponse := shared.NewRpcResponse(req.Id, req.Jsonrpc, res, err)
responses[responseCount] = rpcResponse responses[responseCount] = rpcResponse
@ -97,7 +110,12 @@ func handle(id int, conn net.Conn, api shared.EthereumApi, c codec.Codec) {
} }
} else { } else {
var rpcResponse interface{} var rpcResponse interface{}
requests[0].SetCtx(ctx)
res, err := api.Execute(requests[0]) res, err := api.Execute(requests[0])
if ctx.IsCancelled() {
res = nil
err = access.ErrCancel
}
rpcResponse = shared.NewRpcResponse(requests[0].Id, requests[0].Jsonrpc, res, err) rpcResponse = shared.NewRpcResponse(requests[0].Id, requests[0].Jsonrpc, res, err)
err = codec.WriteResponse(rpcResponse) err = codec.WriteResponse(rpcResponse)

View file

@ -29,6 +29,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/codec"
@ -67,6 +68,7 @@ type stopServer struct {
type handler struct { type handler struct {
codec codec.Codec codec codec.Codec
api shared.EthereumApi api shared.EthereumApi
channelID *access.OdrChannelID
} }
// StartHTTP starts listening for RPC requests sent via HTTP. // StartHTTP starts listening for RPC requests sent via HTTP.
@ -82,7 +84,8 @@ func StartHttp(cfg HttpConfig, codec codec.Codec, api shared.EthereumApi) error
return nil // RPC service already running on given host/port return nil // RPC service already running on given host/port
} }
// Set up the request handler, wrapping it with CORS headers if configured. // Set up the request handler, wrapping it with CORS headers if configured.
handler := http.Handler(&handler{codec, api}) channelID := access.NewChannelID(time.Second)
handler := http.Handler(&handler{codec, api, channelID})
if len(cfg.CorsDomain) > 0 { if len(cfg.CorsDomain) > 0 {
opts := cors.Options{ opts := cors.Options{
AllowedMethods: []string{"POST"}, AllowedMethods: []string{"POST"},
@ -121,9 +124,15 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
} }
c := h.codec.New(nil) c := h.codec.New(nil)
ctx := access.NewContext(h.channelID)
var rpcReq shared.Request var rpcReq shared.Request
if err = c.Decode(payload, &rpcReq); err == nil { if err = c.Decode(payload, &rpcReq); err == nil {
rpcReq.SetCtx(ctx)
reply, err := h.api.Execute(&rpcReq) reply, err := h.api.Execute(&rpcReq)
if ctx.IsCancelled() {
reply = nil
err = access.ErrCancel
}
res := shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err) res := shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
sendJSON(w, &res) sendJSON(w, &res)
return return
@ -133,8 +142,17 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if err = c.Decode(payload, &reqBatch); err == nil { if err = c.Decode(payload, &reqBatch); err == nil {
resBatch := make([]*interface{}, len(reqBatch)) resBatch := make([]*interface{}, len(reqBatch))
resCount := 0 resCount := 0
var reply interface{}
var err error
for i, rpcReq := range reqBatch { for i, rpcReq := range reqBatch {
reply, err := h.api.Execute(&rpcReq) if !ctx.IsCancelled() {
rpcReq.SetCtx(ctx)
reply, err = h.api.Execute(&rpcReq)
}
if ctx.IsCancelled() {
reply = nil
err = access.ErrCancel
}
if rpcReq.Id != nil { // this leaves nil entries in the response batch for later removal if rpcReq.Id != nil { // this leaves nil entries in the response batch for later removal
resBatch[i] = shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err) resBatch[i] = shared.NewRpcResponse(rpcReq.Id, rpcReq.Jsonrpc, reply, err)
resCount += 1 resCount += 1

View file

@ -18,7 +18,9 @@ package comms
import ( import (
"fmt" "fmt"
"time"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/shared" "github.com/ethereum/go-ethereum/rpc/shared"
) )
@ -30,12 +32,14 @@ type InProcClient struct {
lastJsonrpc string lastJsonrpc string
lastErr error lastErr error
lastRes interface{} lastRes interface{}
channelID *access.OdrChannelID
} }
// Create a new in process client // Create a new in process client
func NewInProcClient(codec codec.Codec) *InProcClient { func NewInProcClient(codec codec.Codec) *InProcClient {
return &InProcClient{ return &InProcClient{
codec: codec, codec: codec,
channelID: access.NewChannelID(time.Second),
} }
} }
@ -52,7 +56,13 @@ func (self *InProcClient) Send(req interface{}) error {
if r, ok := req.(*shared.Request); ok { if r, ok := req.(*shared.Request); ok {
self.lastId = r.Id self.lastId = r.Id
self.lastJsonrpc = r.Jsonrpc self.lastJsonrpc = r.Jsonrpc
ctx := access.NewContext(self.channelID)
r.SetCtx(ctx)
self.lastRes, self.lastErr = self.api.Execute(r) self.lastRes, self.lastErr = self.api.Execute(r)
if ctx.IsCancelled() {
self.lastRes = nil
self.lastErr = access.ErrCancel
}
return self.lastErr return self.lastErr
} }

View file

@ -19,6 +19,7 @@ package shared
import ( import (
"encoding/json" "encoding/json"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
) )
@ -39,11 +40,14 @@ type EthereumApi interface {
} }
// RPC request // RPC request
// (ODR context is propagated inside the request struct because every
// request has its own context. It does not influence JSON encoding.)
type Request struct { type Request struct {
Id interface{} `json:"id"` Id interface{} `json:"id"`
Jsonrpc string `json:"jsonrpc"` Jsonrpc string `json:"jsonrpc"`
Method string `json:"method"` Method string `json:"method"`
Params json.RawMessage `json:"params"` Params json.RawMessage `json:"params"`
ctx *access.OdrContext
} }
// RPC response // RPC response
@ -73,6 +77,14 @@ type ErrorObject struct {
// Data interface{} `json:"data"` // Data interface{} `json:"data"`
} }
func (req *Request) GetCtx() *access.OdrContext {
return req.ctx
}
func (req *Request) SetCtx(ctx *access.OdrContext) {
req.ctx = ctx
}
// Create RPC error response, this allows for custom error codes // Create RPC error response, this allows for custom error codes
func NewRpcErrorResponse(id interface{}, jsonrpcver string, errCode int, err error) *ErrorResponse { func NewRpcErrorResponse(id interface{}, jsonrpcver string, errCode int, err error) *ErrorResponse {
jsonerr := &ErrorObject{errCode, err.Error()} jsonerr := &ErrorObject{errCode, err.Error()}

View file

@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -203,7 +204,7 @@ func runBlockTest(test *BlockTest) error {
return fmt.Errorf("lastblockhash validation mismatch: want: %x, have: %x", lastblockhash, cmlast) return fmt.Errorf("lastblockhash validation mismatch: want: %x, have: %x", lastblockhash, cmlast)
} }
newDB, err := cm.State() newDB, err := cm.State(access.NullCtx)
if err != nil { if err != nil {
return err return err
} }
@ -217,7 +218,7 @@ func runBlockTest(test *BlockTest) error {
// InsertPreState populates the given database with the genesis // InsertPreState populates the given database with the genesis
// accounts defined by the test. // accounts defined by the test.
func (t *BlockTest) InsertPreState(db ethdb.Database, am *accounts.Manager) (*state.StateDB, error) { func (t *BlockTest) InsertPreState(db ethdb.Database, am *accounts.Manager) (*state.StateDB, error) {
statedb, err := state.New(common.Hash{}, db) statedb, err := state.New(common.Hash{}, access.NewDbChainAccess(db), access.NullCtx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -441,7 +442,7 @@ func (test *BlockTest) ValidateImportedHeaders(cm *core.BlockChain, validBlocks
// block-by-block, so we can only validate imported headers after // block-by-block, so we can only validate imported headers after
// all blocks have been processed by ChainManager, as they may not // all blocks have been processed by ChainManager, as they may not
// be part of the longest chain until last block is imported. // be part of the longest chain until last block is imported.
for b := cm.CurrentBlock(); b != nil && b.NumberU64() != 0; b = cm.GetBlock(b.Header().ParentHash) { for b := cm.CurrentBlock(); b != nil && b.NumberU64() != 0; b = cm.GetBlock(b.Header().ParentHash, access.NoOdr) {
bHash := common.Bytes2Hex(b.Hash().Bytes()) // hex without 0x prefix bHash := common.Bytes2Hex(b.Hash().Bytes()) // hex without 0x prefix
if err := validateHeader(bmap[bHash].BlockHeader, b.Header()); err != nil { if err := validateHeader(bmap[bHash].BlockHeader, b.Header()); err != nil {
return fmt.Errorf("Imported block header validation failed: %v", err) return fmt.Errorf("Imported block header validation failed: %v", err)

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -103,7 +104,7 @@ func BenchStateTest(p string, conf bconf, b *testing.B) error {
func benchStateTest(test VmTest, env map[string]string, b *testing.B) { func benchStateTest(test VmTest, env map[string]string, b *testing.B) {
b.StopTimer() b.StopTimer()
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db), access.NullCtx)
for addr, account := range test.Pre { for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account) obj := StateObjectFromAccount(db, addr, account)
statedb.SetStateObject(obj) statedb.SetStateObject(obj)
@ -142,7 +143,7 @@ func runStateTests(tests map[string]VmTest, skipTests []string) error {
func runStateTest(test VmTest) error { func runStateTest(test VmTest) error {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db), access.NullCtx)
for addr, account := range test.Pre { for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account) obj := StateObjectFromAccount(db, addr, account)
statedb.SetStateObject(obj) statedb.SetStateObject(obj)
@ -192,7 +193,7 @@ func runStateTest(test VmTest) error {
} }
for addr, value := range account.Storage { for addr, value := range account.Storage {
v := obj.GetState(common.HexToHash(addr)) v := obj.GetState(common.HexToHash(addr), access.NullCtx)
vexp := common.HexToHash(value) vexp := common.HexToHash(value)
if v != vexp { if v != vexp {
@ -233,7 +234,7 @@ func RunState(statedb *state.StateDB, env, tx map[string]string) ([]byte, vm.Log
// Set pre compiled contracts // Set pre compiled contracts
vm.Precompiled = vm.PrecompiledContracts() vm.Precompiled = vm.PrecompiledContracts()
snapshot := statedb.Copy() snapshot := statedb.Copy(access.NullCtx)
gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"])) gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"]))
key, _ := hex.DecodeString(tx["secretKey"]) key, _ := hex.DecodeString(tx["secretKey"])

View file

@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
@ -89,7 +90,7 @@ func (self Log) Topics() [][]byte {
} }
func StateObjectFromAccount(db ethdb.Database, addr string, account Account) *state.StateObject { func StateObjectFromAccount(db ethdb.Database, addr string, account Account) *state.StateObject {
obj := state.NewStateObject(common.HexToAddress(addr), db) obj := state.NewStateObject(common.HexToAddress(addr), access.NewDbChainAccess(db))
obj.SetBalance(common.Big(account.Balance)) obj.SetBalance(common.Big(account.Balance))
if common.IsHex(account.Code) { if common.IsHex(account.Code) {
@ -201,7 +202,7 @@ func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
return self.state.GetBalance(from).Cmp(balance) >= 0 return self.state.GetBalance(from).Cmp(balance) >= 0
} }
func (self *Env) MakeSnapshot() vm.Database { func (self *Env) MakeSnapshot() vm.Database {
return self.state.Copy() return self.state.Copy(access.NullCtx)
} }
func (self *Env) SetSnapshot(copy vm.Database) { func (self *Env) SetSnapshot(copy vm.Database) {
self.state.Set(copy.(*state.StateDB)) self.state.Set(copy.(*state.StateDB))

View file

@ -25,6 +25,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -108,7 +109,7 @@ func BenchVmTest(p string, conf bconf, b *testing.B) error {
func benchVmTest(test VmTest, env map[string]string, b *testing.B) { func benchVmTest(test VmTest, env map[string]string, b *testing.B) {
b.StopTimer() b.StopTimer()
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db), access.NullCtx)
for addr, account := range test.Pre { for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account) obj := StateObjectFromAccount(db, addr, account)
statedb.SetStateObject(obj) statedb.SetStateObject(obj)
@ -159,7 +160,7 @@ func runVmTests(tests map[string]VmTest, skipTests []string) error {
func runVmTest(test VmTest) error { func runVmTest(test VmTest) error {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, access.NewDbChainAccess(db), access.NullCtx)
for addr, account := range test.Pre { for addr, account := range test.Pre {
obj := StateObjectFromAccount(db, addr, account) obj := StateObjectFromAccount(db, addr, account)
statedb.SetStateObject(obj) statedb.SetStateObject(obj)
@ -214,7 +215,7 @@ func runVmTest(test VmTest) error {
} }
for addr, value := range account.Storage { for addr, value := range account.Storage {
v := obj.GetState(common.HexToHash(addr)) v := obj.GetState(common.HexToHash(addr), access.NoOdr)
vexp := common.HexToHash(value) vexp := common.HexToHash(value)
if v != vexp { if v != vexp {

View file

@ -62,6 +62,17 @@ func newARC(c int) *arc {
} }
} }
func (a *arc) Clear() {
a.mutex.Lock()
defer a.mutex.Unlock()
a.p = 0
a.t1 = list.New()
a.b1 = list.New()
a.t2 = list.New()
a.b2 = list.New()
a.cache = make(map[string]*entry, a.c)
}
// Put inserts a new key-value pair into the cache. // Put inserts a new key-value pair into the cache.
// This optimizes future access to this entry (side effect). // This optimizes future access to this entry (side effect).
func (a *arc) Put(key hashNode, value node) bool { func (a *arc) Put(key hashNode, value node) bool {

View file

@ -69,6 +69,23 @@ func compactHexDecode(str []byte) []byte {
return nibbles return nibbles
} }
func compactHexEncode(nibbles []byte) []byte {
nl := len(nibbles)
if nibbles[nl-1] == 16 {
nl--
}
l := (nl + 1) / 2
var str = make([]byte, l)
for i, _ := range str {
b := nibbles[i*2] * 16
if nl > i*2 {
b += nibbles[i*2+1]
}
str[i] = b
}
return str
}
func decodeCompact(key []byte) []byte { func decodeCompact(key []byte) []byte {
l := len(key) / 2 l := len(key) / 2
var res = make([]byte, l) var res = make([]byte, l)

View file

@ -16,7 +16,11 @@
package trie package trie
import "bytes" import (
"bytes"
"github.com/ethereum/go-ethereum/les/access"
)
type Iterator struct { type Iterator struct {
trie *Trie trie *Trie
@ -100,7 +104,7 @@ func (self *Iterator) next(node interface{}, key []byte, isIterStart bool) []byt
} }
case hashNode: case hashNode:
return self.next(self.trie.resolveHash(node), key, isIterStart) return self.next(self.trie.resolveHash(node, nil, nil, access.NoOdr), key, isIterStart)
} }
return nil return nil
} }
@ -127,7 +131,7 @@ func (self *Iterator) key(node interface{}) []byte {
} }
} }
case hashNode: case hashNode:
return self.key(self.trie.resolveHash(node)) return self.key(self.trie.resolveHash(node, nil, nil, access.NoOdr))
} }
return nil return nil

View file

@ -16,7 +16,11 @@
package trie package trie
import "testing" import (
"testing"
"github.com/ethereum/go-ethereum/les/access"
)
func TestIterator(t *testing.T) { func TestIterator(t *testing.T) {
trie := newEmpty() trie := newEmpty()
@ -32,7 +36,7 @@ func TestIterator(t *testing.T) {
v := make(map[string]bool) v := make(map[string]bool)
for _, val := range vals { for _, val := range vals {
v[val.k] = false v[val.k] = false
trie.Update([]byte(val.k), []byte(val.v)) trie.Update([]byte(val.k), []byte(val.v), access.NoOdr)
} }
trie.Commit() trie.Commit()

View file

@ -6,10 +6,13 @@ import (
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
type MerkleProof []rlp.RawValue
// Prove constructs a merkle proof for key. The result contains all // Prove constructs a merkle proof for key. The result contains all
// encoded nodes on the path to the value at key. The value itself is // encoded nodes on the path to the value at key. The value itself is
// also included in the last node and can be retrieved by verifying // also included in the last node and can be retrieved by verifying
@ -17,29 +20,28 @@ import (
// //
// The returned proof is nil if the trie does not contain a value for key. // The returned proof is nil if the trie does not contain a value for key.
// For existing keys, the proof will have at least one element. // For existing keys, the proof will have at least one element.
func (t *Trie) Prove(key []byte) []rlp.RawValue { func (t *Trie) Prove(key []byte) MerkleProof {
// Collect all nodes on the path to key. // Collect all nodes on the path to key.
key = compactHexDecode(key) key = compactHexDecode(key)
nodes := []node{} nodes := []node{}
tn := t.root tn := t.root
for len(key) > 0 { for len(key) > 0 && tn != nil {
switch n := tn.(type) { switch n := tn.(type) {
case shortNode: case shortNode:
if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) { if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) {
// The trie doesn't contain the key. // The trie doesn't contain the key.
return nil tn = nil
} } else {
tn = n.Val tn = n.Val
}
key = key[len(n.Key):] key = key[len(n.Key):]
nodes = append(nodes, n) nodes = append(nodes, n)
case fullNode: case fullNode:
tn = n[key[0]] tn = n[key[0]]
key = key[1:] key = key[1:]
nodes = append(nodes, n) nodes = append(nodes, n)
case nil:
return nil
case hashNode: case hashNode:
tn = t.resolveHash(n) tn = t.resolveHash(n, nil, nil, access.NoOdr)
default: default:
panic(fmt.Sprintf("%T: invalid node: %v", tn, tn)) panic(fmt.Sprintf("%T: invalid node: %v", tn, tn))
} }
@ -67,7 +69,7 @@ func (t *Trie) Prove(key []byte) []rlp.RawValue {
// value for key in a trie with the given root hash. VerifyProof // value for key in a trie with the given root hash. VerifyProof
// returns an error if the proof contains invalid trie nodes or the // returns an error if the proof contains invalid trie nodes or the
// wrong value. // wrong value.
func VerifyProof(rootHash common.Hash, key []byte, proof []rlp.RawValue) (value []byte, err error) { func VerifyProof(rootHash common.Hash, key []byte, proof MerkleProof) (value []byte, err error) {
key = compactHexDecode(key) key = compactHexDecode(key)
sha := sha3.NewKeccak256() sha := sha3.NewKeccak256()
wantHash := rootHash.Bytes() wantHash := rootHash.Bytes()
@ -84,7 +86,12 @@ func VerifyProof(rootHash common.Hash, key []byte, proof []rlp.RawValue) (value
keyrest, cld := get(n, key) keyrest, cld := get(n, key)
switch cld := cld.(type) { switch cld := cld.(type) {
case nil: case nil:
if i != len(proof)-1 {
return nil, fmt.Errorf("key mismatch at proof node %d", i) return nil, fmt.Errorf("key mismatch at proof node %d", i)
} else {
// The trie doesn't contain the key.
return nil, nil
}
case hashNode: case hashNode:
key = keyrest key = keyrest
wantHash = cld wantHash = cld
@ -98,6 +105,19 @@ func VerifyProof(rootHash common.Hash, key []byte, proof []rlp.RawValue) (value
return nil, errors.New("unexpected end of proof") return nil, errors.New("unexpected end of proof")
} }
func StoreProof(db Database, proof MerkleProof) {
sha := sha3.NewKeccak256()
for _, buf := range proof {
sha.Reset()
sha.Write(buf)
hash := sha.Sum(nil)
val, _ := db.Get(hash)
if val == nil {
db.Put(hash, buf)
}
}
}
func get(tn node, key []byte) ([]byte, node) { func get(tn node, key []byte) ([]byte, node) {
for len(key) > 0 { for len(key) > 0 {
switch n := tn.(type) { switch n := tn.(type) {

View file

@ -8,6 +8,7 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@ -119,14 +120,14 @@ func randomTrie(n int) (*Trie, map[string]*kv) {
for i := byte(0); i < 100; i++ { for i := byte(0); i < 100; i++ {
value := &kv{common.LeftPadBytes([]byte{i}, 32), []byte{i}, false} value := &kv{common.LeftPadBytes([]byte{i}, 32), []byte{i}, false}
value2 := &kv{common.LeftPadBytes([]byte{i + 10}, 32), []byte{i}, false} value2 := &kv{common.LeftPadBytes([]byte{i + 10}, 32), []byte{i}, false}
trie.Update(value.k, value.v) trie.Update(value.k, value.v, access.NoOdr)
trie.Update(value2.k, value2.v) trie.Update(value2.k, value2.v, access.NoOdr)
vals[string(value.k)] = value vals[string(value.k)] = value
vals[string(value2.k)] = value2 vals[string(value2.k)] = value2
} }
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
value := &kv{randBytes(32), randBytes(20), false} value := &kv{randBytes(32), randBytes(20), false}
trie.Update(value.k, value.v) trie.Update(value.k, value.v, access.NoOdr)
vals[string(value.k)] = value vals[string(value.k)] = value
} }
return trie, vals return trie, vals

View file

@ -20,6 +20,7 @@ import (
"hash" "hash"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/crypto/sha3"
) )
@ -49,11 +50,11 @@ type SecureTrie struct {
// trie is initially empty. Otherwise, New will panics if db is nil // trie is initially empty. Otherwise, New will panics if db is nil
// and returns ErrMissingRoot if the root node cannpt be found. // and returns ErrMissingRoot if the root node cannpt be found.
// Accessing the trie loads nodes from db on demand. // Accessing the trie loads nodes from db on demand.
func NewSecure(root common.Hash, db Database) (*SecureTrie, error) { func NewSecure(root common.Hash, db Database, access OdrAccess) (*SecureTrie, error) {
if db == nil { if db == nil {
panic("NewSecure called with nil database") panic("NewSecure called with nil database")
} }
trie, err := New(root, db) trie, err := New(root, db, access)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -62,8 +63,8 @@ func NewSecure(root common.Hash, db Database) (*SecureTrie, error) {
// Get returns the value for key stored in the trie. // Get returns the value for key stored in the trie.
// The value bytes must not be modified by the caller. // The value bytes must not be modified by the caller.
func (t *SecureTrie) Get(key []byte) []byte { func (t *SecureTrie) Get(key []byte, ctx *access.OdrContext) []byte {
return t.Trie.Get(t.hashKey(key)) return t.Trie.Get(t.hashKey(key), ctx)
} }
// Update associates key with value in the trie. Subsequent calls to // Update associates key with value in the trie. Subsequent calls to
@ -72,15 +73,15 @@ func (t *SecureTrie) Get(key []byte) []byte {
// //
// The value bytes must not be modified by the caller while they are // The value bytes must not be modified by the caller while they are
// stored in the trie. // stored in the trie.
func (t *SecureTrie) Update(key, value []byte) { func (t *SecureTrie) Update(key, value []byte, ctx *access.OdrContext) {
hk := t.hashKey(key) hk := t.hashKey(key)
t.Trie.Update(hk, value) t.Trie.Update(hk, value, ctx)
t.Trie.db.Put(t.secKey(hk), key) t.Trie.db.Put(t.secKey(hk), key)
} }
// Delete removes any existing value for key from the trie. // Delete removes any existing value for key from the trie.
func (t *SecureTrie) Delete(key []byte) { func (t *SecureTrie) Delete(key []byte, ctx *access.OdrContext) {
t.Trie.Delete(t.hashKey(key)) t.Trie.Delete(t.hashKey(key), ctx)
} }
// GetKey returns the sha3 preimage of a hashed key that was // GetKey returns the sha3 preimage of a hashed key that was

View file

@ -21,13 +21,14 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
) )
func newEmptySecure() *SecureTrie { func newEmptySecure() *SecureTrie {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
trie, _ := NewSecure(common.Hash{}, db) trie, _ := NewSecure(common.Hash{}, db, nil)
return trie return trie
} }
@ -45,9 +46,9 @@ func TestSecureDelete(t *testing.T) {
} }
for _, val := range vals { for _, val := range vals {
if val.v != "" { if val.v != "" {
trie.Update([]byte(val.k), []byte(val.v)) trie.Update([]byte(val.k), []byte(val.v), access.NoOdr)
} else { } else {
trie.Delete([]byte(val.k)) trie.Delete([]byte(val.k), access.NoOdr)
} }
} }
hash := trie.Hash() hash := trie.Hash()
@ -59,13 +60,13 @@ func TestSecureDelete(t *testing.T) {
func TestSecureGetKey(t *testing.T) { func TestSecureGetKey(t *testing.T) {
trie := newEmptySecure() trie := newEmptySecure()
trie.Update([]byte("foo"), []byte("bar")) trie.Update([]byte("foo"), []byte("bar"), access.NoOdr)
key := []byte("foo") key := []byte("foo")
value := []byte("bar") value := []byte("bar")
seckey := crypto.Sha3(key) seckey := crypto.Sha3(key)
if !bytes.Equal(trie.Get(key), value) { if !bytes.Equal(trie.Get(key, access.NoOdr), value) {
t.Errorf("Get did not return bar") t.Errorf("Get did not return bar")
} }
if k := trie.GetKey(seckey); !bytes.Equal(k, key) { if k := trie.GetKey(seckey); !bytes.Equal(k, key) {

View file

@ -21,6 +21,7 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
) )
@ -28,18 +29,18 @@ import (
func makeTestTrie() (ethdb.Database, *Trie, map[string][]byte) { func makeTestTrie() (ethdb.Database, *Trie, map[string][]byte) {
// Create an empty trie // Create an empty trie
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
trie, _ := New(common.Hash{}, db) trie, _ := New(common.Hash{}, db, nil)
// Fill it with some arbitrary data // Fill it with some arbitrary data
content := make(map[string][]byte) content := make(map[string][]byte)
for i := byte(0); i < 255; i++ { for i := byte(0); i < 255; i++ {
key, val := common.LeftPadBytes([]byte{1, i}, 32), []byte{i} key, val := common.LeftPadBytes([]byte{1, i}, 32), []byte{i}
content[string(key)] = val content[string(key)] = val
trie.Update(key, val) trie.Update(key, val, access.NoOdr)
key, val = common.LeftPadBytes([]byte{2, i}, 32), []byte{i} key, val = common.LeftPadBytes([]byte{2, i}, 32), []byte{i}
content[string(key)] = val content[string(key)] = val
trie.Update(key, val) trie.Update(key, val, access.NoOdr)
} }
trie.Commit() trie.Commit()
@ -50,12 +51,12 @@ func makeTestTrie() (ethdb.Database, *Trie, map[string][]byte) {
// checkTrieContents cross references a reconstructed trie with an expected data // checkTrieContents cross references a reconstructed trie with an expected data
// content map. // content map.
func checkTrieContents(t *testing.T, db Database, root []byte, content map[string][]byte) { func checkTrieContents(t *testing.T, db Database, root []byte, content map[string][]byte) {
trie, err := New(common.BytesToHash(root), db) trie, err := New(common.BytesToHash(root), db, nil)
if err != nil { if err != nil {
t.Fatalf("failed to create trie at %x: %v", root, err) t.Fatalf("failed to create trie at %x: %v", root, err)
} }
for key, val := range content { for key, val := range content {
if have := trie.Get([]byte(key)); bytes.Compare(have, val) != 0 { if have := trie.Get([]byte(key), access.NoOdr); bytes.Compare(have, val) != 0 {
t.Errorf("entry %x: content mismatch: have %x, want %x", key, have, val) t.Errorf("entry %x: content mismatch: have %x, want %x", key, have, val)
} }
} }
@ -63,8 +64,8 @@ func checkTrieContents(t *testing.T, db Database, root []byte, content map[strin
// Tests that an empty trie is not scheduled for syncing. // Tests that an empty trie is not scheduled for syncing.
func TestEmptyTrieSync(t *testing.T) { func TestEmptyTrieSync(t *testing.T) {
emptyA, _ := New(common.Hash{}, nil) emptyA, _ := New(common.Hash{}, nil, nil)
emptyB, _ := New(emptyRoot, nil) emptyB, _ := New(emptyRoot, nil, nil)
for i, trie := range []*Trie{emptyA, emptyB} { for i, trie := range []*Trie{emptyA, emptyB} {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()

View file

@ -24,6 +24,7 @@ import (
"hash" "hash"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
@ -44,7 +45,18 @@ var (
emptyState = crypto.Sha3Hash(nil) emptyState = crypto.Sha3Hash(nil)
) )
func ClearGlobalCache() {
globalCache.Clear()
}
var ErrMissingRoot = errors.New("missing root node") var ErrMissingRoot = errors.New("missing root node")
var ErrTrieOdrFailure = errors.New("can't commit to db because of ODR failure")
// OdrAccess must be implemented by on-demand network access layer
type OdrAccess interface {
OdrEnabled() bool
RetrieveKey(key []byte, ctx *access.OdrContext) bool
}
// Database must be implemented by backing stores for the trie. // Database must be implemented by backing stores for the trie.
type Database interface { type Database interface {
@ -69,6 +81,7 @@ type DatabaseWriter interface {
type Trie struct { type Trie struct {
root node root node
db Database db Database
access OdrAccess
*hasher *hasher
} }
@ -78,15 +91,17 @@ type Trie struct {
// trie is initially empty and does not require a database. Otherwise, // trie is initially empty and does not require a database. Otherwise,
// New will panics if db is nil or root does not exist in the // New will panics if db is nil or root does not exist in the
// database. Accessing the trie loads nodes from db on demand. // database. Accessing the trie loads nodes from db on demand.
func New(root common.Hash, db Database) (*Trie, error) { func New(root common.Hash, db Database, access OdrAccess) (*Trie, error) {
trie := &Trie{db: db} trie := &Trie{db: db, access: access}
if (root != common.Hash{}) && root != emptyRoot { if (root != common.Hash{}) && root != emptyRoot {
if db == nil { if db == nil {
panic("trie.New: cannot use existing root without a database") panic("trie.New: cannot use existing root without a database")
} }
if access == nil || !access.OdrEnabled() {
if v, _ := trie.db.Get(root[:]); len(v) == 0 { if v, _ := trie.db.Get(root[:]); len(v) == 0 {
return nil, ErrMissingRoot return nil, ErrMissingRoot
} }
}
trie.root = hashNode(root.Bytes()) trie.root = hashNode(root.Bytes())
} }
return trie, nil return trie, nil
@ -99,24 +114,25 @@ func (t *Trie) Iterator() *Iterator {
// Get returns the value for key stored in the trie. // Get returns the value for key stored in the trie.
// The value bytes must not be modified by the caller. // The value bytes must not be modified by the caller.
func (t *Trie) Get(key []byte) []byte { func (t *Trie) Get(key []byte, ctx *access.OdrContext) []byte {
key = compactHexDecode(key) key = compactHexDecode(key)
pos := 0
tn := t.root tn := t.root
for len(key) > 0 { for pos < len(key) {
switch n := tn.(type) { switch n := tn.(type) {
case shortNode: case shortNode:
if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) { if len(key)-pos < len(n.Key) || !bytes.Equal(n.Key, key[pos:pos+len(n.Key)]) {
return nil return nil
} }
tn = n.Val tn = n.Val
key = key[len(n.Key):] pos += len(n.Key)
case fullNode: case fullNode:
tn = n[key[0]] tn = n[key[pos]]
key = key[1:] pos++
case nil: case nil:
return nil return nil
case hashNode: case hashNode:
tn = t.resolveHash(n) tn = t.resolveHash(n, key[:pos], key[pos:], ctx)
default: default:
panic(fmt.Sprintf("%T: invalid node: %v", tn, tn)) panic(fmt.Sprintf("%T: invalid node: %v", tn, tn))
} }
@ -130,16 +146,16 @@ func (t *Trie) Get(key []byte) []byte {
// //
// The value bytes must not be modified by the caller while they are // The value bytes must not be modified by the caller while they are
// stored in the trie. // stored in the trie.
func (t *Trie) Update(key, value []byte) { func (t *Trie) Update(key, value []byte, ctx *access.OdrContext) {
k := compactHexDecode(key) k := compactHexDecode(key)
if len(value) != 0 { if len(value) != 0 {
t.root = t.insert(t.root, k, valueNode(value)) t.root = t.insert(t.root, nil, k, valueNode(value), ctx)
} else { } else {
t.root = t.delete(t.root, k) t.root = t.delete(t.root, nil, k, ctx)
} }
} }
func (t *Trie) insert(n node, key []byte, value node) node { func (t *Trie) insert(n node, prefix, key []byte, value node, ctx *access.OdrContext) node {
if len(key) == 0 { if len(key) == 0 {
return value return value
} }
@ -149,12 +165,12 @@ func (t *Trie) insert(n node, key []byte, value node) node {
// If the whole key matches, keep this short node as is // If the whole key matches, keep this short node as is
// and only update the value. // and only update the value.
if matchlen == len(n.Key) { if matchlen == len(n.Key) {
return shortNode{n.Key, t.insert(n.Val, key[matchlen:], value)} return shortNode{n.Key, t.insert(n.Val, append(prefix, key[:matchlen]...), key[matchlen:], value, ctx)}
} }
// Otherwise branch out at the index where they differ. // Otherwise branch out at the index where they differ.
var branch fullNode var branch fullNode
branch[n.Key[matchlen]] = t.insert(nil, n.Key[matchlen+1:], n.Val) branch[n.Key[matchlen]] = t.insert(nil, append(prefix, n.Key[:matchlen+1]...), n.Key[matchlen+1:], n.Val, ctx)
branch[key[matchlen]] = t.insert(nil, key[matchlen+1:], value) branch[key[matchlen]] = t.insert(nil, append(prefix, key[:matchlen+1]...), key[matchlen+1:], value, ctx)
// Replace this shortNode with the branch if it occurs at index 0. // Replace this shortNode with the branch if it occurs at index 0.
if matchlen == 0 { if matchlen == 0 {
return branch return branch
@ -163,7 +179,7 @@ func (t *Trie) insert(n node, key []byte, value node) node {
return shortNode{key[:matchlen], branch} return shortNode{key[:matchlen], branch}
case fullNode: case fullNode:
n[key[0]] = t.insert(n[key[0]], key[1:], value) n[key[0]] = t.insert(n[key[0]], append(prefix, key[0]), key[1:], value, ctx)
return n return n
case nil: case nil:
@ -176,7 +192,7 @@ func (t *Trie) insert(n node, key []byte, value node) node {
// //
// TODO: track whether insertion changed the value and keep // TODO: track whether insertion changed the value and keep
// n as a hash node if it didn't. // n as a hash node if it didn't.
return t.insert(t.resolveHash(n), key, value) return t.insert(t.resolveHash(n, prefix, key, ctx), prefix, key, value, ctx)
default: default:
panic(fmt.Sprintf("%T: invalid node: %v", n, n)) panic(fmt.Sprintf("%T: invalid node: %v", n, n))
@ -184,15 +200,15 @@ func (t *Trie) insert(n node, key []byte, value node) node {
} }
// Delete removes any existing value for key from the trie. // Delete removes any existing value for key from the trie.
func (t *Trie) Delete(key []byte) { func (t *Trie) Delete(key []byte, ctx *access.OdrContext) {
k := compactHexDecode(key) k := compactHexDecode(key)
t.root = t.delete(t.root, k) t.root = t.delete(t.root, nil, k, ctx)
} }
// delete returns the new root of the trie with key deleted. // delete returns the new root of the trie with key deleted.
// It reduces the trie to minimal form by simplifying // It reduces the trie to minimal form by simplifying
// nodes on the way up after deleting recursively. // nodes on the way up after deleting recursively.
func (t *Trie) delete(n node, key []byte) node { func (t *Trie) delete(n node, prefix, key []byte, ctx *access.OdrContext) node {
switch n := n.(type) { switch n := n.(type) {
case shortNode: case shortNode:
matchlen := prefixLen(key, n.Key) matchlen := prefixLen(key, n.Key)
@ -206,7 +222,7 @@ func (t *Trie) delete(n node, key []byte) node {
// from the subtrie. Child can never be nil here since the // from the subtrie. Child can never be nil here since the
// subtrie must contain at least two other values with keys // subtrie must contain at least two other values with keys
// longer than n.Key. // longer than n.Key.
child := t.delete(n.Val, key[len(n.Key):]) child := t.delete(n.Val, append(prefix, key[:len(n.Key)]...), key[len(n.Key):], ctx)
switch child := child.(type) { switch child := child.(type) {
case shortNode: case shortNode:
// Deleting from the subtrie reduced it to another // Deleting from the subtrie reduced it to another
@ -221,7 +237,7 @@ func (t *Trie) delete(n node, key []byte) node {
} }
case fullNode: case fullNode:
n[key[0]] = t.delete(n[key[0]], key[1:]) n[key[0]] = t.delete(n[key[0]], append(prefix, key[0]), key[1:], ctx)
// Check how many non-nil entries are left after deleting and // Check how many non-nil entries are left after deleting and
// reduce the full node to a short node if only one entry is // reduce the full node to a short node if only one entry is
// left. Since n must've contained at least two children // left. Since n must've contained at least two children
@ -250,7 +266,7 @@ func (t *Trie) delete(n node, key []byte) node {
// shortNode{..., shortNode{...}}. Since the entry // shortNode{..., shortNode{...}}. Since the entry
// might not be loaded yet, resolve it just for this // might not be loaded yet, resolve it just for this
// check. // check.
cnode := t.resolve(n[pos]) cnode := t.resolve(n[pos], prefix, []byte{byte(pos)}, ctx)
if cnode, ok := cnode.(shortNode); ok { if cnode, ok := cnode.(shortNode); ok {
k := append([]byte{byte(pos)}, cnode.Key...) k := append([]byte{byte(pos)}, cnode.Key...)
return shortNode{k, cnode.Val} return shortNode{k, cnode.Val}
@ -273,7 +289,7 @@ func (t *Trie) delete(n node, key []byte) node {
// //
// TODO: track whether deletion actually hit a key and keep // TODO: track whether deletion actually hit a key and keep
// n as a hash node if it didn't. // n as a hash node if it didn't.
return t.delete(t.resolveHash(n), key) return t.delete(t.resolveHash(n, prefix, key, ctx), prefix, key, ctx)
default: default:
panic(fmt.Sprintf("%T: invalid node: %v (%v)", n, n, key)) panic(fmt.Sprintf("%T: invalid node: %v (%v)", n, n, key))
@ -287,19 +303,28 @@ func concat(s1 []byte, s2 ...byte) []byte {
return r return r
} }
func (t *Trie) resolve(n node) node { func (t *Trie) resolve(n node, prefix, suffix []byte, ctx *access.OdrContext) node {
if n, ok := n.(hashNode); ok { if n, ok := n.(hashNode); ok {
return t.resolveHash(n) return t.resolveHash(n, prefix, suffix, ctx)
} }
return n return n
} }
func (t *Trie) resolveHash(n hashNode) node { func (t *Trie) resolveHash(n hashNode, prefix, suffix []byte, ctx *access.OdrContext) node {
if v, ok := globalCache.Get(n); ok { if v, ok := globalCache.Get(n); ok {
return v return v
} }
enc, err := t.db.Get(n) enc, err := t.db.Get(n)
if err != nil || enc == nil { if err != nil || enc == nil {
if prefix != nil || suffix != nil {
key := compactHexEncode(append(prefix, suffix...))
if t.access != nil && t.access.RetrieveKey(key, ctx) {
enc, err = t.db.Get(n)
}
}
}
if err != nil || enc == nil {
// TODO: This needs to be improved to properly distinguish errors. // TODO: This needs to be improved to properly distinguish errors.
// Disk I/O errors shouldn't produce nil (and cause a // Disk I/O errors shouldn't produce nil (and cause a
// consensus failure or weird crash), but it is unclear how // consensus failure or weird crash), but it is unclear how
@ -337,7 +362,7 @@ func (t *Trie) Commit() (root common.Hash, err error) {
if t.db == nil { if t.db == nil {
panic("Commit called on trie with nil database") panic("Commit called on trie with nil database")
} }
return t.CommitTo(t.db) return t.CommitTo(t.db, access.NullCtx)
} }
// CommitTo writes all nodes to the given database. // CommitTo writes all nodes to the given database.
@ -347,7 +372,12 @@ func (t *Trie) Commit() (root common.Hash, err error) {
// load nodes from the trie's database. Calling code must ensure that // load nodes from the trie's database. Calling code must ensure that
// the changes made to db are written back to the trie's attached // the changes made to db are written back to the trie's attached
// database before using the trie. // database before using the trie.
func (t *Trie) CommitTo(db DatabaseWriter) (root common.Hash, err error) { func (t *Trie) CommitTo(db DatabaseWriter, ctx *access.OdrContext) (root common.Hash, err error) {
if ctx != nil && ctx.IsCancelled() {
// we should never commit potentially corrupt trie nodes to the db
return common.Hash{}, ErrTrieOdrFailure
}
n, err := t.hashRoot(db) n, err := t.hashRoot(db)
if err != nil { if err != nil {
return (common.Hash{}), err return (common.Hash{}), err

View file

@ -26,6 +26,7 @@ import (
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
) )
@ -37,7 +38,7 @@ func init() {
// Used for testing // Used for testing
func newEmpty() *Trie { func newEmpty() *Trie {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
trie, _ := New(common.Hash{}, db) trie, _ := New(common.Hash{}, db, nil)
return trie return trie
} }
@ -54,13 +55,13 @@ func TestNull(t *testing.T) {
var trie Trie var trie Trie
key := make([]byte, 32) key := make([]byte, 32)
value := common.FromHex("0x823140710bf13990e4500136726d8b55") value := common.FromHex("0x823140710bf13990e4500136726d8b55")
trie.Update(key, value) trie.Update(key, value, access.NoOdr)
value = trie.Get(key) value = trie.Get(key, access.NoOdr)
} }
func TestMissingRoot(t *testing.T) { func TestMissingRoot(t *testing.T) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
trie, err := New(common.HexToHash("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"), db) trie, err := New(common.HexToHash("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"), db, nil)
if trie != nil { if trie != nil {
t.Error("New returned non-nil trie for invalid root") t.Error("New returned non-nil trie for invalid root")
} }
@ -190,7 +191,7 @@ func TestReplication(t *testing.T) {
} }
// create a new trie on top of the database and check that lookups work. // create a new trie on top of the database and check that lookups work.
trie2, err := New(exp, trie.db) trie2, err := New(exp, trie.db, nil)
if err != nil { if err != nil {
t.Fatalf("can't recreate trie at %x: %v", exp, err) t.Fatalf("can't recreate trie at %x: %v", exp, err)
} }
@ -231,7 +232,7 @@ func paranoiaCheck(t1 *Trie) (bool, *Trie) {
t2 := new(Trie) t2 := new(Trie)
it := NewIterator(t1) it := NewIterator(t1)
for it.Next() { for it.Next() {
t2.Update(it.Key, it.Value) t2.Update(it.Key, it.Value, access.NoOdr)
} }
return t2.Hash() == t1.Hash(), t2 return t2.Hash() == t1.Hash(), t2
} }
@ -276,15 +277,15 @@ func TestOutput(t *testing.T) {
trie.Commit() trie.Commit()
fmt.Println("############################## SMALL ################################") fmt.Println("############################## SMALL ################################")
trie2, _ := New(trie.Hash(), trie.db) trie2, _ := New(trie.Hash(), trie.db, nil)
getString(trie2, base+"20") getString(trie2, base+"20")
fmt.Println(trie2.root) fmt.Println(trie2.root)
} }
func TestLargeValue(t *testing.T) { func TestLargeValue(t *testing.T) {
trie := newEmpty() trie := newEmpty()
trie.Update([]byte("key1"), []byte{99, 99, 99, 99}) trie.Update([]byte("key1"), []byte{99, 99, 99, 99}, access.NoOdr)
trie.Update([]byte("key2"), bytes.Repeat([]byte{1}, 32)) trie.Update([]byte("key2"), bytes.Repeat([]byte{1}, 32), access.NoOdr)
trie.Hash() trie.Hash()
} }
@ -301,8 +302,8 @@ func TestLargeData(t *testing.T) {
for i := byte(0); i < 255; i++ { for i := byte(0); i < 255; i++ {
value := &kv{common.LeftPadBytes([]byte{i}, 32), []byte{i}, false} value := &kv{common.LeftPadBytes([]byte{i}, 32), []byte{i}, false}
value2 := &kv{common.LeftPadBytes([]byte{10, i}, 32), []byte{i}, false} value2 := &kv{common.LeftPadBytes([]byte{10, i}, 32), []byte{i}, false}
trie.Update(value.k, value.v) trie.Update(value.k, value.v, access.NoOdr)
trie.Update(value2.k, value2.v) trie.Update(value2.k, value2.v, access.NoOdr)
vals[string(value.k)] = value vals[string(value.k)] = value
vals[string(value2.k)] = value2 vals[string(value2.k)] = value2
} }
@ -341,12 +342,12 @@ func benchGet(b *testing.B, commit bool) {
if commit { if commit {
dir, tmpdb := tempDB() dir, tmpdb := tempDB()
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
trie, _ = New(common.Hash{}, tmpdb) trie, _ = New(common.Hash{}, tmpdb, nil)
} }
k := make([]byte, 32) k := make([]byte, 32)
for i := 0; i < benchElemCount; i++ { for i := 0; i < benchElemCount; i++ {
binary.LittleEndian.PutUint64(k, uint64(i)) binary.LittleEndian.PutUint64(k, uint64(i))
trie.Update(k, k) trie.Update(k, k, access.NoOdr)
} }
binary.LittleEndian.PutUint64(k, benchElemCount/2) binary.LittleEndian.PutUint64(k, benchElemCount/2)
if commit { if commit {
@ -355,7 +356,7 @@ func benchGet(b *testing.B, commit bool) {
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
trie.Get(k) trie.Get(k, access.NoOdr)
} }
} }
@ -364,7 +365,7 @@ func benchUpdate(b *testing.B, e binary.ByteOrder) *Trie {
k := make([]byte, 32) k := make([]byte, 32)
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
e.PutUint64(k, uint64(i)) e.PutUint64(k, uint64(i))
trie.Update(k, k) trie.Update(k, k, access.NoOdr)
} }
return trie return trie
} }
@ -374,7 +375,7 @@ func benchHash(b *testing.B, e binary.ByteOrder) {
k := make([]byte, 32) k := make([]byte, 32)
for i := 0; i < benchElemCount; i++ { for i := 0; i < benchElemCount; i++ {
e.PutUint64(k, uint64(i)) e.PutUint64(k, uint64(i))
trie.Update(k, k) trie.Update(k, k, access.NoOdr)
} }
b.ResetTimer() b.ResetTimer()
@ -396,13 +397,13 @@ func tempDB() (string, Database) {
} }
func getString(trie *Trie, k string) []byte { func getString(trie *Trie, k string) []byte {
return trie.Get([]byte(k)) return trie.Get([]byte(k), access.NoOdr)
} }
func updateString(trie *Trie, k, v string) { func updateString(trie *Trie, k, v string) {
trie.Update([]byte(k), []byte(v)) trie.Update([]byte(k), []byte(v), access.NoOdr)
} }
func deleteString(trie *Trie, k string) { func deleteString(trie *Trie, k string) {
trie.Delete([]byte(k)) trie.Delete([]byte(k), access.NoOdr)
} }

View file

@ -45,7 +45,7 @@ func (self *State) SafeGet(addr string) *Object {
func (self *State) safeGet(addr string) *state.StateObject { func (self *State) safeGet(addr string) *state.StateObject {
object := self.state.GetStateObject(common.HexToAddress(addr)) object := self.state.GetStateObject(common.HexToAddress(addr))
if object == nil { if object == nil {
object = state.NewStateObject(common.HexToAddress(addr), self.xeth.backend.ChainDb()) object = state.NewStateObject(common.HexToAddress(addr), self.xeth.backend.ChainAccess())
} }
return object return object

View file

@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -39,20 +40,20 @@ func NewObject(state *state.StateObject) *Object {
return &Object{state} return &Object{state}
} }
func (self *Object) StorageString(str string) []byte { func (self *Object) StorageString(str string, ctx *access.OdrContext) []byte {
if common.IsHex(str) { if common.IsHex(str) {
return self.storage(common.Hex2Bytes(str[2:])) return self.storage(common.Hex2Bytes(str[2:]), ctx)
} else { } else {
return self.storage(common.RightPadBytes([]byte(str), 32)) return self.storage(common.RightPadBytes([]byte(str), 32), ctx)
} }
} }
func (self *Object) StorageValue(addr *common.Value) []byte { func (self *Object) StorageValue(addr *common.Value, ctx *access.OdrContext) []byte {
return self.storage(addr.Bytes()) return self.storage(addr.Bytes(), ctx)
} }
func (self *Object) storage(addr []byte) []byte { func (self *Object) storage(addr []byte, ctx *access.OdrContext) []byte {
return self.StateObject.GetState(common.BytesToHash(addr)).Bytes() return self.StateObject.GetState(common.BytesToHash(addr), ctx).Bytes()
} }
func (self *Object) Storage() (storage map[string]string) { func (self *Object) Storage() (storage map[string]string) {

View file

@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/compiler" "github.com/ethereum/go-ethereum/common/compiler"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/les/access"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
@ -84,6 +85,7 @@ type XEth struct {
state *State state *State
whisper *Whisper whisper *Whisper
filterManager *filters.FilterSystem filterManager *filters.FilterSystem
ctx *access.OdrContext
} }
func NewTest(eth *eth.Ethereum, frontend Frontend) *XEth { func NewTest(eth *eth.Ethereum, frontend Frontend) *XEth {
@ -113,7 +115,7 @@ func New(ethereum *eth.Ethereum, frontend Frontend) *XEth {
if frontend == nil { if frontend == nil {
xeth.frontend = dummyFrontend{} xeth.frontend = dummyFrontend{}
} }
state, _ := xeth.backend.BlockChain().State() state, _ := xeth.backend.BlockChain().State(access.NullCtx)
xeth.state = NewState(xeth, state) xeth.state = NewState(xeth, state)
go xeth.start() go xeth.start()
return xeth return xeth
@ -207,15 +209,15 @@ func (self *XEth) AtStateNum(num int64) *XEth {
var err error var err error
switch num { switch num {
case -2: case -2:
st = self.backend.Miner().PendingState().Copy() st = self.backend.Miner().PendingState().Copy(self.ctx)
default: default:
if block := self.getBlockByHeight(num); block != nil { if block := self.getBlockByHeight(num); block != nil {
st, err = state.New(block.Root(), self.backend.ChainDb()) st, err = state.New(block.Root(), self.backend.ChainAccess(), self.ctx)
if err != nil { if err != nil {
return nil return nil
} }
} else { } else {
st, err = state.New(self.backend.BlockChain().GetBlockByNumber(0).Root(), self.backend.ChainDb()) st, err = state.New(self.backend.BlockChain().GetBlockByNumber(0, self.ctx).Root(), self.backend.ChainAccess(), self.ctx)
if err != nil { if err != nil {
return nil return nil
} }
@ -238,6 +240,18 @@ func (self *XEth) WithState(statedb *state.StateDB) *XEth {
func (self *XEth) State() *State { return self.state } func (self *XEth) State() *State { return self.state }
func (self *XEth) WithCtx(ctx *access.OdrContext) *XEth {
xeth := &XEth{
backend: self.backend,
frontend: self.frontend,
gpo: self.gpo,
ctx: ctx,
}
xeth.state = NewState(xeth, self.state.State().Copy(ctx))
return xeth
}
// subscribes to new head block events and // subscribes to new head block events and
// waits until blockchain height is greater n at any time // waits until blockchain height is greater n at any time
// given the current head, waits for the next chain event // given the current head, waits for the next chain event
@ -270,7 +284,7 @@ func (self *XEth) UpdateState() (wait chan *big.Int) {
wait <- n wait <- n
n = nil n = nil
} }
statedb, err := state.New(event.Block.Root(), self.backend.ChainDb()) statedb, err := state.New(event.Block.Root(), self.backend.ChainAccess(), access.NullCtx)
if err != nil { if err != nil {
glog.V(logger.Error).Infoln("Could not create new state: %v", err) glog.V(logger.Error).Infoln("Could not create new state: %v", err)
return return
@ -305,19 +319,19 @@ func (self *XEth) getBlockByHeight(height int64) *types.Block {
num = uint64(height) num = uint64(height)
} }
return self.backend.BlockChain().GetBlockByNumber(num) return self.backend.BlockChain().GetBlockByNumber(num, self.ctx)
} }
func (self *XEth) BlockByHash(strHash string) *Block { func (self *XEth) BlockByHash(strHash string) *Block {
hash := common.HexToHash(strHash) hash := common.HexToHash(strHash)
block := self.backend.BlockChain().GetBlock(hash) block := self.backend.BlockChain().GetBlock(hash, self.ctx)
return NewBlock(block) return NewBlock(block)
} }
func (self *XEth) EthBlockByHash(strHash string) *types.Block { func (self *XEth) EthBlockByHash(strHash string) *types.Block {
hash := common.HexToHash(strHash) hash := common.HexToHash(strHash)
block := self.backend.BlockChain().GetBlock(hash) block := self.backend.BlockChain().GetBlock(hash, self.ctx)
return block return block
} }
@ -375,15 +389,19 @@ func (self *XEth) Td(hash common.Hash) *big.Int {
} }
func (self *XEth) CurrentBlock() *types.Block { func (self *XEth) CurrentBlock() *types.Block {
return self.backend.BlockChain().CurrentBlock() return self.backend.BlockChain().CurrentBlockOdr(self.ctx)
}
func (self *XEth) CurrentHeader() *types.Header {
return self.backend.BlockChain().CurrentHeader()
} }
func (self *XEth) GetBlockReceipts(bhash common.Hash) types.Receipts { func (self *XEth) GetBlockReceipts(bhash common.Hash) types.Receipts {
return self.backend.BlockProcessor().GetBlockReceipts(bhash) return self.backend.BlockProcessor().GetBlockReceipts(bhash) //ODR
} }
func (self *XEth) GetTxReceipt(txhash common.Hash) *types.Receipt { func (self *XEth) GetTxReceipt(txhash common.Hash) *types.Receipt { //ODR
return core.GetReceipt(self.backend.ChainDb(), txhash) return core.GetReceipt(self.backend.ChainAccess(), txhash, access.NoOdr) //TODO
} }
func (self *XEth) GasLimit() *big.Int { func (self *XEth) GasLimit() *big.Int {
@ -547,7 +565,7 @@ func (self *XEth) NewLogFilter(earliest, latest int64, skip, max int, address []
self.logMu.Lock() self.logMu.Lock()
defer self.logMu.Unlock() defer self.logMu.Unlock()
filter := filters.New(self.backend.ChainDb()) filter := filters.New(self.backend.ChainAccess())
id := self.filterManager.Add(filter) id := self.filterManager.Add(filter)
self.logQueue[id] = &logQueue{timeout: time.Now()} self.logQueue[id] = &logQueue{timeout: time.Now()}
@ -571,7 +589,7 @@ func (self *XEth) NewTransactionFilter() int {
self.transactionMu.Lock() self.transactionMu.Lock()
defer self.transactionMu.Unlock() defer self.transactionMu.Unlock()
filter := filters.New(self.backend.ChainDb()) filter := filters.New(self.backend.ChainAccess())
id := self.filterManager.Add(filter) id := self.filterManager.Add(filter)
self.transactionQueue[id] = &hashQueue{timeout: time.Now()} self.transactionQueue[id] = &hashQueue{timeout: time.Now()}
@ -590,7 +608,7 @@ func (self *XEth) NewBlockFilter() int {
self.blockMu.Lock() self.blockMu.Lock()
defer self.blockMu.Unlock() defer self.blockMu.Unlock()
filter := filters.New(self.backend.ChainDb()) filter := filters.New(self.backend.ChainAccess())
id := self.filterManager.Add(filter) id := self.filterManager.Add(filter)
self.blockQueue[id] = &hashQueue{timeout: time.Now()} self.blockQueue[id] = &hashQueue{timeout: time.Now()}
@ -657,7 +675,7 @@ func (self *XEth) Logs(id int) vm.Logs {
} }
func (self *XEth) AllLogs(earliest, latest int64, skip, max int, address []string, topics [][]string) vm.Logs { func (self *XEth) AllLogs(earliest, latest int64, skip, max int, address []string, topics [][]string) vm.Logs {
filter := filters.New(self.backend.ChainDb()) filter := filters.New(self.backend.ChainAccess())
filter.SetBeginBlock(earliest) filter.SetBeginBlock(earliest)
filter.SetEndBlock(latest) filter.SetEndBlock(latest)
filter.SetAddresses(cAddress(address)) filter.SetAddresses(cAddress(address))
@ -829,7 +847,7 @@ func (self *XEth) PushTx(encodedTx string) (string, error) {
} }
func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, string, error) { func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, string, error) {
statedb := self.State().State().Copy() statedb := self.State().State().CopyWithCtx()
var from *state.StateObject var from *state.StateObject
if len(fromStr) == 0 { if len(fromStr) == 0 {
accounts, err := self.backend.AccountManager().Accounts() accounts, err := self.backend.AccountManager().Accounts()
@ -864,7 +882,7 @@ func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr st
msg.gasPrice = self.DefaultGasPrice() msg.gasPrice = self.DefaultGasPrice()
} }
header := self.CurrentBlock().Header() header := self.CurrentHeader()
vmenv := core.NewEnv(statedb, self.backend.BlockChain(), msg, header) vmenv := core.NewEnv(statedb, self.backend.BlockChain(), msg, header)
gp := new(core.GasPool).AddGas(common.MaxBig) gp := new(core.GasPool).AddGas(common.MaxBig)
res, gas, err := core.ApplyMessage(vmenv, msg, gp) res, gas, err := core.ApplyMessage(vmenv, msg, gp)