fixed failing tests, added new ones

This commit is contained in:
zsfelfoldi 2015-10-24 04:13:17 +02:00
parent a486c11fbe
commit 7a44bab77b
14 changed files with 879 additions and 99 deletions

View file

@ -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),

View file

@ -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) {

View file

@ -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 {

View file

@ -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()

View file

@ -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)))

View file

@ -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
} }

View file

@ -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"

View file

@ -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
View 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
View 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
View 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)
}

View file

@ -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

View file

@ -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 {

View file

@ -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")