diff --git a/les/handler.go b/les/handler.go index 9efe7d9e1..50c32fb95 100644 --- a/les/handler.go +++ b/les/handler.go @@ -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,33 +788,51 @@ 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 + // 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 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)) - if err != nil { + if number = rawdb.ReadHeaderNumber(pm.chainDb, req.BHash); number == nil { + p.Log().Warn("Failed to retrieve block num for proof", "hash", req.BHash) continue } - trie, _ = statedb.Database().OpenStorageTrie(common.BytesToHash(req.AccKey), account.Root) - } else { - trie, _ = statedb.Database().OpenTrie(root) + 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 trie == nil { - continue + // 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 + } } // 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 } diff --git a/light/lightchain.go b/light/lightchain.go index fb5f8ead2..38f1f6341 100644 --- a/light/lightchain.go +++ b/light/lightchain.go @@ -211,9 +211,8 @@ func (lc *LightChain) Genesis() *types.Block { return lc.genesisBlock } -// State returns a new mutable state based on the current HEAD block. -func (lc *LightChain) State() (*state.StateDB, error) { - return nil, errors.New("not implemented, needs client/server interface split") +func (lc *LightChain) StateCache() state.Database { + panic("not implemented") } // GetBody retrieves a block body (transactions and uncles) from the database