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",
|
Name: "natspec",
|
||||||
Usage: "Enable NatSpec confirmation notice",
|
Usage: "Enable NatSpec confirmation notice",
|
||||||
}
|
}
|
||||||
DocRootFlag = DirectoryFlag{
|
|
||||||
Name: "docroot",
|
|
||||||
Usage: "Document Root for HTTPClient file scheme",
|
|
||||||
Value: DirectoryString{common.HomeDir()},
|
|
||||||
}
|
|
||||||
CacheFlag = cli.IntFlag{
|
CacheFlag = cli.IntFlag{
|
||||||
Name: "cache",
|
Name: "cache",
|
||||||
Usage: "Megabytes of memory allocated to internal caching (min 16MB / database forced)",
|
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),
|
Olympic: ctx.GlobalBool(OlympicFlag.Name),
|
||||||
NAT: MakeNAT(ctx),
|
NAT: MakeNAT(ctx),
|
||||||
NatSpec: ctx.GlobalBool(NatspecEnabledFlag.Name),
|
NatSpec: ctx.GlobalBool(NatspecEnabledFlag.Name),
|
||||||
DocRoot: ctx.GlobalString(DocRootFlag.Name),
|
|
||||||
Discovery: !ctx.GlobalBool(NoDiscoverFlag.Name),
|
Discovery: !ctx.GlobalBool(NoDiscoverFlag.Name),
|
||||||
NodeKey: MakeNodeKey(ctx),
|
NodeKey: MakeNodeKey(ctx),
|
||||||
Shh: ctx.GlobalBool(WhisperEnabledFlag.Name),
|
Shh: ctx.GlobalBool(WhisperEnabledFlag.Name),
|
||||||
|
|
|
||||||
|
|
@ -30,35 +30,48 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrCancel = errors.New("ODR cancelled")
|
ErrCancel = errors.New("ODR cancelled")
|
||||||
errNotInDb = errors.New("object not found in database")
|
errNotInDb = errors.New("object not found in database")
|
||||||
)
|
)
|
||||||
|
|
||||||
const LogLevel = logger.Info
|
const LogLevel = logger.Debug
|
||||||
|
|
||||||
var (
|
var (
|
||||||
requestTimeout = time.Millisecond * 300
|
requestTimeout = time.Millisecond * 300
|
||||||
retryPeers = time.Second * 1
|
retryPeers = time.Second * 1
|
||||||
)
|
)
|
||||||
|
|
||||||
type ChainAccess struct {
|
type ChainAccess struct {
|
||||||
db ethdb.Database
|
db ethdb.Database
|
||||||
odr bool // light client mode, odr enabled
|
odr bool // light client mode, odr enabled
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
valFunc validatorFunc
|
sentReqs map[uint64]*sentReq
|
||||||
deliverChan chan *Msg
|
sentReqCnt uint64
|
||||||
peers *peerSet
|
peers *peerSet
|
||||||
|
|
||||||
// p2p access objects
|
// p2p access objects
|
||||||
// parameters (light/full/archive)
|
// 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 {
|
func NewDbChainAccess(db ethdb.Database) *ChainAccess {
|
||||||
return NewChainAccess(db, false)
|
return NewChainAccess(db, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewChainAccess(db ethdb.Database, odr bool) *ChainAccess {
|
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 {
|
func (self *ChainAccess) Db() ethdb.Database {
|
||||||
|
|
@ -103,63 +116,63 @@ type ObjectAccess interface {
|
||||||
Valid(*Msg) bool // if true, keeps the retrieved object
|
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) {
|
func (self *ChainAccess) Deliver(id string, msg *Msg) (processed bool) {
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
valFunc := self.valFunc
|
defer self.lock.Unlock()
|
||||||
chn := self.deliverChan
|
|
||||||
self.lock.Unlock()
|
for i, req := range self.sentReqs {
|
||||||
if (valFunc != nil) && (chn != nil) && valFunc(msg) {
|
if req.valFunc(msg) {
|
||||||
chn <- msg
|
req.deliverChan <- msg
|
||||||
return true
|
delete(self.sentReqs, i)
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ChainAccess) networkRequest(rqFunc requestFunc, valFunc validatorFunc, ctx *OdrContext) (*Msg, error) {
|
func (self *ChainAccess) networkRequest(rqFunc requestFunc, valFunc validatorFunc, ctx *OdrContext) (*Msg, error) {
|
||||||
|
req := &sentReq{
|
||||||
|
deliverChan: make(chan *Msg),
|
||||||
|
valFunc: valFunc,
|
||||||
|
}
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
self.deliverChan = make(chan *Msg)
|
reqCnt := self.sentReqCnt
|
||||||
self.valFunc = valFunc
|
self.sentReqCnt++
|
||||||
|
self.sentReqs[reqCnt] = req
|
||||||
self.lock.Unlock()
|
self.lock.Unlock()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
self.deliverChan = nil
|
delete(self.sentReqs, reqCnt)
|
||||||
self.valFunc = nil
|
|
||||||
self.lock.Unlock()
|
self.lock.Unlock()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
//fmt.Println("networkRequest")
|
|
||||||
|
|
||||||
var msg *Msg
|
var msg *Msg
|
||||||
|
|
||||||
for {
|
for {
|
||||||
peers := self.peers.BestPeers()
|
peers := self.peers.BestPeers()
|
||||||
if len(peers) == 0 {
|
if len(peers) == 0 {
|
||||||
select {
|
select {
|
||||||
case <-ctx.cancelOrTimeout:
|
case <-ctx.cancelOrTimeout:
|
||||||
return nil, ErrCancel
|
return nil, ErrCancel
|
||||||
case <-time.After(retryPeers):
|
case <-time.After(retryPeers):
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
for _, peer := range peers {
|
||||||
for _, peer := range peers {
|
rqFunc(peer)
|
||||||
rqFunc(peer)
|
select {
|
||||||
select {
|
case <-ctx.cancelOrTimeout:
|
||||||
case <-ctx.cancelOrTimeout:
|
return nil, ErrCancel
|
||||||
return nil, ErrCancel
|
case msg = <-req.deliverChan:
|
||||||
case msg = <-self.deliverChan:
|
peer.Promote()
|
||||||
peer.Promote()
|
glog.V(LogLevel).Infof("networkRequest success")
|
||||||
glog.V(LogLevel).Infof("networkRequest success")
|
return msg, nil
|
||||||
return msg, nil
|
case <-time.After(requestTimeout):
|
||||||
case <-time.After(requestTimeout):
|
peer.Demote()
|
||||||
peer.Demote()
|
glog.V(LogLevel).Infof("networkRequest timeout")
|
||||||
glog.V(LogLevel).Infof("networkRequest timeout")
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ChainAccess) Retrieve(obj ObjectAccess, ctx *OdrContext) (err error) {
|
func (self *ChainAccess) Retrieve(obj ObjectAccess, ctx *OdrContext) (err error) {
|
||||||
|
|
|
||||||
|
|
@ -24,19 +24,19 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type OdrContext struct {
|
type OdrContext struct {
|
||||||
cancel, cancelOrTimeout chan struct{}
|
cancel, cancelOrTimeout chan struct{}
|
||||||
id *OdrChannelID
|
id *OdrChannelID
|
||||||
cancelled int32
|
cancelled int32
|
||||||
}
|
}
|
||||||
|
|
||||||
var NullCtx = (*OdrContext)(nil) // used when creating states
|
var NullCtx = (*OdrContext)(nil) // used when creating states
|
||||||
var NoOdr = (*OdrContext)(nil) // used for individual requests
|
var NoOdr = (*OdrContext)(nil) // used for individual requests
|
||||||
|
|
||||||
func NewContext(id *OdrChannelID) *OdrContext {
|
func NewContext(id *OdrChannelID) *OdrContext {
|
||||||
ctx := &OdrContext{
|
ctx := &OdrContext{
|
||||||
cancel: make(chan struct{}),
|
cancel: make(chan struct{}),
|
||||||
cancelOrTimeout: make(chan struct{}),
|
cancelOrTimeout: make(chan struct{}),
|
||||||
id: id,
|
id: id,
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
select {
|
select {
|
||||||
|
|
|
||||||
|
|
@ -274,9 +274,6 @@ func (bc *BlockChain) SetHead(head uint64) {
|
||||||
if bc.currentBlock == nil {
|
if bc.currentBlock == nil {
|
||||||
bc.currentBlock = bc.genesisBlock
|
bc.currentBlock = bc.genesisBlock
|
||||||
}
|
}
|
||||||
bc.insert(bc.currentBlock)
|
|
||||||
} else {
|
|
||||||
bc.writeHeader(bc.currentHeader)
|
|
||||||
}
|
}
|
||||||
if bc.currentHeader == nil {
|
if bc.currentHeader == nil {
|
||||||
bc.currentHeader = bc.genesisBlock.Header()
|
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
|
// Import the chain as an archive node and ensure all pointers are updated
|
||||||
archiveDb, _ := ethdb.NewMemDatabase()
|
archiveDb, _ := ethdb.NewMemDatabase()
|
||||||
archiveCa := access.NewDbChainAccess(archiveDb)
|
archiveCa := access.NewDbChainAccess(archiveDb)
|
||||||
|
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
|
||||||
archive, _ := NewBlockChain(archiveCa, FakePow{}, new(event.TypeMux))
|
archive, _ := NewBlockChain(archiveCa, FakePow{}, new(event.TypeMux))
|
||||||
archive.SetProcessor(NewBlockProcessor(archiveCa, FakePow{}, archive, 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/common"
|
||||||
"github.com/ethereum/go-ethereum/core/access"
|
"github.com/ethereum/go-ethereum/core/access"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"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))
|
glog.V(access.LogLevel).Infof("ODR: invalid number of entries: %d", len(receipts))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
data, err := rlp.EncodeToBytes(receipts[0])
|
hash := types.DeriveSha(receipts[0])
|
||||||
if err != nil {
|
|
||||||
glog.V(access.LogLevel).Infof("ODR: RLP encode error: %v", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
hash := crypto.Sha3Hash(data)
|
|
||||||
header := GetHeader(self.db, self.blockHash)
|
header := GetHeader(self.db, self.blockHash)
|
||||||
if header == nil {
|
if header == nil {
|
||||||
glog.V(access.LogLevel).Infof("ODR: header not found for block %08x", self.blockHash[:4])
|
glog.V(access.LogLevel).Infof("ODR: header not found for block %08x", self.blockHash[:4])
|
||||||
return false
|
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])
|
glog.V(access.LogLevel).Infof("ODR: header receipts hash %08x does not match calculated RLP hash %08x", header.ReceiptHash[:4], hash[:4])
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
//"fmt"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/access"
|
"github.com/ethereum/go-ethereum/core/access"
|
||||||
|
|
|
||||||
|
|
@ -241,14 +241,14 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
defer msg.Discard()
|
defer msg.Discard()
|
||||||
|
|
||||||
// Handle the message depending on its contents
|
// Handle the message depending on its contents
|
||||||
switch {
|
switch msg.Code {
|
||||||
case msg.Code == StatusMsg:
|
case StatusMsg:
|
||||||
glog.V(access.LogLevel).Infof("LES: received StatusMsg from peer %v", p.id)
|
glog.V(access.LogLevel).Infof("LES: received StatusMsg from peer %v", p.id)
|
||||||
// Status messages should never arrive after the handshake
|
// Status messages should never arrive after the handshake
|
||||||
return errResp(ErrExtraStatusMsg, "uncontrolled status message")
|
return errResp(ErrExtraStatusMsg, "uncontrolled status message")
|
||||||
|
|
||||||
// Block header query, collect the requested headers and reply
|
// 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)
|
glog.V(access.LogLevel).Infof("LES: received GetBlockHeadersMsg from peer %v", p.id)
|
||||||
// Decode the complex header query
|
// Decode the complex header query
|
||||||
var query getBlockHeadersData
|
var query getBlockHeadersData
|
||||||
|
|
@ -313,7 +313,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
}
|
}
|
||||||
return p.SendBlockHeaders(headers)
|
return p.SendBlockHeaders(headers)
|
||||||
|
|
||||||
case msg.Code == BlockHeadersMsg:
|
case BlockHeadersMsg:
|
||||||
glog.V(access.LogLevel).Infof("LES: received BlockHeadersMsg from peer %v", p.id)
|
glog.V(access.LogLevel).Infof("LES: received BlockHeadersMsg from peer %v", p.id)
|
||||||
// A batch of headers arrived to one of our previous requests
|
// A batch of headers arrived to one of our previous requests
|
||||||
var headers []*types.Header
|
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)
|
glog.V(access.LogLevel).Infof("LES: received GetBlockBodiesMsg from peer %v", p.id)
|
||||||
// Decode the retrieval message
|
// Decode the retrieval message
|
||||||
msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
|
msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
|
||||||
|
|
@ -360,7 +360,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
}
|
}
|
||||||
return p.SendBlockBodiesRLP(bodies)
|
return p.SendBlockBodiesRLP(bodies)
|
||||||
|
|
||||||
case msg.Code == BlockBodiesMsg:
|
case BlockBodiesMsg:
|
||||||
glog.V(access.LogLevel).Infof("LES: received BlockBodiesMsg from peer %v", p.id)
|
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
|
// A batch of block bodies arrived to one of our previous requests
|
||||||
var data []*types.Body
|
var data []*types.Body
|
||||||
|
|
@ -372,7 +372,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
Obj: data,
|
Obj: data,
|
||||||
})
|
})
|
||||||
|
|
||||||
case msg.Code == GetNodeDataMsg:
|
case GetNodeDataMsg:
|
||||||
glog.V(access.LogLevel).Infof("LES: received GetNodeDataMsg from peer %v", p.id)
|
glog.V(access.LogLevel).Infof("LES: received GetNodeDataMsg from peer %v", p.id)
|
||||||
// Decode the retrieval message
|
// Decode the retrieval message
|
||||||
msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
|
msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
|
||||||
|
|
@ -400,7 +400,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
}
|
}
|
||||||
return p.SendNodeData(data)
|
return p.SendNodeData(data)
|
||||||
|
|
||||||
case msg.Code == NodeDataMsg:
|
case NodeDataMsg:
|
||||||
glog.V(access.LogLevel).Infof("LES: received NodeDataMsg from peer %v", p.id)
|
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
|
// A batch of node state data arrived to one of our previous requests
|
||||||
var data [][]byte
|
var data [][]byte
|
||||||
|
|
@ -412,7 +412,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
Obj: data,
|
Obj: data,
|
||||||
})
|
})
|
||||||
|
|
||||||
case msg.Code == GetReceiptsMsg:
|
case GetReceiptsMsg:
|
||||||
glog.V(access.LogLevel).Infof("LES: received GetReceiptsMsg from peer %v", p.id)
|
glog.V(access.LogLevel).Infof("LES: received GetReceiptsMsg from peer %v", p.id)
|
||||||
// Decode the retrieval message
|
// Decode the retrieval message
|
||||||
msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
|
msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
|
||||||
|
|
@ -449,19 +449,19 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
}
|
}
|
||||||
return p.SendReceiptsRLP(receipts)
|
return p.SendReceiptsRLP(receipts)
|
||||||
|
|
||||||
case msg.Code == ReceiptsMsg:
|
case ReceiptsMsg:
|
||||||
glog.V(access.LogLevel).Infof("LES: received ReceiptsMsg from peer %v", p.id)
|
glog.V(access.LogLevel).Infof("LES: received ReceiptsMsg from peer %v", p.id)
|
||||||
// A batch of receipts arrived to one of our previous requests
|
// 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 {
|
if err := msg.Decode(&receipts); err != nil {
|
||||||
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
||||||
}
|
}
|
||||||
pm.chainAccess.Deliver(p.id, &access.Msg{
|
pm.chainAccess.Deliver(p.id, &access.Msg{
|
||||||
MsgType: access.MsgReceipts,
|
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)
|
glog.V(access.LogLevel).Infof("LES: received GetProofsMsg from peer %v", p.id)
|
||||||
// Decode the retrieval message
|
// Decode the retrieval message
|
||||||
msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
|
msgStream := rlp.NewStream(msg.Payload, uint64(msg.Size))
|
||||||
|
|
@ -490,7 +490,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
}
|
}
|
||||||
return p.SendProofs(proofs)
|
return p.SendProofs(proofs)
|
||||||
|
|
||||||
case msg.Code == ProofsMsg:
|
case ProofsMsg:
|
||||||
glog.V(access.LogLevel).Infof("LES: received ProofsMsg from peer %v", p.id)
|
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
|
// A batch of merkle proofs arrived to one of our previous requests
|
||||||
var data []trie.MerkleProof
|
var data []trie.MerkleProof
|
||||||
|
|
@ -502,7 +502,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
Obj: data,
|
Obj: data,
|
||||||
})
|
})
|
||||||
|
|
||||||
case msg.Code == NewBlockHashesMsg:
|
case NewBlockHashesMsg:
|
||||||
glog.V(access.LogLevel).Infof("LES: received NewBlockHashesMsg from peer %v", p.id)
|
glog.V(access.LogLevel).Infof("LES: received NewBlockHashesMsg from peer %v", p.id)
|
||||||
// Retrieve and deseralize the remote new block hashes notification
|
// Retrieve and deseralize the remote new block hashes notification
|
||||||
type announce struct {
|
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
|
// Protocol messages belonging to LPV1
|
||||||
StatusMsg = 0x00
|
StatusMsg = 0x00
|
||||||
NewBlockHashesMsg = 0x01
|
NewBlockHashesMsg = 0x01
|
||||||
GetBlockHeadersMsg = 0x03
|
GetBlockHeadersMsg = 0x02
|
||||||
BlockHeadersMsg = 0x04
|
BlockHeadersMsg = 0x03
|
||||||
GetBlockBodiesMsg = 0x05
|
GetBlockBodiesMsg = 0x04
|
||||||
BlockBodiesMsg = 0x06
|
BlockBodiesMsg = 0x05
|
||||||
GetNodeDataMsg = 0x0d
|
GetNodeDataMsg = 0x06
|
||||||
NodeDataMsg = 0x0e
|
NodeDataMsg = 0x07
|
||||||
GetReceiptsMsg = 0x0f
|
GetReceiptsMsg = 0x08
|
||||||
ReceiptsMsg = 0x10
|
ReceiptsMsg = 0x09
|
||||||
GetProofsMsg = 0x11
|
GetProofsMsg = 0x0a
|
||||||
ProofsMsg = 0x12
|
ProofsMsg = 0x0b
|
||||||
)
|
)
|
||||||
|
|
||||||
type errCode int
|
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.
|
// Put inserts a new key-value pair into the cache.
|
||||||
// This optimizes future access to this entry (side effect).
|
// This optimizes future access to this entry (side effect).
|
||||||
func (a *arc) Put(key hashNode, value node) bool {
|
func (a *arc) Put(key hashNode, value node) bool {
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,10 @@ var (
|
||||||
emptyState = crypto.Sha3Hash(nil)
|
emptyState = crypto.Sha3Hash(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func ClearGlobalCache() {
|
||||||
|
globalCache.Clear()
|
||||||
|
}
|
||||||
|
|
||||||
var ErrMissingRoot = errors.New("missing root node")
|
var ErrMissingRoot = errors.New("missing root node")
|
||||||
var ErrTrieOdrFailure = errors.New("can't commit to db because of ODR failure")
|
var ErrTrieOdrFailure = errors.New("can't commit to db because of ODR failure")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue