|
|
@@ -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,24 +642,33 @@ 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 {
|
|
|
- continue
|
|
|
- }
|
|
|
- account, err := pm.getAccount(statedb, header.Root, common.BytesToHash(req.AccKey))
|
|
|
- if err != nil {
|
|
|
- continue
|
|
|
- }
|
|
|
- code, _ := statedb.Database().TrieDB().Node(common.BytesToHash(account.CodeHash))
|
|
|
+ // 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()
|
|
|
|
|
|
- data = append(data, code)
|
|
|
- if bytes += len(code); bytes >= softResponseLimit {
|
|
|
- break
|
|
|
- }
|
|
|
- }
|
|
|
+ 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
|
|
|
+ }
|
|
|
+ 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
|
|
|
+ }
|
|
|
+ // 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,34 +788,52 @@ 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 {
|
|
|
- continue
|
|
|
- }
|
|
|
- var trie state.Trie
|
|
|
- if len(req.AccKey) > 0 {
|
|
|
- account, err := pm.getAccount(statedb, header.Root, common.BytesToHash(req.AccKey))
|
|
|
- if err != nil {
|
|
|
- continue
|
|
|
- }
|
|
|
- trie, _ = statedb.Database().OpenStorageTrie(common.BytesToHash(req.AccKey), account.Root)
|
|
|
- } else {
|
|
|
- trie, _ = statedb.Database().OpenTrie(header.Root)
|
|
|
- }
|
|
|
- if trie != nil {
|
|
|
- var proof light.NodeList
|
|
|
- trie.Prove(req.Key, 0, &proof)
|
|
|
+ // 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()
|
|
|
|
|
|
- proofs = append(proofs, proof)
|
|
|
- if bytes += proof.DataSize(); bytes >= softResponseLimit {
|
|
|
- break
|
|
|
- }
|
|
|
- }
|
|
|
+ var trie state.Trie
|
|
|
+ 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
|
|
|
}
|
|
|
}
|
|
|
+ // Prove the user's request from the account or stroage trie
|
|
|
+ var proof light.NodeList
|
|
|
+ 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
|
|
|
-
|
|
|
- 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
|
|
|
- }
|
|
|
+ // 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 {
|
|
|
+ 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 {
|
|
|
- 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))
|
|
|
+ // 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
|
|
|
+ }
|
|
|
+ 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, 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(root)
|
|
|
}
|
|
|
- if trie == nil {
|
|
|
+ // Prove the user's request from the account or stroage trie
|
|
|
+ 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
|
|
|
}
|
|
|
- // Prove the user's request from the account or stroage trie
|
|
|
- trie.Prove(req.Key, req.FromLevel, nodes)
|
|
|
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
|
|
|
}
|