mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 13:46:43 +00:00
fixed failing tests, added new ones
This commit is contained in:
parent
a486c11fbe
commit
7a44bab77b
14 changed files with 879 additions and 99 deletions
|
|
@ -135,11 +135,6 @@ var (
|
|||
Name: "natspec",
|
||||
Usage: "Enable NatSpec confirmation notice",
|
||||
}
|
||||
DocRootFlag = DirectoryFlag{
|
||||
Name: "docroot",
|
||||
Usage: "Document Root for HTTPClient file scheme",
|
||||
Value: DirectoryString{common.HomeDir()},
|
||||
}
|
||||
CacheFlag = cli.IntFlag{
|
||||
Name: "cache",
|
||||
Usage: "Megabytes of memory allocated to internal caching (min 16MB / database forced)",
|
||||
|
|
@ -466,7 +461,6 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
|
|||
Olympic: ctx.GlobalBool(OlympicFlag.Name),
|
||||
NAT: MakeNAT(ctx),
|
||||
NatSpec: ctx.GlobalBool(NatspecEnabledFlag.Name),
|
||||
DocRoot: ctx.GlobalString(DocRootFlag.Name),
|
||||
Discovery: !ctx.GlobalBool(NoDiscoverFlag.Name),
|
||||
NodeKey: MakeNodeKey(ctx),
|
||||
Shh: ctx.GlobalBool(WhisperEnabledFlag.Name),
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ var (
|
|||
errNotInDb = errors.New("object not found in database")
|
||||
)
|
||||
|
||||
const LogLevel = logger.Info
|
||||
const LogLevel = logger.Debug
|
||||
|
||||
var (
|
||||
requestTimeout = time.Millisecond * 300
|
||||
|
|
@ -45,20 +45,33 @@ type ChainAccess struct {
|
|||
db ethdb.Database
|
||||
odr bool // light client mode, odr enabled
|
||||
lock sync.Mutex
|
||||
valFunc validatorFunc
|
||||
deliverChan chan *Msg
|
||||
sentReqs map[uint64]*sentReq
|
||||
sentReqCnt uint64
|
||||
peers *peerSet
|
||||
|
||||
// p2p access objects
|
||||
// parameters (light/full/archive)
|
||||
}
|
||||
|
||||
type requestFunc func(*Peer) error
|
||||
type validatorFunc func(*Msg) bool
|
||||
|
||||
type sentReq struct {
|
||||
valFunc validatorFunc
|
||||
deliverChan chan *Msg
|
||||
}
|
||||
|
||||
func NewDbChainAccess(db ethdb.Database) *ChainAccess {
|
||||
return NewChainAccess(db, false)
|
||||
}
|
||||
|
||||
func NewChainAccess(db ethdb.Database, odr bool) *ChainAccess {
|
||||
return &ChainAccess{db: db, peers: newPeerSet(), odr: odr}
|
||||
return &ChainAccess{
|
||||
db: db,
|
||||
peers: newPeerSet(),
|
||||
sentReqs: make(map[uint64]*sentReq),
|
||||
odr: odr,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *ChainAccess) Db() ethdb.Database {
|
||||
|
|
@ -103,37 +116,37 @@ type ObjectAccess interface {
|
|||
Valid(*Msg) bool // if true, keeps the retrieved object
|
||||
}
|
||||
|
||||
type requestFunc func(*Peer) error
|
||||
type validatorFunc func(*Msg) bool
|
||||
|
||||
func (self *ChainAccess) Deliver(id string, msg *Msg) (processed bool) {
|
||||
self.lock.Lock()
|
||||
valFunc := self.valFunc
|
||||
chn := self.deliverChan
|
||||
self.lock.Unlock()
|
||||
if (valFunc != nil) && (chn != nil) && valFunc(msg) {
|
||||
chn <- msg
|
||||
defer self.lock.Unlock()
|
||||
|
||||
for i, req := range self.sentReqs {
|
||||
if req.valFunc(msg) {
|
||||
req.deliverChan <- msg
|
||||
delete(self.sentReqs, i)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *ChainAccess) networkRequest(rqFunc requestFunc, valFunc validatorFunc, ctx *OdrContext) (*Msg, error) {
|
||||
|
||||
req := &sentReq{
|
||||
deliverChan: make(chan *Msg),
|
||||
valFunc: valFunc,
|
||||
}
|
||||
self.lock.Lock()
|
||||
self.deliverChan = make(chan *Msg)
|
||||
self.valFunc = valFunc
|
||||
reqCnt := self.sentReqCnt
|
||||
self.sentReqCnt++
|
||||
self.sentReqs[reqCnt] = req
|
||||
self.lock.Unlock()
|
||||
|
||||
defer func() {
|
||||
self.lock.Lock()
|
||||
self.deliverChan = nil
|
||||
self.valFunc = nil
|
||||
delete(self.sentReqs, reqCnt)
|
||||
self.lock.Unlock()
|
||||
}()
|
||||
|
||||
//fmt.Println("networkRequest")
|
||||
|
||||
var msg *Msg
|
||||
|
||||
for {
|
||||
|
|
@ -150,7 +163,7 @@ func (self *ChainAccess) networkRequest(rqFunc requestFunc, valFunc validatorFun
|
|||
select {
|
||||
case <-ctx.cancelOrTimeout:
|
||||
return nil, ErrCancel
|
||||
case msg = <-self.deliverChan:
|
||||
case msg = <-req.deliverChan:
|
||||
peer.Promote()
|
||||
glog.V(LogLevel).Infof("networkRequest success")
|
||||
return msg, nil
|
||||
|
|
|
|||
|
|
@ -274,9 +274,6 @@ func (bc *BlockChain) SetHead(head uint64) {
|
|||
if bc.currentBlock == nil {
|
||||
bc.currentBlock = bc.genesisBlock
|
||||
}
|
||||
bc.insert(bc.currentBlock)
|
||||
} else {
|
||||
bc.writeHeader(bc.currentHeader)
|
||||
}
|
||||
if bc.currentHeader == nil {
|
||||
bc.currentHeader = bc.genesisBlock.Header()
|
||||
|
|
|
|||
|
|
@ -800,7 +800,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||
// Import the chain as an archive node and ensure all pointers are updated
|
||||
archiveDb, _ := ethdb.NewMemDatabase()
|
||||
archiveCa := access.NewDbChainAccess(archiveDb)
|
||||
|
||||
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
|
||||
archive, _ := NewBlockChain(archiveCa, FakePow{}, new(event.TypeMux))
|
||||
archive.SetProcessor(NewBlockProcessor(archiveCa, FakePow{}, archive, new(event.TypeMux)))
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/access"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
|
|
@ -110,18 +109,13 @@ func (self *ReceiptsAccess) Valid(msg *access.Msg) bool {
|
|||
glog.V(access.LogLevel).Infof("ODR: invalid number of entries: %d", len(receipts))
|
||||
return false
|
||||
}
|
||||
data, err := rlp.EncodeToBytes(receipts[0])
|
||||
if err != nil {
|
||||
glog.V(access.LogLevel).Infof("ODR: RLP encode error: %v", err)
|
||||
return false
|
||||
}
|
||||
hash := crypto.Sha3Hash(data)
|
||||
hash := types.DeriveSha(receipts[0])
|
||||
header := GetHeader(self.db, self.blockHash)
|
||||
if header == nil {
|
||||
glog.V(access.LogLevel).Infof("ODR: header not found for block %08x", self.blockHash[:4])
|
||||
return false
|
||||
}
|
||||
if bytes.Compare(header.ReceiptHash[:], hash[:]) != 0 {
|
||||
if !bytes.Equal(header.ReceiptHash[:], hash[:]) {
|
||||
glog.V(access.LogLevel).Infof("ODR: header receipts hash %08x does not match calculated RLP hash %08x", header.ReceiptHash[:4], hash[:4])
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package state
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
//"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/access"
|
||||
|
|
|
|||
|
|
@ -241,14 +241,14 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
defer msg.Discard()
|
||||
|
||||
// Handle the message depending on its contents
|
||||
switch {
|
||||
case msg.Code == StatusMsg:
|
||||
switch msg.Code {
|
||||
case StatusMsg:
|
||||
glog.V(access.LogLevel).Infof("LES: received StatusMsg from peer %v", p.id)
|
||||
// Status messages should never arrive after the handshake
|
||||
return errResp(ErrExtraStatusMsg, "uncontrolled status message")
|
||||
|
||||
// Block header query, collect the requested headers and reply
|
||||
case msg.Code == GetBlockHeadersMsg:
|
||||
case GetBlockHeadersMsg:
|
||||
glog.V(access.LogLevel).Infof("LES: received GetBlockHeadersMsg from peer %v", p.id)
|
||||
// Decode the complex header query
|
||||
var query getBlockHeadersData
|
||||
|
|
@ -313,7 +313,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
}
|
||||
return p.SendBlockHeaders(headers)
|
||||
|
||||
case msg.Code == BlockHeadersMsg:
|
||||
case BlockHeadersMsg:
|
||||
glog.V(access.LogLevel).Infof("LES: received BlockHeadersMsg from peer %v", p.id)
|
||||
// A batch of headers arrived to one of our previous requests
|
||||
var headers []*types.Header
|
||||
|
|
@ -332,7 +332,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
}
|
||||
//}
|
||||
|
||||
case msg.Code == GetBlockBodiesMsg:
|
||||
case GetBlockBodiesMsg:
|
||||
glog.V(access.LogLevel).Infof("LES: received GetBlockBodiesMsg from peer %v", p.id)
|
||||
// Decode the retrieval message
|
||||
msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
|
||||
|
|
@ -360,7 +360,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
}
|
||||
return p.SendBlockBodiesRLP(bodies)
|
||||
|
||||
case msg.Code == BlockBodiesMsg:
|
||||
case BlockBodiesMsg:
|
||||
glog.V(access.LogLevel).Infof("LES: received BlockBodiesMsg from peer %v", p.id)
|
||||
// A batch of block bodies arrived to one of our previous requests
|
||||
var data []*types.Body
|
||||
|
|
@ -372,7 +372,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
Obj: data,
|
||||
})
|
||||
|
||||
case msg.Code == GetNodeDataMsg:
|
||||
case GetNodeDataMsg:
|
||||
glog.V(access.LogLevel).Infof("LES: received GetNodeDataMsg from peer %v", p.id)
|
||||
// Decode the retrieval message
|
||||
msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
|
||||
|
|
@ -400,7 +400,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
}
|
||||
return p.SendNodeData(data)
|
||||
|
||||
case msg.Code == NodeDataMsg:
|
||||
case NodeDataMsg:
|
||||
glog.V(access.LogLevel).Infof("LES: received NodeDataMsg from peer %v", p.id)
|
||||
// A batch of node state data arrived to one of our previous requests
|
||||
var data [][]byte
|
||||
|
|
@ -412,7 +412,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
Obj: data,
|
||||
})
|
||||
|
||||
case msg.Code == GetReceiptsMsg:
|
||||
case GetReceiptsMsg:
|
||||
glog.V(access.LogLevel).Infof("LES: received GetReceiptsMsg from peer %v", p.id)
|
||||
// Decode the retrieval message
|
||||
msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
|
||||
|
|
@ -449,19 +449,19 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
}
|
||||
return p.SendReceiptsRLP(receipts)
|
||||
|
||||
case msg.Code == ReceiptsMsg:
|
||||
case ReceiptsMsg:
|
||||
glog.V(access.LogLevel).Infof("LES: received ReceiptsMsg from peer %v", p.id)
|
||||
// A batch of receipts arrived to one of our previous requests
|
||||
var receipts [][]*types.Receipt
|
||||
var receipts []types.Receipts
|
||||
if err := msg.Decode(&receipts); err != nil {
|
||||
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
||||
}
|
||||
pm.chainAccess.Deliver(p.id, &access.Msg{
|
||||
MsgType: access.MsgReceipts,
|
||||
Obj: receipts[0],
|
||||
Obj: receipts,
|
||||
})
|
||||
|
||||
case msg.Code == GetProofsMsg:
|
||||
case GetProofsMsg:
|
||||
glog.V(access.LogLevel).Infof("LES: received GetProofsMsg from peer %v", p.id)
|
||||
// Decode the retrieval message
|
||||
msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
|
||||
|
|
@ -490,7 +490,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
}
|
||||
return p.SendProofs(proofs)
|
||||
|
||||
case msg.Code == ProofsMsg:
|
||||
case ProofsMsg:
|
||||
glog.V(access.LogLevel).Infof("LES: received ProofsMsg from peer %v", p.id)
|
||||
// A batch of merkle proofs arrived to one of our previous requests
|
||||
var data []trie.MerkleProof
|
||||
|
|
@ -502,7 +502,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
Obj: data,
|
||||
})
|
||||
|
||||
case msg.Code == NewBlockHashesMsg:
|
||||
case NewBlockHashesMsg:
|
||||
glog.V(access.LogLevel).Infof("LES: received NewBlockHashesMsg from peer %v", p.id)
|
||||
// Retrieve and deseralize the remote new block hashes notification
|
||||
type announce struct {
|
||||
|
|
|
|||
434
les/handler_test.go
Normal file
434
les/handler_test.go
Normal file
|
|
@ -0,0 +1,434 @@
|
|||
package les
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/access"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
// Tests that block headers can be retrieved from a remote chain based on user queries.
|
||||
func TestGetBlockHeadersLes1(t *testing.T) { testGetBlockHeaders(t, 1) }
|
||||
|
||||
func testGetBlockHeaders(t *testing.T, protocol int) {
|
||||
pm, _ := newTestProtocolManagerMust(t, false, downloader.MaxHashFetch+15, nil)
|
||||
peer, _ := newTestPeer("peer", protocol, pm, true)
|
||||
defer peer.close()
|
||||
|
||||
// Create a "random" unknown hash for testing
|
||||
var unknown common.Hash
|
||||
for i, _ := range unknown {
|
||||
unknown[i] = byte(i)
|
||||
}
|
||||
// Create a batch of tests for various scenarios
|
||||
limit := uint64(downloader.MaxHeaderFetch)
|
||||
tests := []struct {
|
||||
query *getBlockHeadersData // The query to execute for header retrieval
|
||||
expect []common.Hash // The hashes of the block whose headers are expected
|
||||
}{
|
||||
// A single random block should be retrievable by hash and number too
|
||||
{
|
||||
&getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash()}, Amount: 1},
|
||||
[]common.Hash{pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash()},
|
||||
}, {
|
||||
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1},
|
||||
[]common.Hash{pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash()},
|
||||
},
|
||||
// Multiple headers should be retrievable in both directions
|
||||
{
|
||||
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3},
|
||||
[]common.Hash{
|
||||
pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash(),
|
||||
pm.blockchain.GetBlockByNumber(limit/2+1, access.NoOdr).Hash(),
|
||||
pm.blockchain.GetBlockByNumber(limit/2+2, access.NoOdr).Hash(),
|
||||
},
|
||||
}, {
|
||||
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true},
|
||||
[]common.Hash{
|
||||
pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash(),
|
||||
pm.blockchain.GetBlockByNumber(limit/2-1, access.NoOdr).Hash(),
|
||||
pm.blockchain.GetBlockByNumber(limit/2-2, access.NoOdr).Hash(),
|
||||
},
|
||||
},
|
||||
// Multiple headers with skip lists should be retrievable
|
||||
{
|
||||
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3},
|
||||
[]common.Hash{
|
||||
pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash(),
|
||||
pm.blockchain.GetBlockByNumber(limit/2+4, access.NoOdr).Hash(),
|
||||
pm.blockchain.GetBlockByNumber(limit/2+8, access.NoOdr).Hash(),
|
||||
},
|
||||
}, {
|
||||
&getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true},
|
||||
[]common.Hash{
|
||||
pm.blockchain.GetBlockByNumber(limit/2, access.NoOdr).Hash(),
|
||||
pm.blockchain.GetBlockByNumber(limit/2-4, access.NoOdr).Hash(),
|
||||
pm.blockchain.GetBlockByNumber(limit/2-8, access.NoOdr).Hash(),
|
||||
},
|
||||
},
|
||||
// The chain endpoints should be retrievable
|
||||
{
|
||||
&getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1},
|
||||
[]common.Hash{pm.blockchain.GetBlockByNumber(0, access.NoOdr).Hash()},
|
||||
}, {
|
||||
&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64()}, Amount: 1},
|
||||
[]common.Hash{pm.blockchain.CurrentBlock().Hash()},
|
||||
},
|
||||
// Ensure protocol limits are honored
|
||||
{
|
||||
&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true},
|
||||
pm.blockchain.GetBlockHashesFromHash(pm.blockchain.CurrentBlock().Hash(), limit),
|
||||
},
|
||||
// Check that requesting more than available is handled gracefully
|
||||
{
|
||||
&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3},
|
||||
[]common.Hash{
|
||||
pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()-4, access.NoOdr).Hash(),
|
||||
pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64(), access.NoOdr).Hash(),
|
||||
},
|
||||
}, {
|
||||
&getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true},
|
||||
[]common.Hash{
|
||||
pm.blockchain.GetBlockByNumber(4, access.NoOdr).Hash(),
|
||||
pm.blockchain.GetBlockByNumber(0, access.NoOdr).Hash(),
|
||||
},
|
||||
},
|
||||
// Check that requesting more than available is handled gracefully, even if mid skip
|
||||
{
|
||||
&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3},
|
||||
[]common.Hash{
|
||||
pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()-4, access.NoOdr).Hash(),
|
||||
pm.blockchain.GetBlockByNumber(pm.blockchain.CurrentBlock().NumberU64()-1, access.NoOdr).Hash(),
|
||||
},
|
||||
}, {
|
||||
&getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true},
|
||||
[]common.Hash{
|
||||
pm.blockchain.GetBlockByNumber(4, access.NoOdr).Hash(),
|
||||
pm.blockchain.GetBlockByNumber(1, access.NoOdr).Hash(),
|
||||
},
|
||||
},
|
||||
// Check that non existing headers aren't returned
|
||||
{
|
||||
&getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1},
|
||||
[]common.Hash{},
|
||||
}, {
|
||||
&getBlockHeadersData{Origin: hashOrNumber{Number: pm.blockchain.CurrentBlock().NumberU64() + 1}, Amount: 1},
|
||||
[]common.Hash{},
|
||||
},
|
||||
}
|
||||
// Run each of the tests and verify the results against the chain
|
||||
for i, tt := range tests {
|
||||
// Collect the headers to expect in the response
|
||||
headers := []*types.Header{}
|
||||
for _, hash := range tt.expect {
|
||||
headers = append(headers, pm.blockchain.GetBlock(hash, access.NoOdr).Header())
|
||||
}
|
||||
// Send the hash request and verify the response
|
||||
p2p.Send(peer.app, GetBlockHeadersMsg, tt.query)
|
||||
if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, headers); err != nil {
|
||||
t.Errorf("test %d: headers mismatch: %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that block contents can be retrieved from a remote chain based on their hashes.
|
||||
func TestGetBlockBodiesLes1(t *testing.T) { testGetBlockBodies(t, 1) }
|
||||
|
||||
func testGetBlockBodies(t *testing.T, protocol int) {
|
||||
pm, _ := newTestProtocolManagerMust(t, false, downloader.MaxBlockFetch+15, nil)
|
||||
peer, _ := newTestPeer("peer", protocol, pm, true)
|
||||
defer peer.close()
|
||||
|
||||
// Create a batch of tests for various scenarios
|
||||
limit := downloader.MaxBlockFetch
|
||||
tests := []struct {
|
||||
random int // Number of blocks to fetch randomly from the chain
|
||||
explicit []common.Hash // Explicitly requested blocks
|
||||
available []bool // Availability of explicitly requested blocks
|
||||
expected int // Total number of existing blocks to expect
|
||||
}{
|
||||
{1, nil, nil, 1}, // A single random block should be retrievable
|
||||
{10, nil, nil, 10}, // Multiple random blocks should be retrievable
|
||||
{limit, nil, nil, limit}, // The maximum possible blocks should be retrievable
|
||||
{limit + 1, nil, nil, limit}, // No more than the possible block count should be returned
|
||||
{0, []common.Hash{pm.blockchain.Genesis().Hash()}, []bool{true}, 1}, // The genesis block should be retrievable
|
||||
{0, []common.Hash{pm.blockchain.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable
|
||||
{0, []common.Hash{common.Hash{}}, []bool{false}, 0}, // A non existent block should not be returned
|
||||
|
||||
// Existing and non-existing blocks interleaved should not cause problems
|
||||
{0, []common.Hash{
|
||||
common.Hash{},
|
||||
pm.blockchain.GetBlockByNumber(1, access.NoOdr).Hash(),
|
||||
common.Hash{},
|
||||
pm.blockchain.GetBlockByNumber(10, access.NoOdr).Hash(),
|
||||
common.Hash{},
|
||||
pm.blockchain.GetBlockByNumber(100, access.NoOdr).Hash(),
|
||||
common.Hash{},
|
||||
}, []bool{false, true, false, true, false, true, false}, 3},
|
||||
}
|
||||
// Run each of the tests and verify the results against the chain
|
||||
for i, tt := range tests {
|
||||
// Collect the hashes to request, and the response to expect
|
||||
hashes, seen := []common.Hash{}, make(map[int64]bool)
|
||||
bodies := []*types.Body{}
|
||||
|
||||
for j := 0; j < tt.random; j++ {
|
||||
for {
|
||||
num := rand.Int63n(int64(pm.blockchain.CurrentBlock().NumberU64()))
|
||||
if !seen[num] {
|
||||
seen[num] = true
|
||||
|
||||
block := pm.blockchain.GetBlockByNumber(uint64(num), access.NoOdr)
|
||||
hashes = append(hashes, block.Hash())
|
||||
if len(bodies) < tt.expected {
|
||||
bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()})
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
for j, hash := range tt.explicit {
|
||||
hashes = append(hashes, hash)
|
||||
if tt.available[j] && len(bodies) < tt.expected {
|
||||
block := pm.blockchain.GetBlock(hash, access.NoOdr)
|
||||
bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()})
|
||||
}
|
||||
}
|
||||
// Send the hash request and verify the response
|
||||
p2p.Send(peer.app, GetBlockBodiesMsg, hashes)
|
||||
if err := p2p.ExpectMsg(peer.app, BlockBodiesMsg, bodies); err != nil {
|
||||
t.Errorf("test %d: bodies mismatch: %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that the node state database can be retrieved based on hashes.
|
||||
func TestGetNodeDataLes1(t *testing.T) { testGetNodeData(t, 1) }
|
||||
|
||||
func testGetNodeData(t *testing.T, protocol int) {
|
||||
// Define three accounts to simulate transactions with
|
||||
acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||
acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
|
||||
acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey)
|
||||
acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey)
|
||||
|
||||
// Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_makerts_test)
|
||||
generator := func(i int, block *core.BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
// In block 1, the test bank sends account #1 some ether.
|
||||
tx, _ := types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil).SignECDSA(testBankKey)
|
||||
block.AddTx(tx)
|
||||
case 1:
|
||||
// In block 2, the test bank sends some more ether to account #1.
|
||||
// acc1Addr passes it on to account #2.
|
||||
tx1, _ := types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(testBankKey)
|
||||
tx2, _ := types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(acc1Key)
|
||||
block.AddTx(tx1)
|
||||
block.AddTx(tx2)
|
||||
case 2:
|
||||
// Block 3 is empty but was mined by account #2.
|
||||
block.SetCoinbase(acc2Addr)
|
||||
block.SetExtra([]byte("yeehaw"))
|
||||
case 3:
|
||||
// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
|
||||
b2 := block.PrevBlock(1).Header()
|
||||
b2.Extra = []byte("foo")
|
||||
block.AddUncle(b2)
|
||||
b3 := block.PrevBlock(2).Header()
|
||||
b3.Extra = []byte("foo")
|
||||
block.AddUncle(b3)
|
||||
}
|
||||
}
|
||||
// Assemble the test environment
|
||||
pm, _ := newTestProtocolManagerMust(t, false, 4, generator)
|
||||
peer, _ := newTestPeer("peer", protocol, pm, true)
|
||||
defer peer.close()
|
||||
|
||||
// Fetch for now the entire chain db
|
||||
hashes := []common.Hash{}
|
||||
for _, key := range pm.chainAccess.Db().(*ethdb.MemDatabase).Keys() {
|
||||
if len(key) == len(common.Hash{}) {
|
||||
hashes = append(hashes, common.BytesToHash(key))
|
||||
}
|
||||
}
|
||||
p2p.Send(peer.app, GetNodeDataMsg, hashes)
|
||||
msg, err := peer.app.ReadMsg()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read node data response: %v", err)
|
||||
}
|
||||
if msg.Code != NodeDataMsg {
|
||||
t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, 0x0c)
|
||||
}
|
||||
var data [][]byte
|
||||
if err := msg.Decode(&data); err != nil {
|
||||
t.Fatalf("failed to decode response node data: %v", err)
|
||||
}
|
||||
// Verify that all hashes correspond to the requested data, and reconstruct a state tree
|
||||
for i, want := range hashes {
|
||||
if hash := crypto.Sha3Hash(data[i]); hash != want {
|
||||
fmt.Errorf("data hash mismatch: have %x, want %x", hash, want)
|
||||
}
|
||||
}
|
||||
statedb, _ := ethdb.NewMemDatabase()
|
||||
for i := 0; i < len(data); i++ {
|
||||
statedb.Put(hashes[i].Bytes(), data[i])
|
||||
}
|
||||
accounts := []common.Address{testBankAddress, acc1Addr, acc2Addr}
|
||||
for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
|
||||
trie, _ := state.New(pm.blockchain.GetBlockByNumber(i, access.NoOdr).Root(), access.NewDbChainAccess(statedb), access.NullCtx)
|
||||
|
||||
for j, acc := range accounts {
|
||||
state, _ := pm.blockchain.State(access.NullCtx)
|
||||
bw := state.GetBalance(acc)
|
||||
bh := trie.GetBalance(acc)
|
||||
|
||||
if (bw != nil && bh == nil) || (bw == nil && bh != nil) {
|
||||
t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw)
|
||||
}
|
||||
if bw != nil && bh != nil && bw.Cmp(bw) != 0 {
|
||||
t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that the transaction receipts can be retrieved based on hashes.
|
||||
func TestGetReceiptLes1(t *testing.T) { testGetReceipt(t, 1) }
|
||||
|
||||
func testGetReceipt(t *testing.T, protocol int) {
|
||||
// Define three accounts to simulate transactions with
|
||||
acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||
acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
|
||||
acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey)
|
||||
acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey)
|
||||
|
||||
// Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_makerts_test)
|
||||
generator := func(i int, block *core.BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
// In block 1, the test bank sends account #1 some ether.
|
||||
tx, _ := types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil).SignECDSA(testBankKey)
|
||||
block.AddTx(tx)
|
||||
case 1:
|
||||
// In block 2, the test bank sends some more ether to account #1.
|
||||
// acc1Addr passes it on to account #2.
|
||||
tx1, _ := types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(testBankKey)
|
||||
tx2, _ := types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(acc1Key)
|
||||
block.AddTx(tx1)
|
||||
block.AddTx(tx2)
|
||||
case 2:
|
||||
// Block 3 is empty but was mined by account #2.
|
||||
block.SetCoinbase(acc2Addr)
|
||||
block.SetExtra([]byte("yeehaw"))
|
||||
case 3:
|
||||
// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
|
||||
b2 := block.PrevBlock(1).Header()
|
||||
b2.Extra = []byte("foo")
|
||||
block.AddUncle(b2)
|
||||
b3 := block.PrevBlock(2).Header()
|
||||
b3.Extra = []byte("foo")
|
||||
block.AddUncle(b3)
|
||||
}
|
||||
}
|
||||
// Assemble the test environment
|
||||
pm, _ := newTestProtocolManagerMust(t, false, 4, generator)
|
||||
peer, _ := newTestPeer("peer", protocol, pm, true)
|
||||
defer peer.close()
|
||||
|
||||
// Collect the hashes to request, and the response to expect
|
||||
hashes, receipts := []common.Hash{}, []types.Receipts{}
|
||||
for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
|
||||
block := pm.blockchain.GetBlockByNumber(i, access.NoOdr)
|
||||
|
||||
hashes = append(hashes, block.Hash())
|
||||
receipts = append(receipts, core.GetBlockReceipts(pm.chainAccess, block.Hash(), access.NoOdr))
|
||||
}
|
||||
// Send the hash request and verify the response
|
||||
p2p.Send(peer.app, GetReceiptsMsg, hashes)
|
||||
if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, receipts); err != nil {
|
||||
t.Errorf("receipts mismatch: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that trie merkle proofs can be retrieved
|
||||
func TestGetProofsLes1(t *testing.T) { testGetReceipt(t, 1) }
|
||||
|
||||
func testGetProofs(t *testing.T, protocol int) {
|
||||
// Define three accounts to simulate transactions with
|
||||
acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||
acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
|
||||
acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey)
|
||||
acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey)
|
||||
|
||||
// Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_makerts_test)
|
||||
generator := func(i int, block *core.BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
// In block 1, the test bank sends account #1 some ether.
|
||||
tx, _ := types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil).SignECDSA(testBankKey)
|
||||
block.AddTx(tx)
|
||||
case 1:
|
||||
// In block 2, the test bank sends some more ether to account #1.
|
||||
// acc1Addr passes it on to account #2.
|
||||
tx1, _ := types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(testBankKey)
|
||||
tx2, _ := types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(acc1Key)
|
||||
block.AddTx(tx1)
|
||||
block.AddTx(tx2)
|
||||
case 2:
|
||||
// Block 3 is empty but was mined by account #2.
|
||||
block.SetCoinbase(acc2Addr)
|
||||
block.SetExtra([]byte("yeehaw"))
|
||||
case 3:
|
||||
// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
|
||||
b2 := block.PrevBlock(1).Header()
|
||||
b2.Extra = []byte("foo")
|
||||
block.AddUncle(b2)
|
||||
b3 := block.PrevBlock(2).Header()
|
||||
b3.Extra = []byte("foo")
|
||||
block.AddUncle(b3)
|
||||
}
|
||||
}
|
||||
// Assemble the test environment
|
||||
pm, ca := newTestProtocolManagerMust(t, false, 4, generator)
|
||||
db := ca.Db()
|
||||
peer, _ := newTestPeer("peer", protocol, pm, true)
|
||||
defer peer.close()
|
||||
|
||||
var proofreqs []access.ProofReq
|
||||
var proofs []trie.MerkleProof
|
||||
|
||||
accounts := []common.Address{testBankAddress, acc1Addr, acc2Addr, common.Address{}}
|
||||
for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
|
||||
root := pm.blockchain.GetBlockByNumber(i, access.NoOdr).Root()
|
||||
trie, _ := trie.NewSecure(root, db, nil)
|
||||
|
||||
for _, acc := range accounts {
|
||||
req := access.ProofReq{
|
||||
Root: root,
|
||||
Key: acc[:],
|
||||
}
|
||||
proofreqs = append(proofreqs, req)
|
||||
|
||||
proof := trie.Prove(acc[:])
|
||||
proofs = append(proofs, proof)
|
||||
}
|
||||
}
|
||||
// Send the proof request and verify the response
|
||||
p2p.Send(peer.app, GetProofsMsg, proofreqs)
|
||||
if err := p2p.ExpectMsg(peer.app, ProofsMsg, proofs); err != nil {
|
||||
t.Errorf("receipts mismatch: %v", err)
|
||||
}
|
||||
}
|
||||
194
les/helper_test.go
Normal file
194
les/helper_test.go
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
// This file contains some shares testing functionality, common to multiple
|
||||
// different files and modules being tested.
|
||||
|
||||
package les
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/access"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
)
|
||||
|
||||
var (
|
||||
testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
|
||||
testBankFunds = big.NewInt(1000000)
|
||||
)
|
||||
|
||||
// newTestProtocolManager creates a new protocol manager for testing purposes,
|
||||
// with the given number of blocks already known, and potential notification
|
||||
// channels for different events.
|
||||
func newTestProtocolManager(mode Mode, blocks int, generator func(int, *core.BlockGen)) (*ProtocolManager, *access.ChainAccess, error) {
|
||||
var (
|
||||
evmux = new(event.TypeMux)
|
||||
pow = new(core.FakePow)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
ca = access.NewChainAccess(db, mode == LightMode)
|
||||
genesis = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{testBankAddress, testBankFunds})
|
||||
blockchain, _ = core.NewBlockChain(ca, pow, evmux)
|
||||
blockproc = core.NewBlockProcessor(ca, pow, blockchain, evmux)
|
||||
)
|
||||
blockchain.SetProcessor(blockproc)
|
||||
chain, _ := core.GenerateChain(genesis, db, blocks, generator)
|
||||
if _, err := blockchain.InsertChain(chain); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pm, err := NewProtocolManager(mode, NetworkId, evmux, pow, blockchain, ca)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pm.Start()
|
||||
return pm, ca, nil
|
||||
}
|
||||
|
||||
// newTestProtocolManagerMust creates a new protocol manager for testing purposes,
|
||||
// with the given number of blocks already known, and potential notification
|
||||
// channels for different events. In case of an error, the constructor force-
|
||||
// fails the test.
|
||||
func newTestProtocolManagerMust(t *testing.T, lightSync bool, blocks int, generator func(int, *core.BlockGen)) (*ProtocolManager, *access.ChainAccess) {
|
||||
mode := ArchiveMode
|
||||
if lightSync {
|
||||
mode = LightMode
|
||||
}
|
||||
pm, ca, err := newTestProtocolManager(mode, blocks, generator)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create protocol manager: %v", err)
|
||||
}
|
||||
return pm, ca
|
||||
}
|
||||
|
||||
// testTxPool is a fake, helper transaction pool for testing purposes
|
||||
type testTxPool struct {
|
||||
pool []*types.Transaction // Collection of all transactions
|
||||
added chan<- []*types.Transaction // Notification channel for new transactions
|
||||
|
||||
lock sync.RWMutex // Protects the transaction pool
|
||||
}
|
||||
|
||||
// AddTransactions appends a batch of transactions to the pool, and notifies any
|
||||
// listeners if the addition channel is non nil
|
||||
func (p *testTxPool) AddTransactions(txs []*types.Transaction) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
p.pool = append(p.pool, txs...)
|
||||
if p.added != nil {
|
||||
p.added <- txs
|
||||
}
|
||||
}
|
||||
|
||||
// GetTransactions returns all the transactions known to the pool
|
||||
func (p *testTxPool) GetTransactions() types.Transactions {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
|
||||
txs := make([]*types.Transaction, len(p.pool))
|
||||
copy(txs, p.pool)
|
||||
|
||||
return txs
|
||||
}
|
||||
|
||||
// newTestTransaction create a new dummy transaction.
|
||||
func newTestTransaction(from *crypto.Key, nonce uint64, datasize int) *types.Transaction {
|
||||
tx := types.NewTransaction(nonce, common.Address{}, big.NewInt(0), big.NewInt(100000), big.NewInt(0), make([]byte, datasize))
|
||||
tx, _ = tx.SignECDSA(from.PrivateKey)
|
||||
|
||||
return tx
|
||||
}
|
||||
|
||||
// testPeer is a simulated peer to allow testing direct network calls.
|
||||
type testPeer struct {
|
||||
net p2p.MsgReadWriter // Network layer reader/writer to simulate remote messaging
|
||||
app *p2p.MsgPipeRW // Application layer reader/writer to simulate the local side
|
||||
*peer
|
||||
}
|
||||
|
||||
// newTestPeer creates a new peer registered at the given protocol manager.
|
||||
func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*testPeer, <-chan error) {
|
||||
// Create a message pipe to communicate through
|
||||
app, net := p2p.MsgPipe()
|
||||
|
||||
// Generate a random id and create the peer
|
||||
var id discover.NodeID
|
||||
rand.Read(id[:])
|
||||
|
||||
peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)
|
||||
|
||||
// Start the peer on a new thread
|
||||
errc := make(chan error, 1)
|
||||
go func() {
|
||||
pm.newPeerCh <- peer
|
||||
errc <- pm.handle(peer)
|
||||
}()
|
||||
tp := &testPeer{
|
||||
app: app,
|
||||
net: net,
|
||||
peer: peer,
|
||||
}
|
||||
// Execute any implicitly requested handshakes and return
|
||||
if shake {
|
||||
td, head, genesis := pm.blockchain.Status()
|
||||
tp.handshake(nil, td, head, genesis)
|
||||
}
|
||||
return tp, errc
|
||||
}
|
||||
|
||||
func newTestPeerPair(name string, version int, pm, pm2 *ProtocolManager) (*peer, <-chan error, *peer, <-chan error) {
|
||||
// Create a message pipe to communicate through
|
||||
app, net := p2p.MsgPipe()
|
||||
|
||||
// Generate a random id and create the peer
|
||||
var id discover.NodeID
|
||||
rand.Read(id[:])
|
||||
|
||||
peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)
|
||||
peer2 := pm2.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), app)
|
||||
|
||||
// Start the peer on a new thread
|
||||
errc := make(chan error, 1)
|
||||
errc2 := make(chan error, 1)
|
||||
go func() {
|
||||
pm.newPeerCh <- peer
|
||||
errc <- pm.handle(peer)
|
||||
}()
|
||||
go func() {
|
||||
pm2.newPeerCh <- peer2
|
||||
errc2 <- pm2.handle(peer2)
|
||||
}()
|
||||
return peer, errc, peer2, errc2
|
||||
}
|
||||
|
||||
// handshake simulates a trivial handshake that expects the same state from the
|
||||
// remote side as we are simulating locally.
|
||||
func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, genesis common.Hash) {
|
||||
msg := &statusData{
|
||||
ProtocolVersion: uint32(p.version),
|
||||
NetworkId: uint32(NetworkId),
|
||||
TD: td,
|
||||
CurrentBlock: head,
|
||||
GenesisBlock: genesis,
|
||||
}
|
||||
if err := p2p.ExpectMsg(p.app, StatusMsg, msg); err != nil {
|
||||
t.Fatalf("status recv: %v", err)
|
||||
}
|
||||
if err := p2p.Send(p.app, StatusMsg, msg); err != nil {
|
||||
t.Fatalf("status send: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// close terminates the local side of the peer, notifying the remote protocol
|
||||
// manager of termination.
|
||||
func (p *testPeer) close() {
|
||||
p.app.Close()
|
||||
}
|
||||
140
les/odr_test.go
Normal file
140
les/odr_test.go
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
package les
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/access"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
type odrTestFn func(ca *access.ChainAccess, bc *core.BlockChain, ctx *access.OdrContext, bhash common.Hash) []byte
|
||||
|
||||
func TestOdrGetBlockLes1(t *testing.T) { testOdr(t, 1, odrGetBlock) }
|
||||
|
||||
func odrGetBlock(ca *access.ChainAccess, bc *core.BlockChain, ctx *access.OdrContext, bhash common.Hash) []byte {
|
||||
block := bc.GetBlock(bhash, ctx)
|
||||
if block == nil {
|
||||
return nil
|
||||
}
|
||||
rlp, _ := rlp.EncodeToBytes(block)
|
||||
return rlp
|
||||
}
|
||||
|
||||
func TestOdrGetReceiptsLes1(t *testing.T) { testOdr(t, 1, odrGetReceipts) }
|
||||
|
||||
func odrGetReceipts(ca *access.ChainAccess, bc *core.BlockChain, ctx *access.OdrContext, bhash common.Hash) []byte {
|
||||
receipts := core.GetBlockReceipts(ca, bhash, ctx)
|
||||
if receipts == nil {
|
||||
return nil
|
||||
}
|
||||
rlp, _ := rlp.EncodeToBytes(receipts)
|
||||
return rlp
|
||||
}
|
||||
|
||||
func TestOdrAccountsLes1(t *testing.T) { testOdr(t, 1, odrAccounts) }
|
||||
|
||||
func odrAccounts(ca *access.ChainAccess, bc *core.BlockChain, ctx *access.OdrContext, bhash common.Hash) []byte {
|
||||
acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||
acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
|
||||
acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey)
|
||||
acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey)
|
||||
dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678")
|
||||
acc := []common.Address{ testBankAddress, acc1Addr, acc2Addr, dummyAddr}
|
||||
|
||||
trie.ClearGlobalCache()
|
||||
|
||||
var res []byte
|
||||
for _, addr := range acc {
|
||||
header := bc.GetHeader(bhash)
|
||||
st, err := state.New(header.Root, ca, ctx)
|
||||
if err == nil {
|
||||
bal := st.GetBalance(addr)
|
||||
rlp, _ := rlp.EncodeToBytes(bal)
|
||||
res = append(res, rlp...)
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func testOdr(t *testing.T, protocol int, fn odrTestFn) {
|
||||
// Define accounts to simulate transactions with
|
||||
acc1Key, _ := crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||
acc2Key, _ := crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
|
||||
acc1Addr := crypto.PubkeyToAddress(acc1Key.PublicKey)
|
||||
acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey)
|
||||
|
||||
// Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_makerts_test)
|
||||
generator := func(i int, block *core.BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
// In block 1, the test bank sends account #1 some ether.
|
||||
tx, _ := types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil).SignECDSA(testBankKey)
|
||||
block.AddTx(tx)
|
||||
case 1:
|
||||
// In block 2, the test bank sends some more ether to account #1.
|
||||
// acc1Addr passes it on to account #2.
|
||||
tx1, _ := types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(testBankKey)
|
||||
tx2, _ := types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(acc1Key)
|
||||
block.AddTx(tx1)
|
||||
block.AddTx(tx2)
|
||||
case 2:
|
||||
// Block 3 is empty but was mined by account #2.
|
||||
block.SetCoinbase(acc2Addr)
|
||||
block.SetExtra([]byte("yeehaw"))
|
||||
case 3:
|
||||
// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
|
||||
b2 := block.PrevBlock(1).Header()
|
||||
b2.Extra = []byte("foo")
|
||||
block.AddUncle(b2)
|
||||
b3 := block.PrevBlock(2).Header()
|
||||
b3.Extra = []byte("foo")
|
||||
block.AddUncle(b3)
|
||||
}
|
||||
}
|
||||
// Assemble the test environment
|
||||
pm, ca := newTestProtocolManagerMust(t, false, 4, generator)
|
||||
lpm, lca := newTestProtocolManagerMust(t, true, 0, nil)
|
||||
_, _, lpeer, _ := newTestPeerPair("peer", protocol, pm, lpm)
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
lpm.synchronise(lpeer)
|
||||
|
||||
cid := access.NewChannelID(time.Millisecond * 200)
|
||||
|
||||
test := func(expFail uint64) {
|
||||
for i := uint64(0); i <= pm.blockchain.CurrentBlock().NumberU64(); i++ {
|
||||
bhash := core.GetCanonicalHash(ca.Db(), i)
|
||||
b1 := fn(ca, pm.blockchain, access.NoOdr, bhash)
|
||||
b2 := fn(lca, lpm.blockchain, access.NewContext(cid), bhash)
|
||||
eq := bytes.Equal(b1, b2)
|
||||
exp := i < expFail
|
||||
if exp && !eq {
|
||||
t.Errorf("odr mismatch")
|
||||
}
|
||||
if !exp && eq {
|
||||
t.Errorf("unexpected odr match")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// temporarily remove peer to test odr fails
|
||||
lca.UnregisterPeer(lpeer.id)
|
||||
// expect retrievals to fail (except genesis block) without a les peer
|
||||
test(1)
|
||||
lca.RegisterPeer(lpeer.id, lpeer.version, lpeer.Head(), lpeer.RequestBodies, lpeer.RequestNodeData, lpeer.RequestReceipts, lpeer.RequestProofs)
|
||||
// expect all retrievals to pass
|
||||
test(5)
|
||||
lca.UnregisterPeer(lpeer.id)
|
||||
// still expect all retrievals to pass, now data should be cached locally
|
||||
test(5)
|
||||
}
|
||||
|
|
@ -66,16 +66,16 @@ const (
|
|||
// Protocol messages belonging to LPV1
|
||||
StatusMsg = 0x00
|
||||
NewBlockHashesMsg = 0x01
|
||||
GetBlockHeadersMsg = 0x03
|
||||
BlockHeadersMsg = 0x04
|
||||
GetBlockBodiesMsg = 0x05
|
||||
BlockBodiesMsg = 0x06
|
||||
GetNodeDataMsg = 0x0d
|
||||
NodeDataMsg = 0x0e
|
||||
GetReceiptsMsg = 0x0f
|
||||
ReceiptsMsg = 0x10
|
||||
GetProofsMsg = 0x11
|
||||
ProofsMsg = 0x12
|
||||
GetBlockHeadersMsg = 0x02
|
||||
BlockHeadersMsg = 0x03
|
||||
GetBlockBodiesMsg = 0x04
|
||||
BlockBodiesMsg = 0x05
|
||||
GetNodeDataMsg = 0x06
|
||||
NodeDataMsg = 0x07
|
||||
GetReceiptsMsg = 0x08
|
||||
ReceiptsMsg = 0x09
|
||||
GetProofsMsg = 0x0a
|
||||
ProofsMsg = 0x0b
|
||||
)
|
||||
|
||||
type errCode int
|
||||
|
|
|
|||
11
trie/arc.go
11
trie/arc.go
|
|
@ -62,6 +62,17 @@ func newARC(c int) *arc {
|
|||
}
|
||||
}
|
||||
|
||||
func (a *arc) Clear() {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
a.p = 0
|
||||
a.t1 = list.New()
|
||||
a.b1 = list.New()
|
||||
a.t2 = list.New()
|
||||
a.b2 = list.New()
|
||||
a.cache = make(map[string]*entry, a.c)
|
||||
}
|
||||
|
||||
// Put inserts a new key-value pair into the cache.
|
||||
// This optimizes future access to this entry (side effect).
|
||||
func (a *arc) Put(key hashNode, value node) bool {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,10 @@ var (
|
|||
emptyState = crypto.Sha3Hash(nil)
|
||||
)
|
||||
|
||||
func ClearGlobalCache() {
|
||||
globalCache.Clear()
|
||||
}
|
||||
|
||||
var ErrMissingRoot = errors.New("missing root node")
|
||||
var ErrTrieOdrFailure = errors.New("can't commit to db because of ODR failure")
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue