@ -72,7 +72,7 @@ type BlockChain interface {
GetHeaderByHash ( hash common . Hash ) * types . Header
CurrentHeader ( ) * types . Header
GetTd ( hash common . Hash , number uint64 ) * big . Int
State ( ) ( * state . StateDB , error )
StateCache ( ) state . Database
InsertHeaderChain ( chain [ ] * types . Header , checkFreq int ) ( int , error )
Rollback ( chain [ ] common . Hash )
GetHeaderByNumber ( number uint64 ) * types . Header
@ -642,26 +642,35 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
if i != 0 && ! task . waitOrStop ( ) {
return
}
// Retrieve the requested state entry, stopping if enough was found
if number := rawdb . ReadHeaderNumber ( pm . chainDb , req . BHash ) ; number != nil {
if header := rawdb . ReadHeader ( pm . chainDb , req . BHash , * number ) ; header != nil {
statedb , err := pm . blockchain . State ( )
// Look up the root hash belonging to the request
number := rawdb . ReadHeaderNumber ( pm . chainDb , req . BHash )
if number == nil {
p . Log ( ) . Warn ( "Failed to retrieve block num for code" , "hash" , req . BHash )
continue
}
header := rawdb . ReadHeader ( pm . chainDb , req . BHash , * number )
if header == nil {
p . Log ( ) . Warn ( "Failed to retrieve header for code" , "block" , * number , "hash" , req . BHash )
continue
}
triedb := pm . blockchain . StateCache ( ) . TrieDB ( )
account , err := pm . getAccount ( triedb , header . Root , common . BytesToHash ( req . AccKey ) )
if err != nil {
p . Log ( ) . Warn ( "Failed to retrieve account for code" , "block" , header . Number , "hash" , header . Hash ( ) , "account" , common . BytesToHash ( req . AccKey ) , "err" , err )
continue
}
account , err := pm . getAccount ( statedb , header . Root , common . BytesToHash ( req . AccKey ) )
code , err := triedb . Node ( common . BytesToHash ( account . CodeHash ) )
if err != nil {
p . Log ( ) . Warn ( "Failed to retrieve account code" , "block" , header . Number , "hash" , header . Hash ( ) , "account" , common . BytesToHash ( req . AccKey ) , "codehash" , common . BytesToHash ( account . CodeHash ) , "err" , err )
continue
}
code , _ := statedb . Database ( ) . TrieDB ( ) . Node ( common . BytesToHash ( account . CodeHash ) )
// Accumulate the code and abort if enough data was retrieved
data = append ( data , code )
if bytes += len ( code ) ; bytes >= softResponseLimit {
break
}
}
}
}
sendResponse ( req . ReqID , uint64 ( reqCnt ) , p . ReplyCode ( req . ReqID , data ) , task . done ( ) )
} ( )
@ -779,35 +788,53 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
if i != 0 && ! task . waitOrStop ( ) {
return
}
// Retrieve the requested state entry, stopping if enough was found
if number := rawdb . ReadHeaderNumber ( pm . chainDb , req . BHash ) ; number != nil {
if header := rawdb . ReadHeader ( pm . chainDb , req . BHash , * number ) ; header != nil {
statedb , err := pm . blockchain . State ( )
if err != nil {
// Look up the root hash belonging to the request
number := rawdb . ReadHeaderNumber ( pm . chainDb , req . BHash )
if number == nil {
p . Log ( ) . Warn ( "Failed to retrieve block num for proof" , "hash" , req . BHash )
continue
}
header := rawdb . ReadHeader ( pm . chainDb , req . BHash , * number )
if header == nil {
p . Log ( ) . Warn ( "Failed to retrieve header for proof" , "block" , * number , "hash" , req . BHash )
continue
}
// Open the account or storage trie for the request
statedb := pm . blockchain . StateCache ( )
var trie state . Trie
if len ( req . AccKey ) > 0 {
account , err := pm . getAccount ( statedb , header . Root , common . BytesToHash ( req . AccKey ) )
switch len ( req . AccKey ) {
case 0 :
// No account key specified, open an account trie
trie , err = statedb . OpenTrie ( header . Root )
if trie == nil || err != nil {
p . Log ( ) . Warn ( "Failed to open storage trie for proof" , "block" , header . Number , "hash" , header . Hash ( ) , "root" , header . Root , "err" , err )
continue
}
default :
// Account key specified, open a storage trie
account , err := pm . getAccount ( statedb . TrieDB ( ) , header . Root , common . BytesToHash ( req . AccKey ) )
if err != nil {
p . Log ( ) . Warn ( "Failed to retrieve account for proof" , "block" , header . Number , "hash" , header . Hash ( ) , "account" , common . BytesToHash ( req . AccKey ) , "err" , err )
continue
}
trie , err = statedb . OpenStorageTrie ( common . BytesToHash ( req . AccKey ) , account . Root )
if trie == nil || err != nil {
p . Log ( ) . Warn ( "Failed to open storage trie for proof" , "block" , header . Number , "hash" , header . Hash ( ) , "account" , common . BytesToHash ( req . AccKey ) , "root" , account . Root , "err" , err )
continue
}
trie , _ = statedb . Database ( ) . OpenStorageTrie ( common . BytesToHash ( req . AccKey ) , account . Root )
} else {
trie , _ = statedb . Database ( ) . OpenTrie ( header . Root )
}
if trie != nil {
// Prove the user's request from the account or stroage trie
var proof light . NodeList
trie . Prove ( req . Key , 0 , & proof )
if err := trie . Prove ( req . Key , 0 , & proof ) ; err != nil {
p . Log ( ) . Warn ( "Failed to prove state request" , "block" , header . Number , "hash" , header . Hash ( ) , "err" , err )
continue
}
proofs = append ( proofs , proof )
if bytes += proof . DataSize ( ) ; bytes >= softResponseLimit {
break
}
}
}
}
}
sendResponse ( req . ReqID , uint64 ( reqCnt ) , p . ReplyProofs ( req . ReqID , proofs ) , task . done ( ) )
} ( )
@ -824,7 +851,6 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
// Gather state data until the fetch or network limits is reached
var (
lastBHash common . Hash
statedb * state . StateDB
root common . Hash
)
reqCnt := len ( req . Reqs )
@ -832,43 +858,60 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp ( ErrRequestRejected , "" )
}
go func ( ) {
nodes := light . NewNodeSet ( )
for i , req := range req . Reqs {
if i != 0 && ! task . waitOrStop ( ) {
return
}
// Look up the state belonging to the request
if statedb == nil || req . BHash != lastBHash {
statedb , root , lastBHash = nil , common . Hash { } , req . BHash
// Look up the root hash belonging to the request
var (
number * uint64
header * types . Header
trie state . Trie
)
if req . BHash != lastBHash {
root , lastBHash = common . Hash { } , req . BHash
if number := rawdb . ReadHeaderNumber ( pm . chainDb , req . BHash ) ; number != nil {
if header := rawdb . ReadHeader ( pm . chainDb , req . BHash , * number ) ; header != nil {
statedb , _ = pm . blockchain . State ( )
root = header . Root
if number = rawdb . ReadHeaderNumber ( pm . chainDb , req . BHash ) ; number == nil {
p . Log ( ) . Warn ( "Failed to retrieve block num for proof" , "hash" , req . BHash )
continue
}
if header = rawdb . ReadHeader ( pm . chainDb , req . BHash , * number ) ; header == nil {
p . Log ( ) . Warn ( "Failed to retrieve header for proof" , "block" , * number , "hash" , req . BHash )
continue
}
root = header . Root
}
if statedb == nil {
// Open the account or storage trie for the request
statedb := pm . blockchain . StateCache ( )
switch len ( req . AccKey ) {
case 0 :
// No account key specified, open an account trie
trie , err = statedb . OpenTrie ( root )
if trie == nil || err != nil {
p . Log ( ) . Warn ( "Failed to open storage trie for proof" , "block" , header . Number , "hash" , header . Hash ( ) , "root" , root , "err" , err )
continue
}
// Pull the account or storage trie of the request
var trie state . Trie
if len ( req . AccKey ) > 0 {
account , err := pm . getAccount ( statedb , root , common . BytesToHash ( req . AccKey ) )
default :
// Account key specified, open a storage trie
account , err := pm . getAccount ( statedb . TrieDB ( ) , root , common . BytesToHash ( req . AccKey ) )
if err != nil {
p . Log ( ) . Warn ( "Failed to retrieve account for proof" , "block" , header . Number , "hash" , header . Hash ( ) , "account" , common . BytesToHash ( req . AccKey ) , "err" , err )
continue
}
trie , _ = statedb . Database ( ) . OpenStorageTrie ( common . BytesToHash ( req . AccKey ) , account . Root )
} else {
trie , _ = statedb . Database ( ) . OpenTrie ( root )
}
if trie == nil {
trie , err = statedb . OpenStorageTrie ( common . BytesToHash ( req . AccKey ) , account . Root )
if trie == nil || err != nil {
p . Log ( ) . Warn ( "Failed to open storage trie for proof" , "block" , header . Number , "hash" , header . Hash ( ) , "account" , common . BytesToHash ( req . AccKey ) , "root" , account . Root , "err" , err )
continue
}
}
// Prove the user's request from the account or stroage trie
trie . Prove ( req . Key , req . FromLevel , nodes )
if err := trie . Prove ( req . Key , req . FromLevel , nodes ) ; err != nil {
p . Log ( ) . Warn ( "Failed to prove state request" , "block" , header . Number , "hash" , header . Hash ( ) , "err" , err )
continue
}
if nodes . DataSize ( ) >= softResponseLimit {
break
}
@ -1190,8 +1233,8 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
}
// getAccount retrieves an account from the state based at root.
func ( pm * ProtocolManager ) getAccount ( statedb * state . StateDB , root , hash common . Hash ) ( state . Account , error ) {
trie , err := trie . New ( root , statedb . Database ( ) . TrieDB ( ) )
func ( pm * ProtocolManager ) getAccount ( triedb * trie . Database , root , hash common . Hash ) ( state . Account , error ) {
trie , err := trie . New ( root , triedb )
if err != nil {
return state . Account { } , err
}