Merge pull request #19288 from karalabe/les-verbose-errors

les, light: verbose errors on state retrieval issues
This commit is contained in:
Péter Szilágyi 2019-03-18 13:51:28 +02:00 committed by GitHub
commit 47c03c0f8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 116 additions and 74 deletions

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

@ -211,9 +211,8 @@ func (lc *LightChain) Genesis() *types.Block {
return lc.genesisBlock return lc.genesisBlock
} }
// State returns a new mutable state based on the current HEAD block. func (lc *LightChain) StateCache() state.Database {
func (lc *LightChain) State() (*state.StateDB, error) { panic("not implemented")
return nil, errors.New("not implemented, needs client/server interface split")
} }
// GetBody retrieves a block body (transactions and uncles) from the database // GetBody retrieves a block body (transactions and uncles) from the database