diff --git a/les/odr_requests.go b/les/odr_requests.go index a4fe9c8fc4..7a5c7c26fb 100644 --- a/les/odr_requests.go +++ b/les/odr_requests.go @@ -365,7 +365,6 @@ func (r *ChtRequest) GetCost(peer *peer) uint64 { func (r *ChtRequest) CanSend(peer *peer, config *light.IndexerConfig) bool { peer.lock.RLock() defer peer.lock.RUnlock() - return peer.headInfo.Number >= config.ChtConfirm && r.ChtNum <= (peer.headInfo.Number-config.ChtConfirm)/config.ChtSize } @@ -394,7 +393,7 @@ func (r *ChtRequest) Request(reqID uint64, peer *peer, config *light.IndexerConf } blockNum := binary.BigEndian.Uint64(r.Key) // convert HelperTrie request to old CHT request - reqsV1 = append(reqsV1, ChtReq{ChtNum: (r.TrieIdx+1)*(config.ChtSize/config.PairChtSize), BlockNum: blockNum, FromLevel: r.FromLevel}) + reqsV1 = append(reqsV1, ChtReq{ChtNum: (r.TrieIdx + 1) * (config.ChtSize / config.PairChtSize), BlockNum: blockNum, FromLevel: r.FromLevel}) } return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), reqsV1) case lpv2: @@ -409,7 +408,6 @@ func (r *ChtRequest) Request(reqID uint64, peer *peer, config *light.IndexerConf // to the request (implementation of LesOdrRequest) func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error { log.Debug("Validating CHT", "cht", r.ChtNum, "block", r.Numbers) - switch msg.MsgType { case MsgHeaderProofs: // LES/1 backwards compatibility resps := msg.Obj.([]ChtResp) @@ -428,6 +426,7 @@ func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error { binary.BigEndian.PutUint64(encNumber[:], num) value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], light.NodeList(resp.Proof).NodeSet()) if err != nil { + fmt.Println(err) return err } var node light.ChtNode diff --git a/les/odr_test.go b/les/odr_test.go index e6458adf56..ee1f7d5a4f 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -36,6 +36,26 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) +type chtTestFn func(ctx context.Context, bc *core.BlockChain, lc *light.LightChain, number uint64) []byte + +func TestChtGetHeadersLes1(t *testing.T) { testCht(t, 1, chtGetHeader) } + +func TestChtGetHeadersLes2(t *testing.T) { testCht(t, 2, chtGetHeader) } + +func chtGetHeader(ctx context.Context, bc *core.BlockChain, lc *light.LightChain, number uint64) []byte { + var header *types.Header + if bc != nil { + header = bc.GetHeaderByNumber(number) + } else { + header, _ = lc.GetHeaderByNumberOdr(ctx, number) + } + if header == nil { + return nil + } + rlp, _ := rlp.EncodeToBytes(header) + return rlp +} + type odrTestFn func(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte func TestOdrGetBlockLes1(t *testing.T) { testOdr(t, 1, 1, odrGetBlock) } @@ -159,6 +179,61 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai return res } +// testCht tests cht requests whose validation guaranteed by calculated cht root. +func testCht(t *testing.T, protocol int, fn chtTestFn) { + // Assemble the test environment + config := light.TestServerIndexerConfig + waitIndexers := func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) { + for { + cs, _, _ := cIndexer.Sections() + bs, _, _ := bIndexer.Sections() + bts, _, _ := btIndexer.Sections() + if cs >= 8 && bs >= 8 && bts >= 1 { + break + } + time.Sleep(10 * time.Millisecond) + } + } + server, client, tearDown := newClientServerEnv(t, int(config.ChtSize*8+config.ChtConfirm), protocol, waitIndexers, false) + defer func() { + if tearDown != nil { + tearDown() + } + }() + + // Add trusted checkpoint for client side indexers. + cs, _, head := server.chtIndexer.Sections() + light.StoreChtRoot(client.db, cs/8-1, head, light.GetChtRoot(server.db, cs-1, head)) + client.chtIndexer.AddKnownSectionHead(cs/8-1, head) + bts, _, head := server.bloomTrieIndexer.Sections() + light.StoreBloomTrieRoot(client.db, bts-1, head, light.GetBloomTrieRoot(server.db, bts-1, head)) + client.bloomTrieIndexer.AddKnownSectionHead(bts-1, head) + + // Create connected peer pair. + peer, err1, lPeer, err2 := newTestPeerPair("peer", protocol, server.pm, client.pm) + select { + case <-time.After(time.Millisecond * 100): + case err := <-err1: + t.Fatalf("peer 1 handshake error: %v", err) + case err := <-err2: + t.Fatalf("peer 2 handshake error: %v", err) + } + server.rPeer, client.rPeer = peer, lPeer + + test := func() { + for i := uint64(0); i <= config.ChtSize*8-1; i++ { + h1 := fn(light.NoOdr, server.pm.blockchain.(*core.BlockChain), nil, i) + ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) + h2 := fn(ctx, nil, client.pm.blockchain.(*light.LightChain), i) + if !bytes.Equal(h1, h2) { + t.Error("cht mismatch") + } + cancel() + } + } + test() +} + // testOdr tests odr requests whose validation guaranteed by block headers. func testOdr(t *testing.T, protocol int, expFail uint64, fn odrTestFn) { // Assemble the test environment diff --git a/les/peer.go b/les/peer.go index f4d6c53c75..1e2f446ed1 100644 --- a/les/peer.go +++ b/les/peer.go @@ -298,6 +298,7 @@ func (p *peer) RequestHelperTrieProofs(reqID, cost uint64, data interface{}) err if !ok { return errInvalidHelpTrieReq } + p.Log().Debug("Fetching batch of HelperTrie proofs", "count", len(reqs)) return sendRequest(p.rw, GetHelperTrieProofsMsg, reqID, cost, reqs) default: panic(nil) @@ -489,7 +490,6 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis p.fcServer = flowcontrol.NewServerNode(params) p.fcCosts = MRC.decode() } - p.headInfo = &announceData{Td: rTd, Hash: rHash, Number: rNum} return nil }