mirror of https://github.com/ethereum/go-ethereum
les: implement ultralight client (#16904)
For more information about this light client mode, read https://hackmd.io/s/HJy7jjZpmpull/18515/head
parent
b8f9b3779f
commit
769657060e
@ -0,0 +1,9 @@ |
||||
package eth |
||||
|
||||
const DefaultULCMinTrustedFraction = 75 |
||||
|
||||
// ULCConfig is a Ultra Light client options.
|
||||
type ULCConfig struct { |
||||
TrustedServers []string `toml:",omitempty"` // A list of trusted servers
|
||||
MinTrustedFraction int `toml:",omitempty"` // Minimum percentage of connected trusted servers to validate trusted (1-100)
|
||||
} |
@ -0,0 +1,155 @@ |
||||
package les |
||||
|
||||
import ( |
||||
"math/big" |
||||
"testing" |
||||
|
||||
"net" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/core/types" |
||||
"github.com/ethereum/go-ethereum/crypto" |
||||
"github.com/ethereum/go-ethereum/p2p" |
||||
"github.com/ethereum/go-ethereum/p2p/enode" |
||||
) |
||||
|
||||
func TestFetcherULCPeerSelector(t *testing.T) { |
||||
|
||||
var ( |
||||
id1 enode.ID = newNodeID(t).ID() |
||||
id2 enode.ID = newNodeID(t).ID() |
||||
id3 enode.ID = newNodeID(t).ID() |
||||
id4 enode.ID = newNodeID(t).ID() |
||||
) |
||||
|
||||
ftn1 := &fetcherTreeNode{ |
||||
hash: common.HexToHash("1"), |
||||
td: big.NewInt(1), |
||||
} |
||||
ftn2 := &fetcherTreeNode{ |
||||
hash: common.HexToHash("2"), |
||||
td: big.NewInt(2), |
||||
parent: ftn1, |
||||
} |
||||
ftn3 := &fetcherTreeNode{ |
||||
hash: common.HexToHash("3"), |
||||
td: big.NewInt(3), |
||||
parent: ftn2, |
||||
} |
||||
lf := lightFetcher{ |
||||
pm: &ProtocolManager{ |
||||
ulc: &ulc{ |
||||
trustedKeys: map[string]struct{}{ |
||||
id1.String(): {}, |
||||
id2.String(): {}, |
||||
id3.String(): {}, |
||||
id4.String(): {}, |
||||
}, |
||||
minTrustedFraction: 70, |
||||
}, |
||||
}, |
||||
maxConfirmedTd: ftn1.td, |
||||
|
||||
peers: map[*peer]*fetcherPeerInfo{ |
||||
{ |
||||
id: "peer1", |
||||
Peer: p2p.NewPeer(id1, "peer1", []p2p.Cap{}), |
||||
isTrusted: true, |
||||
}: { |
||||
nodeByHash: map[common.Hash]*fetcherTreeNode{ |
||||
ftn1.hash: ftn1, |
||||
ftn2.hash: ftn2, |
||||
}, |
||||
}, |
||||
{ |
||||
Peer: p2p.NewPeer(id2, "peer2", []p2p.Cap{}), |
||||
id: "peer2", |
||||
isTrusted: true, |
||||
}: { |
||||
nodeByHash: map[common.Hash]*fetcherTreeNode{ |
||||
ftn1.hash: ftn1, |
||||
ftn2.hash: ftn2, |
||||
}, |
||||
}, |
||||
{ |
||||
id: "peer3", |
||||
Peer: p2p.NewPeer(id3, "peer3", []p2p.Cap{}), |
||||
isTrusted: true, |
||||
}: { |
||||
nodeByHash: map[common.Hash]*fetcherTreeNode{ |
||||
ftn1.hash: ftn1, |
||||
ftn2.hash: ftn2, |
||||
ftn3.hash: ftn3, |
||||
}, |
||||
}, |
||||
{ |
||||
id: "peer4", |
||||
Peer: p2p.NewPeer(id4, "peer4", []p2p.Cap{}), |
||||
isTrusted: true, |
||||
}: { |
||||
nodeByHash: map[common.Hash]*fetcherTreeNode{ |
||||
ftn1.hash: ftn1, |
||||
}, |
||||
}, |
||||
}, |
||||
chain: &lightChainStub{ |
||||
tds: map[common.Hash]*big.Int{}, |
||||
headers: map[common.Hash]*types.Header{ |
||||
ftn1.hash: {}, |
||||
ftn2.hash: {}, |
||||
ftn3.hash: {}, |
||||
}, |
||||
}, |
||||
} |
||||
bestHash, bestAmount, bestTD, sync := lf.findBestRequest() |
||||
|
||||
if bestTD == nil { |
||||
t.Fatal("Empty result") |
||||
} |
||||
|
||||
if bestTD.Cmp(ftn2.td) != 0 { |
||||
t.Fatal("bad td", bestTD) |
||||
} |
||||
if bestHash != ftn2.hash { |
||||
t.Fatal("bad hash", bestTD) |
||||
} |
||||
|
||||
_, _ = bestAmount, sync |
||||
} |
||||
|
||||
type lightChainStub struct { |
||||
BlockChain |
||||
tds map[common.Hash]*big.Int |
||||
headers map[common.Hash]*types.Header |
||||
insertHeaderChainAssertFunc func(chain []*types.Header, checkFreq int) (int, error) |
||||
} |
||||
|
||||
func (l *lightChainStub) GetHeader(hash common.Hash, number uint64) *types.Header { |
||||
if h, ok := l.headers[hash]; ok { |
||||
return h |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (l *lightChainStub) LockChain() {} |
||||
func (l *lightChainStub) UnlockChain() {} |
||||
|
||||
func (l *lightChainStub) GetTd(hash common.Hash, number uint64) *big.Int { |
||||
if td, ok := l.tds[hash]; ok { |
||||
return td |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func (l *lightChainStub) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) { |
||||
return l.insertHeaderChainAssertFunc(chain, checkFreq) |
||||
} |
||||
|
||||
func newNodeID(t *testing.T) *enode.Node { |
||||
key, err := crypto.GenerateKey() |
||||
if err != nil { |
||||
t.Fatal("generate key err:", err) |
||||
} |
||||
return enode.NewV4(&key.PublicKey, net.IP{}, 35000, 35000) |
||||
} |
@ -0,0 +1,297 @@ |
||||
package les |
||||
|
||||
import ( |
||||
"math/big" |
||||
"testing" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/les/flowcontrol" |
||||
"github.com/ethereum/go-ethereum/p2p" |
||||
"github.com/ethereum/go-ethereum/p2p/enode" |
||||
"github.com/ethereum/go-ethereum/rlp" |
||||
) |
||||
|
||||
const ( |
||||
test_networkid = 10 |
||||
protocol_version = 2123 |
||||
) |
||||
|
||||
var ( |
||||
hash = common.HexToHash("some string") |
||||
genesis = common.HexToHash("genesis hash") |
||||
headNum = uint64(1234) |
||||
td = big.NewInt(123) |
||||
) |
||||
|
||||
//ulc connects to trusted peer and send announceType=announceTypeSigned
|
||||
func TestPeerHandshakeSetAnnounceTypeToAnnounceTypeSignedForTrustedPeer(t *testing.T) { |
||||
|
||||
var id enode.ID = newNodeID(t).ID() |
||||
|
||||
//peer to connect(on ulc side)
|
||||
p := peer{ |
||||
Peer: p2p.NewPeer(id, "test peer", []p2p.Cap{}), |
||||
version: protocol_version, |
||||
isTrusted: true, |
||||
rw: &rwStub{ |
||||
WriteHook: func(recvList keyValueList) { |
||||
//checking that ulc sends to peer allowedRequests=onlyAnnounceRequests and announceType = announceTypeSigned
|
||||
recv := recvList.decode() |
||||
var reqType uint64 |
||||
|
||||
err := recv.get("announceType", &reqType) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
|
||||
if reqType != announceTypeSigned { |
||||
t.Fatal("Expected announceTypeSigned") |
||||
} |
||||
}, |
||||
ReadHook: func(l keyValueList) keyValueList { |
||||
l = l.add("serveHeaders", nil) |
||||
l = l.add("serveChainSince", uint64(0)) |
||||
l = l.add("serveStateSince", uint64(0)) |
||||
l = l.add("txRelay", nil) |
||||
l = l.add("flowControl/BL", uint64(0)) |
||||
l = l.add("flowControl/MRR", uint64(0)) |
||||
l = l.add("flowControl/MRC", RequestCostList{}) |
||||
|
||||
return l |
||||
}, |
||||
}, |
||||
network: test_networkid, |
||||
} |
||||
|
||||
err := p.Handshake(td, hash, headNum, genesis, nil) |
||||
if err != nil { |
||||
t.Fatalf("Handshake error: %s", err) |
||||
} |
||||
|
||||
if p.announceType != announceTypeSigned { |
||||
t.Fatal("Incorrect announceType") |
||||
} |
||||
} |
||||
|
||||
func TestPeerHandshakeAnnounceTypeSignedForTrustedPeersPeerNotInTrusted(t *testing.T) { |
||||
var id enode.ID = newNodeID(t).ID() |
||||
p := peer{ |
||||
Peer: p2p.NewPeer(id, "test peer", []p2p.Cap{}), |
||||
version: protocol_version, |
||||
rw: &rwStub{ |
||||
WriteHook: func(recvList keyValueList) { |
||||
//checking that ulc sends to peer allowedRequests=noRequests and announceType != announceTypeSigned
|
||||
recv := recvList.decode() |
||||
var reqType uint64 |
||||
|
||||
err := recv.get("announceType", &reqType) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
|
||||
if reqType == announceTypeSigned { |
||||
t.Fatal("Expected not announceTypeSigned") |
||||
} |
||||
}, |
||||
ReadHook: func(l keyValueList) keyValueList { |
||||
l = l.add("serveHeaders", nil) |
||||
l = l.add("serveChainSince", uint64(0)) |
||||
l = l.add("serveStateSince", uint64(0)) |
||||
l = l.add("txRelay", nil) |
||||
l = l.add("flowControl/BL", uint64(0)) |
||||
l = l.add("flowControl/MRR", uint64(0)) |
||||
l = l.add("flowControl/MRC", RequestCostList{}) |
||||
|
||||
return l |
||||
}, |
||||
}, |
||||
network: test_networkid, |
||||
} |
||||
|
||||
err := p.Handshake(td, hash, headNum, genesis, nil) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
if p.announceType == announceTypeSigned { |
||||
t.Fatal("Incorrect announceType") |
||||
} |
||||
} |
||||
|
||||
func TestPeerHandshakeDefaultAllRequests(t *testing.T) { |
||||
var id enode.ID = newNodeID(t).ID() |
||||
|
||||
s := generateLesServer() |
||||
|
||||
p := peer{ |
||||
Peer: p2p.NewPeer(id, "test peer", []p2p.Cap{}), |
||||
version: protocol_version, |
||||
rw: &rwStub{ |
||||
ReadHook: func(l keyValueList) keyValueList { |
||||
l = l.add("announceType", uint64(announceTypeSigned)) |
||||
l = l.add("allowedRequests", uint64(0)) |
||||
|
||||
return l |
||||
}, |
||||
}, |
||||
network: test_networkid, |
||||
} |
||||
|
||||
err := p.Handshake(td, hash, headNum, genesis, s) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
|
||||
if p.isOnlyAnnounce { |
||||
t.Fatal("Incorrect announceType") |
||||
} |
||||
} |
||||
|
||||
func TestPeerHandshakeServerSendOnlyAnnounceRequestsHeaders(t *testing.T) { |
||||
var id enode.ID = newNodeID(t).ID() |
||||
|
||||
s := generateLesServer() |
||||
s.onlyAnnounce = true |
||||
|
||||
p := peer{ |
||||
Peer: p2p.NewPeer(id, "test peer", []p2p.Cap{}), |
||||
version: protocol_version, |
||||
rw: &rwStub{ |
||||
ReadHook: func(l keyValueList) keyValueList { |
||||
l = l.add("announceType", uint64(announceTypeSigned)) |
||||
|
||||
return l |
||||
}, |
||||
WriteHook: func(l keyValueList) { |
||||
for _, v := range l { |
||||
if v.Key == "serveHeaders" || |
||||
v.Key == "serveChainSince" || |
||||
v.Key == "serveStateSince" || |
||||
v.Key == "txRelay" { |
||||
t.Fatalf("%v exists", v.Key) |
||||
} |
||||
} |
||||
}, |
||||
}, |
||||
network: test_networkid, |
||||
} |
||||
|
||||
err := p.Handshake(td, hash, headNum, genesis, s) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
} |
||||
func TestPeerHandshakeClientReceiveOnlyAnnounceRequestsHeaders(t *testing.T) { |
||||
var id enode.ID = newNodeID(t).ID() |
||||
|
||||
p := peer{ |
||||
Peer: p2p.NewPeer(id, "test peer", []p2p.Cap{}), |
||||
version: protocol_version, |
||||
rw: &rwStub{ |
||||
ReadHook: func(l keyValueList) keyValueList { |
||||
l = l.add("flowControl/BL", uint64(0)) |
||||
l = l.add("flowControl/MRR", uint64(0)) |
||||
l = l.add("flowControl/MRC", RequestCostList{}) |
||||
|
||||
l = l.add("announceType", uint64(announceTypeSigned)) |
||||
|
||||
return l |
||||
}, |
||||
}, |
||||
network: test_networkid, |
||||
isTrusted: true, |
||||
} |
||||
|
||||
err := p.Handshake(td, hash, headNum, genesis, nil) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
|
||||
if !p.isOnlyAnnounce { |
||||
t.Fatal("isOnlyAnnounce must be true") |
||||
} |
||||
} |
||||
|
||||
func TestPeerHandshakeClientReturnErrorOnUselessPeer(t *testing.T) { |
||||
var id enode.ID = newNodeID(t).ID() |
||||
|
||||
p := peer{ |
||||
Peer: p2p.NewPeer(id, "test peer", []p2p.Cap{}), |
||||
version: protocol_version, |
||||
rw: &rwStub{ |
||||
ReadHook: func(l keyValueList) keyValueList { |
||||
l = l.add("flowControl/BL", uint64(0)) |
||||
l = l.add("flowControl/MRR", uint64(0)) |
||||
l = l.add("flowControl/MRC", RequestCostList{}) |
||||
|
||||
l = l.add("announceType", uint64(announceTypeSigned)) |
||||
|
||||
return l |
||||
}, |
||||
}, |
||||
network: test_networkid, |
||||
} |
||||
|
||||
err := p.Handshake(td, hash, headNum, genesis, nil) |
||||
if err == nil { |
||||
t.FailNow() |
||||
} |
||||
} |
||||
|
||||
func generateLesServer() *LesServer { |
||||
s := &LesServer{ |
||||
defParams: &flowcontrol.ServerParams{ |
||||
BufLimit: uint64(300000000), |
||||
MinRecharge: uint64(50000), |
||||
}, |
||||
fcManager: flowcontrol.NewClientManager(1, 2, 3), |
||||
fcCostStats: &requestCostStats{ |
||||
stats: make(map[uint64]*linReg, len(reqList)), |
||||
}, |
||||
} |
||||
for _, code := range reqList { |
||||
s.fcCostStats.stats[code] = &linReg{cnt: 100} |
||||
} |
||||
return s |
||||
} |
||||
|
||||
type rwStub struct { |
||||
ReadHook func(l keyValueList) keyValueList |
||||
WriteHook func(l keyValueList) |
||||
} |
||||
|
||||
func (s *rwStub) ReadMsg() (p2p.Msg, error) { |
||||
payload := keyValueList{} |
||||
payload = payload.add("protocolVersion", uint64(protocol_version)) |
||||
payload = payload.add("networkId", uint64(test_networkid)) |
||||
payload = payload.add("headTd", td) |
||||
payload = payload.add("headHash", hash) |
||||
payload = payload.add("headNum", headNum) |
||||
payload = payload.add("genesisHash", genesis) |
||||
|
||||
if s.ReadHook != nil { |
||||
payload = s.ReadHook(payload) |
||||
} |
||||
|
||||
size, p, err := rlp.EncodeToReader(payload) |
||||
if err != nil { |
||||
return p2p.Msg{}, err |
||||
} |
||||
|
||||
return p2p.Msg{ |
||||
Size: uint32(size), |
||||
Payload: p, |
||||
}, nil |
||||
} |
||||
|
||||
func (s *rwStub) WriteMsg(m p2p.Msg) error { |
||||
recvList := keyValueList{} |
||||
if err := m.Decode(&recvList); err != nil { |
||||
return err |
||||
} |
||||
|
||||
if s.WriteHook != nil { |
||||
s.WriteHook(recvList) |
||||
} |
||||
|
||||
return nil |
||||
} |
@ -0,0 +1,39 @@ |
||||
package les |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
"github.com/ethereum/go-ethereum/eth" |
||||
"github.com/ethereum/go-ethereum/p2p/enode" |
||||
) |
||||
|
||||
type ulc struct { |
||||
trustedKeys map[string]struct{} |
||||
minTrustedFraction int |
||||
} |
||||
|
||||
func newULC(ulcConfig *eth.ULCConfig) *ulc { |
||||
if ulcConfig == nil { |
||||
return nil |
||||
} |
||||
|
||||
m := make(map[string]struct{}, len(ulcConfig.TrustedServers)) |
||||
for _, id := range ulcConfig.TrustedServers { |
||||
node, err := enode.ParseV4(id) |
||||
if err != nil { |
||||
fmt.Println("node:", id, " err:", err) |
||||
continue |
||||
} |
||||
m[node.ID().String()] = struct{}{} |
||||
} |
||||
|
||||
return &ulc{m, ulcConfig.MinTrustedFraction} |
||||
} |
||||
|
||||
func (u *ulc) isTrusted(p enode.ID) bool { |
||||
if u.trustedKeys == nil { |
||||
return false |
||||
} |
||||
_, ok := u.trustedKeys[p.String()] |
||||
return ok |
||||
} |
@ -0,0 +1,239 @@ |
||||
package les |
||||
|
||||
import ( |
||||
"fmt" |
||||
"reflect" |
||||
"testing" |
||||
"time" |
||||
|
||||
"net" |
||||
|
||||
"crypto/ecdsa" |
||||
"math/big" |
||||
|
||||
"github.com/ethereum/go-ethereum/core" |
||||
"github.com/ethereum/go-ethereum/crypto" |
||||
"github.com/ethereum/go-ethereum/eth" |
||||
"github.com/ethereum/go-ethereum/ethdb" |
||||
"github.com/ethereum/go-ethereum/light" |
||||
"github.com/ethereum/go-ethereum/p2p" |
||||
"github.com/ethereum/go-ethereum/p2p/enode" |
||||
) |
||||
|
||||
func TestULCSyncWithOnePeer(t *testing.T) { |
||||
f := newFullPeerPair(t, 1, 4, testChainGen) |
||||
ulcConfig := ð.ULCConfig{ |
||||
MinTrustedFraction: 100, |
||||
TrustedServers: []string{f.Node.String()}, |
||||
} |
||||
|
||||
l := newLightPeer(t, ulcConfig) |
||||
|
||||
if reflect.DeepEqual(f.PM.blockchain.CurrentHeader().Hash(), l.PM.blockchain.CurrentHeader().Hash()) { |
||||
t.Fatal("blocks are equal") |
||||
} |
||||
|
||||
_, _, err := connectPeers(f, l, 2) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
|
||||
l.PM.fetcher.lock.Lock() |
||||
l.PM.fetcher.nextRequest() |
||||
l.PM.fetcher.lock.Unlock() |
||||
|
||||
if !reflect.DeepEqual(f.PM.blockchain.CurrentHeader().Hash(), l.PM.blockchain.CurrentHeader().Hash()) { |
||||
t.Fatal("sync doesn't work") |
||||
} |
||||
} |
||||
|
||||
func TestULCReceiveAnnounce(t *testing.T) { |
||||
f := newFullPeerPair(t, 1, 4, testChainGen) |
||||
ulcConfig := ð.ULCConfig{ |
||||
MinTrustedFraction: 100, |
||||
TrustedServers: []string{f.Node.String()}, |
||||
} |
||||
|
||||
l := newLightPeer(t, ulcConfig) |
||||
fPeer, lPeer, err := connectPeers(f, l, 2) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
|
||||
l.PM.synchronise(fPeer) |
||||
|
||||
//check that the sync is finished correctly
|
||||
if !reflect.DeepEqual(f.PM.blockchain.CurrentHeader().Hash(), l.PM.blockchain.CurrentHeader().Hash()) { |
||||
t.Fatal("sync doesn't work") |
||||
} |
||||
|
||||
l.PM.peers.lock.Lock() |
||||
if len(l.PM.peers.peers) == 0 { |
||||
t.Fatal("peer list should not be empty") |
||||
} |
||||
l.PM.peers.lock.Unlock() |
||||
|
||||
time.Sleep(time.Second) |
||||
//send a signed announce message(payload doesn't matter)
|
||||
td := f.PM.blockchain.GetTd(l.PM.blockchain.CurrentHeader().Hash(), l.PM.blockchain.CurrentHeader().Number.Uint64()) |
||||
announce := announceData{ |
||||
Number: l.PM.blockchain.CurrentHeader().Number.Uint64() + 1, |
||||
Td: td.Add(td, big.NewInt(1)), |
||||
} |
||||
announce.sign(f.Key) |
||||
lPeer.SendAnnounce(announce) |
||||
} |
||||
|
||||
func TestULCShouldNotSyncWithTwoPeersOneHaveEmptyChain(t *testing.T) { |
||||
f1 := newFullPeerPair(t, 1, 4, testChainGen) |
||||
f2 := newFullPeerPair(t, 2, 0, nil) |
||||
ulcConf := &ulc{minTrustedFraction: 100, trustedKeys: make(map[string]struct{})} |
||||
ulcConf.trustedKeys[f1.Node.ID().String()] = struct{}{} |
||||
ulcConf.trustedKeys[f2.Node.ID().String()] = struct{}{} |
||||
ulcConfig := ð.ULCConfig{ |
||||
MinTrustedFraction: 100, |
||||
TrustedServers: []string{f1.Node.String(), f2.Node.String()}, |
||||
} |
||||
l := newLightPeer(t, ulcConfig) |
||||
l.PM.ulc.minTrustedFraction = 100 |
||||
|
||||
_, _, err := connectPeers(f1, l, 2) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
_, _, err = connectPeers(f2, l, 2) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
|
||||
l.PM.fetcher.lock.Lock() |
||||
l.PM.fetcher.nextRequest() |
||||
l.PM.fetcher.lock.Unlock() |
||||
|
||||
if reflect.DeepEqual(f2.PM.blockchain.CurrentHeader().Hash(), l.PM.blockchain.CurrentHeader().Hash()) { |
||||
t.Fatal("Incorrect hash: second peer has empty chain") |
||||
} |
||||
} |
||||
|
||||
func TestULCShouldNotSyncWithThreePeersOneHaveEmptyChain(t *testing.T) { |
||||
f1 := newFullPeerPair(t, 1, 3, testChainGen) |
||||
f2 := newFullPeerPair(t, 2, 4, testChainGen) |
||||
f3 := newFullPeerPair(t, 3, 0, nil) |
||||
|
||||
ulcConfig := ð.ULCConfig{ |
||||
MinTrustedFraction: 60, |
||||
TrustedServers: []string{f1.Node.String(), f2.Node.String(), f3.Node.String()}, |
||||
} |
||||
|
||||
l := newLightPeer(t, ulcConfig) |
||||
_, _, err := connectPeers(f1, l, 2) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
|
||||
_, _, err = connectPeers(f2, l, 2) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
|
||||
_, _, err = connectPeers(f3, l, 2) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
|
||||
l.PM.fetcher.lock.Lock() |
||||
l.PM.fetcher.nextRequest() |
||||
l.PM.fetcher.lock.Unlock() |
||||
|
||||
if !reflect.DeepEqual(f1.PM.blockchain.CurrentHeader().Hash(), l.PM.blockchain.CurrentHeader().Hash()) { |
||||
t.Fatal("Incorrect hash") |
||||
} |
||||
} |
||||
|
||||
type pairPeer struct { |
||||
Name string |
||||
Node *enode.Node |
||||
PM *ProtocolManager |
||||
Key *ecdsa.PrivateKey |
||||
} |
||||
|
||||
func connectPeers(full, light pairPeer, version int) (*peer, *peer, error) { |
||||
// Create a message pipe to communicate through
|
||||
app, net := p2p.MsgPipe() |
||||
|
||||
peerLight := full.PM.newPeer(version, NetworkId, p2p.NewPeer(light.Node.ID(), light.Name, nil), net) |
||||
peerFull := light.PM.newPeer(version, NetworkId, p2p.NewPeer(full.Node.ID(), full.Name, nil), app) |
||||
|
||||
// Start the peerLight on a new thread
|
||||
errc1 := make(chan error, 1) |
||||
errc2 := make(chan error, 1) |
||||
go func() { |
||||
select { |
||||
case light.PM.newPeerCh <- peerFull: |
||||
errc1 <- light.PM.handle(peerFull) |
||||
case <-light.PM.quitSync: |
||||
errc1 <- p2p.DiscQuitting |
||||
} |
||||
}() |
||||
go func() { |
||||
select { |
||||
case full.PM.newPeerCh <- peerLight: |
||||
errc2 <- full.PM.handle(peerLight) |
||||
case <-full.PM.quitSync: |
||||
errc2 <- p2p.DiscQuitting |
||||
} |
||||
}() |
||||
|
||||
select { |
||||
case <-time.After(time.Millisecond * 100): |
||||
case err := <-errc1: |
||||
return nil, nil, fmt.Errorf("peerLight handshake error: %v", err) |
||||
case err := <-errc2: |
||||
return nil, nil, fmt.Errorf("peerFull handshake error: %v", err) |
||||
} |
||||
|
||||
return peerFull, peerLight, nil |
||||
} |
||||
|
||||
// newFullPeerPair creates node with full sync mode
|
||||
func newFullPeerPair(t *testing.T, index int, numberOfblocks int, chainGen func(int, *core.BlockGen)) pairPeer { |
||||
db := ethdb.NewMemDatabase() |
||||
|
||||
pmFull := newTestProtocolManagerMust(t, false, numberOfblocks, chainGen, nil, nil, db, nil) |
||||
|
||||
peerPairFull := pairPeer{ |
||||
Name: "full node", |
||||
PM: pmFull, |
||||
} |
||||
key, err := crypto.GenerateKey() |
||||
if err != nil { |
||||
t.Fatal("generate key err:", err) |
||||
} |
||||
peerPairFull.Key = key |
||||
peerPairFull.Node = enode.NewV4(&key.PublicKey, net.ParseIP("127.0.0.1"), 35000, 35000) |
||||
return peerPairFull |
||||
} |
||||
|
||||
// newLightPeer creates node with light sync mode
|
||||
func newLightPeer(t *testing.T, ulcConfig *eth.ULCConfig) pairPeer { |
||||
peers := newPeerSet() |
||||
dist := newRequestDistributor(peers, make(chan struct{})) |
||||
rm := newRetrieveManager(peers, dist, nil) |
||||
ldb := ethdb.NewMemDatabase() |
||||
|
||||
odr := NewLesOdr(ldb, light.DefaultClientIndexerConfig, rm) |
||||
|
||||
pmLight := newTestProtocolManagerMust(t, true, 0, nil, odr, peers, ldb, ulcConfig) |
||||
peerPairLight := pairPeer{ |
||||
Name: "ulc node", |
||||
PM: pmLight, |
||||
} |
||||
|
||||
key, err := crypto.GenerateKey() |
||||
if err != nil { |
||||
t.Fatal("generate key err:", err) |
||||
} |
||||
peerPairLight.Key = key |
||||
peerPairLight.Node = enode.NewV4(&key.PublicKey, net.IP{}, 35000, 35000) |
||||
return peerPairLight |
||||
} |
Loading…
Reference in new issue