diff --git a/core/access/chain_access.go b/core/access/chain_access.go index b41dd296f8..6e5d68f4f5 100644 --- a/core/access/chain_access.go +++ b/core/access/chain_access.go @@ -56,15 +56,18 @@ type ChainAccess struct { 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, @@ -107,6 +110,11 @@ type Msg struct { 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 @@ -116,6 +124,7 @@ type ObjectAccess interface { 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() @@ -130,6 +139,7 @@ func (self *ChainAccess) Deliver(id string, msg *Msg) (processed bool) { 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), @@ -175,6 +185,8 @@ func (self *ChainAccess) networkRequest(rqFunc requestFunc, valFunc validatorFun } } +// 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() { diff --git a/core/access/context.go b/core/access/context.go index 652c9a1cd6..ee9b4e2384 100644 --- a/core/access/context.go +++ b/core/access/context.go @@ -23,6 +23,7 @@ import ( "time" ) +// Context for ODR requests, can be timed out or cancelled manually type OdrContext struct { cancel, cancelOrTimeout chan struct{} id *OdrChannelID @@ -64,6 +65,10 @@ func (self *OdrContext) IsCancelled() bool { 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 } diff --git a/core/access/peer.go b/core/access/peer.go index 2b30a400b0..43d1d2a67e 100644 --- a/core/access/peer.go +++ b/core/access/peer.go @@ -42,6 +42,7 @@ 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 diff --git a/core/blockchain.go b/core/blockchain.go index 5bb452869f..43e7ed530d 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -374,6 +374,7 @@ 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() defer self.mu.RUnlock() diff --git a/core/core_access.go b/core/core_access.go index a5a108ceaf..d7a68c4798 100644 --- a/core/core_access.go +++ b/core/core_access.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) +// ODR request type for block bodies, see access.ObjectAccess interface type BlockAccess struct { db ethdb.Database blockHash common.Hash @@ -87,6 +88,7 @@ func (self *BlockAccess) DbPut() { 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 diff --git a/core/state/state_access.go b/core/state/state_access.go index c4145b5791..68e0949ebc 100644 --- a/core/state/state_access.go +++ b/core/state/state_access.go @@ -53,6 +53,7 @@ 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 @@ -101,6 +102,7 @@ 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 diff --git a/core/state/statedb.go b/core/state/statedb.go index 39632407ac..152335e2c9 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -54,6 +54,7 @@ type StateDB struct { } // Create a new state from a given trie +// When ODR is used, context is specified when creating/copying a state. func New(root common.Hash, ca *access.ChainAccess, ctx *access.OdrContext) (*StateDB, error) { tr, err := trie.NewSecure(root, ca.Db(), NewTrieAccess(ca, root, ca.Db())) if err != nil { @@ -297,10 +298,12 @@ func (self *StateDB) CreateAccount(addr common.Address) vm.Account { // Setting, copying of the state methods // +// 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 state, _ := New(common.Hash{}, self.ca, ctx) diff --git a/rpc/shared/types.go b/rpc/shared/types.go index 7dd6fb2d9a..94ea5530d7 100644 --- a/rpc/shared/types.go +++ b/rpc/shared/types.go @@ -40,6 +40,8 @@ type EthereumApi interface { } // 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 { Id interface{} `json:"id"` Jsonrpc string `json:"jsonrpc"`