les, light: fix receipt reconstruction in client

This commit is contained in:
Péter Szilágyi 2018-02-22 11:00:13 +02:00
parent 724a915470
commit 861a38398b
No known key found for this signature in database
GPG key ID: E9AE538CEDF8293D
5 changed files with 36 additions and 23 deletions

View file

@ -140,20 +140,20 @@ func (r *ReceiptsRequest) GetCost(peer *peer) uint64 {
// CanSend tells if a certain peer is suitable for serving the given request // CanSend tells if a certain peer is suitable for serving the given request
func (r *ReceiptsRequest) CanSend(peer *peer) bool { func (r *ReceiptsRequest) CanSend(peer *peer) bool {
return peer.HasBlock(r.Hash, r.Number) return peer.HasBlock(r.Block.Hash(), r.Block.NumberU64())
} }
// 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 *ReceiptsRequest) Request(reqID uint64, peer *peer) error { func (r *ReceiptsRequest) Request(reqID uint64, peer *peer) error {
peer.Log().Debug("Requesting block receipts", "hash", r.Hash) peer.Log().Debug("Requesting block receipts", "hash", r.Block.Hash())
return peer.RequestReceipts(reqID, r.GetCost(peer), []common.Hash{r.Hash}) return peer.RequestReceipts(reqID, r.GetCost(peer), []common.Hash{r.Block.Hash()})
} }
// Valid processes an ODR request reply message from the LES network // Valid processes an ODR request reply message from the LES network
// returns true and stores results in memory if the message was a valid reply // returns true and stores results in memory if the message was a valid reply
// to the request (implementation of LesOdrRequest) // to the request (implementation of LesOdrRequest)
func (r *ReceiptsRequest) Validate(db ethdb.Database, msg *Msg) error { func (r *ReceiptsRequest) Validate(db ethdb.Database, msg *Msg) error {
log.Debug("Validating block receipts", "hash", r.Hash) log.Debug("Validating block receipts", "hash", r.Block.Hash())
// Ensure we have a correct message with a single block receipt // Ensure we have a correct message with a single block receipt
if msg.MsgType != MsgReceipts { if msg.MsgType != MsgReceipts {
@ -166,14 +166,12 @@ func (r *ReceiptsRequest) Validate(db ethdb.Database, msg *Msg) error {
receipt := receipts[0] receipt := receipts[0]
// Retrieve our stored header and validate receipt content against it // Retrieve our stored header and validate receipt content against it
header := core.GetHeader(db, r.Hash, r.Number) if r.Block.ReceiptHash() != types.DeriveSha(receipt) {
if header == nil {
return errHeaderUnavailable
}
if header.ReceiptHash != types.DeriveSha(receipt) {
return errReceiptHashMismatch return errReceiptHashMismatch
} }
// Validations passed, store and return // Validations passed, store and return
core.SetReceiptsData(r.Config, r.Block, receipt)
r.Receipts = receipt r.Receipts = receipt
return nil return nil
} }

View file

@ -35,13 +35,13 @@ func secAddr(addr common.Address) []byte {
return crypto.Keccak256(addr[:]) return crypto.Keccak256(addr[:])
} }
type accessTestFn func(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest type accessTestFn func(odr *LesOdr, bhash common.Hash, number uint64) light.OdrRequest
func TestBlockAccessLes1(t *testing.T) { testAccess(t, 1, tfBlockAccess) } func TestBlockAccessLes1(t *testing.T) { testAccess(t, 1, tfBlockAccess) }
func TestBlockAccessLes2(t *testing.T) { testAccess(t, 2, tfBlockAccess) } func TestBlockAccessLes2(t *testing.T) { testAccess(t, 2, tfBlockAccess) }
func tfBlockAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { func tfBlockAccess(odr *LesOdr, bhash common.Hash, number uint64) light.OdrRequest {
return &light.BlockRequest{Hash: bhash, Number: number} return &light.BlockRequest{Hash: bhash, Number: number}
} }
@ -49,24 +49,30 @@ func TestReceiptsAccessLes1(t *testing.T) { testAccess(t, 1, tfReceiptsAccess) }
func TestReceiptsAccessLes2(t *testing.T) { testAccess(t, 2, tfReceiptsAccess) } func TestReceiptsAccessLes2(t *testing.T) { testAccess(t, 2, tfReceiptsAccess) }
func tfReceiptsAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { func tfReceiptsAccess(odr *LesOdr, bhash common.Hash, number uint64) light.OdrRequest {
return &light.ReceiptsRequest{Hash: bhash, Number: number} odr.Retrieve(context.TODO(), &light.BlockRequest{Hash: bhash, Number: number})
block := core.GetBlock(odr.Database(), bhash, number)
genesis := core.GetCanonicalHash(odr.Database(), 0)
config, _ := core.GetChainConfig(odr.Database(), genesis)
return &light.ReceiptsRequest{Config: config, Block: block}
} }
func TestTrieEntryAccessLes1(t *testing.T) { testAccess(t, 1, tfTrieEntryAccess) } func TestTrieEntryAccessLes1(t *testing.T) { testAccess(t, 1, tfTrieEntryAccess) }
func TestTrieEntryAccessLes2(t *testing.T) { testAccess(t, 2, tfTrieEntryAccess) } func TestTrieEntryAccessLes2(t *testing.T) { testAccess(t, 2, tfTrieEntryAccess) }
func tfTrieEntryAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { func tfTrieEntryAccess(odr *LesOdr, bhash common.Hash, number uint64) light.OdrRequest {
return &light.TrieRequest{Id: light.StateTrieID(core.GetHeader(db, bhash, core.GetBlockNumber(db, bhash))), Key: testBankSecureTrieKey} return &light.TrieRequest{Id: light.StateTrieID(core.GetHeader(odr.Database(), bhash, core.GetBlockNumber(odr.Database(), bhash))), Key: testBankSecureTrieKey}
} }
func TestCodeAccessLes1(t *testing.T) { testAccess(t, 1, tfCodeAccess) } func TestCodeAccessLes1(t *testing.T) { testAccess(t, 1, tfCodeAccess) }
func TestCodeAccessLes2(t *testing.T) { testAccess(t, 2, tfCodeAccess) } func TestCodeAccessLes2(t *testing.T) { testAccess(t, 2, tfCodeAccess) }
func tfCodeAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { func tfCodeAccess(odr *LesOdr, bhash common.Hash, number uint64) light.OdrRequest {
header := core.GetHeader(db, bhash, core.GetBlockNumber(db, bhash)) header := core.GetHeader(odr.Database(), bhash, core.GetBlockNumber(odr.Database(), bhash))
if header.Number.Uint64() < testContractDeployed { if header.Number.Uint64() < testContractDeployed {
return nil return nil
} }
@ -100,7 +106,7 @@ func testAccess(t *testing.T, protocol int, fn accessTestFn) {
test := func(expFail uint64) { test := func(expFail uint64) {
for i := uint64(0); i <= pm.blockchain.CurrentHeader().Number.Uint64(); i++ { for i := uint64(0); i <= pm.blockchain.CurrentHeader().Number.Uint64(); i++ {
bhash := core.GetCanonicalHash(db, i) bhash := core.GetCanonicalHash(db, i)
if req := fn(ldb, bhash, i); req != nil { if req := fn(odr, bhash, i); req != nil {
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
defer cancel() defer cancel()

View file

@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
) )
// NoOdr is the default context passed to an ODR capable function when the ODR // NoOdr is the default context passed to an ODR capable function when the ODR
@ -118,14 +119,14 @@ func (req *BlockRequest) StoreResult(db ethdb.Database) {
// ReceiptsRequest is the ODR request type for retrieving block bodies // ReceiptsRequest is the ODR request type for retrieving block bodies
type ReceiptsRequest struct { type ReceiptsRequest struct {
OdrRequest OdrRequest
Hash common.Hash Config *params.ChainConfig
Number uint64 Block *types.Block
Receipts types.Receipts Receipts types.Receipts
} }
// StoreResult stores the retrieved data in local database // StoreResult stores the retrieved data in local database
func (req *ReceiptsRequest) StoreResult(db ethdb.Database) { func (req *ReceiptsRequest) StoreResult(db ethdb.Database) {
core.WriteBlockReceipts(db, req.Hash, req.Number, req.Receipts) core.WriteBlockReceipts(db, req.Block.Hash(), req.Block.NumberU64(), req.Receipts)
} }
// ChtRequest is the ODR request type for state/storage trie entries // ChtRequest is the ODR request type for state/storage trie entries

View file

@ -72,7 +72,7 @@ func (odr *testOdr) Retrieve(ctx context.Context, req OdrRequest) error {
case *BlockRequest: case *BlockRequest:
req.Rlp = core.GetBodyRLP(odr.sdb, req.Hash, core.GetBlockNumber(odr.sdb, req.Hash)) req.Rlp = core.GetBodyRLP(odr.sdb, req.Hash, core.GetBlockNumber(odr.sdb, req.Hash))
case *ReceiptsRequest: case *ReceiptsRequest:
req.Receipts = core.GetBlockReceipts(odr.sdb, req.Hash, core.GetBlockNumber(odr.sdb, req.Hash)) req.Receipts = core.GetBlockReceipts(odr.sdb, req.Block.Hash(), req.Block.NumberU64())
case *TrieRequest: case *TrieRequest:
t, _ := trie.New(req.Id.Root, trie.NewDatabase(odr.sdb)) t, _ := trie.New(req.Id.Root, trie.NewDatabase(odr.sdb))
nodes := NewNodeSet() nodes := NewNodeSet()

View file

@ -130,7 +130,15 @@ func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, num
if receipts != nil { if receipts != nil {
return receipts, nil return receipts, nil
} }
r := &ReceiptsRequest{Hash: hash, Number: number} // Receipts unavailable locally, we need the full block to reconstruct
block, err := GetBlock(ctx, odr, hash, number)
if err != nil {
return nil, err
}
genesis := core.GetCanonicalHash(odr.Database(), 0)
config, _ := core.GetChainConfig(odr.Database(), genesis)
r := &ReceiptsRequest{Config: config, Block: block}
if err := odr.Retrieve(ctx, r); err != nil { if err := odr.Retrieve(ctx, r); err != nil {
return nil, err return nil, err
} }