mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 02:12:23 +00:00
light: implement GetHeadersByNumber odr function
This commit is contained in:
parent
8ec6efcb88
commit
378da35d38
4 changed files with 120 additions and 30 deletions
|
|
@ -370,19 +370,20 @@ func (r *ChtRequest) CanSend(peer *peer, config *light.IndexerConfig) bool {
|
||||||
|
|
||||||
// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
|
// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
|
||||||
func (r *ChtRequest) Request(reqID uint64, peer *peer, config *light.IndexerConfig) error {
|
func (r *ChtRequest) Request(reqID uint64, peer *peer, config *light.IndexerConfig) error {
|
||||||
peer.Log().Debug("Requesting CHT", "cht", r.ChtNum, "block", r.Numbers)
|
peer.Log().Debug("Requesting CHT", "cht", r.ChtNum, "blocks", r.Numbers)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
encNum [8]byte
|
encNum [8]byte
|
||||||
reqs = make([]HelperTrieReq, 0, len(r.Numbers))
|
reqs = make([]HelperTrieReq, len(r.Numbers))
|
||||||
)
|
)
|
||||||
for _, num := range r.Numbers {
|
for i, num := range r.Numbers {
|
||||||
binary.BigEndian.PutUint64(encNum[:], num)
|
binary.BigEndian.PutUint64(encNum[:], num)
|
||||||
reqs = append(reqs, HelperTrieReq{
|
reqs[i] = HelperTrieReq{
|
||||||
Type: htCanonical,
|
Type: htCanonical,
|
||||||
TrieIdx: r.ChtNum,
|
TrieIdx: r.ChtNum,
|
||||||
Key: common.CopyBytes(encNum[:]),
|
Key: common.CopyBytes(encNum[:]),
|
||||||
AuxReq: auxHeader,
|
AuxReq: auxHeader,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
switch peer.version {
|
switch peer.version {
|
||||||
case lpv1:
|
case lpv1:
|
||||||
|
|
@ -426,7 +427,6 @@ func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error {
|
||||||
binary.BigEndian.PutUint64(encNumber[:], num)
|
binary.BigEndian.PutUint64(encNumber[:], num)
|
||||||
value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], light.NodeList(resp.Proof).NodeSet())
|
value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], light.NodeList(resp.Proof).NodeSet())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var node light.ChtNode
|
var node light.ChtNode
|
||||||
|
|
|
||||||
|
|
@ -36,13 +36,17 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type chtTestFn func(ctx context.Context, bc *core.BlockChain, lc *light.LightChain, number uint64) []byte
|
type chtTestFn func(ctx context.Context, bc *core.BlockChain, lc *light.LightChain, numbers []uint64) []byte
|
||||||
|
|
||||||
func TestChtGetHeadersLes1(t *testing.T) { testCht(t, 1, chtGetHeader) }
|
func TestChtGetHeaderLes1(t *testing.T) { testCht(t, 1, 1, chtGetHeader) }
|
||||||
|
|
||||||
func TestChtGetHeadersLes2(t *testing.T) { testCht(t, 2, chtGetHeader) }
|
func TestChtGetHeaderLes2(t *testing.T) { testCht(t, 2, 1, chtGetHeader) }
|
||||||
|
|
||||||
func chtGetHeader(ctx context.Context, bc *core.BlockChain, lc *light.LightChain, number uint64) []byte {
|
func chtGetHeader(ctx context.Context, bc *core.BlockChain, lc *light.LightChain, numbers []uint64) []byte {
|
||||||
|
if len(numbers) != 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
number := numbers[0]
|
||||||
var header *types.Header
|
var header *types.Header
|
||||||
if bc != nil {
|
if bc != nil {
|
||||||
header = bc.GetHeaderByNumber(number)
|
header = bc.GetHeaderByNumber(number)
|
||||||
|
|
@ -56,6 +60,26 @@ func chtGetHeader(ctx context.Context, bc *core.BlockChain, lc *light.LightChain
|
||||||
return rlp
|
return rlp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestChtGetHeadersLes1(t *testing.T) { testCht(t, 1, MaxHelperTrieProofsFetch, chtGetHeaders) }
|
||||||
|
|
||||||
|
func TestChtGetHeadersLes2(t *testing.T) { testCht(t, 2, MaxHelperTrieProofsFetch, chtGetHeaders) }
|
||||||
|
|
||||||
|
func chtGetHeaders(ctx context.Context, bc *core.BlockChain, lc *light.LightChain, numbers []uint64) []byte {
|
||||||
|
var headers []*types.Header
|
||||||
|
if bc != nil {
|
||||||
|
for _, number := range numbers {
|
||||||
|
headers = append(headers, bc.GetHeaderByNumber(number))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
headers, _ = lc.GetHeadersByNumberOdr(ctx, numbers)
|
||||||
|
}
|
||||||
|
if headers == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rlp, _ := rlp.EncodeToBytes(headers)
|
||||||
|
return rlp
|
||||||
|
}
|
||||||
|
|
||||||
type odrTestFn func(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte
|
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) }
|
func TestOdrGetBlockLes1(t *testing.T) { testOdr(t, 1, 1, odrGetBlock) }
|
||||||
|
|
@ -180,7 +204,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
|
||||||
}
|
}
|
||||||
|
|
||||||
// testCht tests cht requests whose validation guaranteed by calculated cht root.
|
// testCht tests cht requests whose validation guaranteed by calculated cht root.
|
||||||
func testCht(t *testing.T, protocol int, fn chtTestFn) {
|
func testCht(t *testing.T, protocol int, maxFetch int, fn chtTestFn) {
|
||||||
// Assemble the test environment
|
// Assemble the test environment
|
||||||
config := light.TestServerIndexerConfig
|
config := light.TestServerIndexerConfig
|
||||||
waitIndexers := func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) {
|
waitIndexers := func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) {
|
||||||
|
|
@ -188,23 +212,20 @@ func testCht(t *testing.T, protocol int, fn chtTestFn) {
|
||||||
cs, _, _ := cIndexer.Sections()
|
cs, _, _ := cIndexer.Sections()
|
||||||
bs, _, _ := bIndexer.Sections()
|
bs, _, _ := bIndexer.Sections()
|
||||||
bts, _, _ := btIndexer.Sections()
|
bts, _, _ := btIndexer.Sections()
|
||||||
if cs >= 8 && bs >= 8 && bts >= 1 {
|
if cs >= config.PairChtSize/config.ChtSize && bs >= config.PairChtSize/config.BloomSize &&
|
||||||
|
bts >= config.PairChtSize/config.BloomTrieSize {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
time.Sleep(10 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
server, client, tearDown := newClientServerEnv(t, int(config.ChtSize*8+config.ChtConfirm), protocol, waitIndexers, false)
|
server, client, tearDown := newClientServerEnv(t, int(config.PairChtSize+config.ChtConfirm), protocol, waitIndexers, false)
|
||||||
defer func() {
|
defer tearDown()
|
||||||
if tearDown != nil {
|
|
||||||
tearDown()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Add trusted checkpoint for client side indexers.
|
// Add trusted checkpoint for client side indexers.
|
||||||
cs, _, head := server.chtIndexer.Sections()
|
cs, _, head := server.chtIndexer.Sections()
|
||||||
light.StoreChtRoot(client.db, cs/8-1, head, light.GetChtRoot(server.db, cs-1, head))
|
light.StoreChtRoot(client.db, cs*config.ChtSize/config.PairChtSize-1, head, light.GetChtRoot(server.db, cs-1, head))
|
||||||
client.chtIndexer.AddKnownSectionHead(cs/8-1, head)
|
client.chtIndexer.AddKnownSectionHead(cs*config.ChtSize/config.PairChtSize-1, head)
|
||||||
bts, _, head := server.bloomTrieIndexer.Sections()
|
bts, _, head := server.bloomTrieIndexer.Sections()
|
||||||
light.StoreBloomTrieRoot(client.db, bts-1, head, light.GetBloomTrieRoot(server.db, bts-1, head))
|
light.StoreBloomTrieRoot(client.db, bts-1, head, light.GetBloomTrieRoot(server.db, bts-1, head))
|
||||||
client.bloomTrieIndexer.AddKnownSectionHead(bts-1, head)
|
client.bloomTrieIndexer.AddKnownSectionHead(bts-1, head)
|
||||||
|
|
@ -220,18 +241,23 @@ func testCht(t *testing.T, protocol int, fn chtTestFn) {
|
||||||
}
|
}
|
||||||
server.rPeer, client.rPeer = peer, lPeer
|
server.rPeer, client.rPeer = peer, lPeer
|
||||||
|
|
||||||
test := func() {
|
i := uint64(0)
|
||||||
for i := uint64(0); i <= config.ChtSize*8-1; i++ {
|
for {
|
||||||
h1 := fn(light.NoOdr, server.pm.blockchain.(*core.BlockChain), nil, i)
|
var numbers []uint64
|
||||||
|
for ; i <= config.PairChtSize-1 && len(numbers) < maxFetch; i += 1 {
|
||||||
|
numbers = append(numbers, i)
|
||||||
|
}
|
||||||
|
if len(numbers) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
h1 := fn(light.NoOdr, server.pm.blockchain.(*core.BlockChain), nil, numbers)
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
|
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
|
||||||
h2 := fn(ctx, nil, client.pm.blockchain.(*light.LightChain), i)
|
h2 := fn(ctx, nil, client.pm.blockchain.(*light.LightChain), numbers)
|
||||||
if !bytes.Equal(h1, h2) {
|
if !bytes.Equal(h1, h2) {
|
||||||
t.Error("cht mismatch")
|
t.Error("cht mismatch")
|
||||||
}
|
}
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
test()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// testOdr tests odr requests whose validation guaranteed by block headers.
|
// testOdr tests odr requests whose validation guaranteed by block headers.
|
||||||
|
|
|
||||||
|
|
@ -462,6 +462,13 @@ func (self *LightChain) GetHeaderByNumberOdr(ctx context.Context, number uint64)
|
||||||
return GetHeaderByNumber(ctx, self.odr, number)
|
return GetHeaderByNumber(ctx, self.odr, number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetHeadersByNumberOdr retrieves a batch of block headers from the database or network
|
||||||
|
// by number, caching it (associated with its hash) if found.
|
||||||
|
func (self *LightChain) GetHeadersByNumberOdr(ctx context.Context, numbers []uint64) ([]*types.Header, error) {
|
||||||
|
// TODO(rjl4935456442) Caching the read block headers.
|
||||||
|
return GetHeadersByNumber(ctx, self.odr, numbers)
|
||||||
|
}
|
||||||
|
|
||||||
// Config retrieves the header chain's chain configuration.
|
// Config retrieves the header chain's chain configuration.
|
||||||
func (self *LightChain) Config() *params.ChainConfig { return self.hc.Config() }
|
func (self *LightChain) Config() *params.ChainConfig { return self.hc.Config() }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,63 @@ func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*typ
|
||||||
return r.Headers[0], nil
|
return r.Headers[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetHeadersByNumber(ctx context.Context, odr OdrBackend, numbers []uint64) ([]*types.Header, error) {
|
||||||
|
var (
|
||||||
|
db = odr.Database()
|
||||||
|
headers = make([]*types.Header, len(numbers))
|
||||||
|
missing []int
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, number := range numbers {
|
||||||
|
hash := rawdb.ReadCanonicalHash(db, number)
|
||||||
|
if (hash != common.Hash{}) {
|
||||||
|
// if there is a canonical hash, there is a header too
|
||||||
|
header := rawdb.ReadHeader(db, hash, number)
|
||||||
|
if header == nil {
|
||||||
|
panic("Canonical hash present but header not found")
|
||||||
|
}
|
||||||
|
headers[i] = header
|
||||||
|
} else {
|
||||||
|
missing = append(missing, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
chtCount, sectionHeadNum uint64
|
||||||
|
sectionHead common.Hash
|
||||||
|
)
|
||||||
|
if odr.ChtIndexer() != nil {
|
||||||
|
chtCount, sectionHeadNum, sectionHead = odr.ChtIndexer().Sections()
|
||||||
|
canonicalHash := rawdb.ReadCanonicalHash(db, sectionHeadNum)
|
||||||
|
// if the CHT was injected as a trusted checkpoint, we have no canonical hash yet so we accept zero hash too
|
||||||
|
for chtCount > 0 && canonicalHash != sectionHead && canonicalHash != (common.Hash{}) {
|
||||||
|
chtCount--
|
||||||
|
if chtCount > 0 {
|
||||||
|
sectionHeadNum = chtCount*odr.IndexerConfig().ChtSize - 1
|
||||||
|
sectionHead = odr.ChtIndexer().SectionHead(chtCount - 1)
|
||||||
|
canonicalHash = rawdb.ReadCanonicalHash(db, sectionHeadNum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reqs := make([]uint64, 0, len(missing))
|
||||||
|
for _, index := range missing {
|
||||||
|
if numbers[index] >= chtCount*odr.IndexerConfig().ChtSize {
|
||||||
|
return nil, ErrNoTrustedCht
|
||||||
|
}
|
||||||
|
reqs = append(reqs, numbers[index])
|
||||||
|
}
|
||||||
|
r := &ChtRequest{ChtRoot: GetChtRoot(db, chtCount-1, sectionHead), ChtNum: chtCount - 1, Numbers: reqs}
|
||||||
|
if err := odr.Retrieve(ctx, r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Assemble the final result
|
||||||
|
for i, index := range missing {
|
||||||
|
headers[index] = r.Headers[i]
|
||||||
|
}
|
||||||
|
return headers, nil
|
||||||
|
}
|
||||||
|
|
||||||
func GetCanonicalHash(ctx context.Context, odr OdrBackend, number uint64) (common.Hash, error) {
|
func GetCanonicalHash(ctx context.Context, odr OdrBackend, number uint64) (common.Hash, error) {
|
||||||
hash := rawdb.ReadCanonicalHash(odr.Database(), number)
|
hash := rawdb.ReadCanonicalHash(odr.Database(), number)
|
||||||
if (hash != common.Hash{}) {
|
if (hash != common.Hash{}) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue