all: new p2p node representation (#17643)
Package p2p/enode provides a generalized representation of p2p nodes which can contain arbitrary information in key/value pairs. It is also the new home for the node database. The "v4" identity scheme is also moved here from p2p/enr to remove the dependency on Ethereum crypto from that package. Record signature handling is changed significantly. The identity scheme registry is removed and acceptable schemes must be passed to any method that needs identity. This means records must now be validated explicitly after decoding. The enode API is designed to make signature handling easy and safe: most APIs around the codebase work with enode.Node, which is a wrapper around a valid record. Going from enr.Record to enode.Node requires a valid signature. * p2p/discover: port to p2p/enode This ports the discovery code to the new node representation in p2p/enode. The wire protocol is unchanged, this can be considered a refactoring change. The Kademlia table can now deal with nodes using an arbitrary identity scheme. This requires a few incompatible API changes: - Table.Lookup is not available anymore. It used to take a public key as argument because v4 protocol requires one. Its replacement is LookupRandom. - Table.Resolve takes *enode.Node instead of NodeID. This is also for v4 protocol compatibility because nodes cannot be looked up by ID alone. - Types Node and NodeID are gone. Further commits in the series will be fixes all over the the codebase to deal with those removals. * p2p: port to p2p/enode and discovery changes This adapts package p2p to the changes in p2p/discover. All uses of discover.Node and discover.NodeID are replaced by their equivalents from p2p/enode. New API is added to retrieve the enode.Node instance of a peer. The behavior of Server.Self with discovery disabled is improved. It now tries much harder to report a working IP address, falling back to 127.0.0.1 if no suitable address can be determined through other means. These changes were needed for tests of other packages later in the series. * p2p/simulations, p2p/testing: port to p2p/enode No surprises here, mostly replacements of discover.Node, discover.NodeID with their new equivalents. The 'interesting' API changes are: - testing.ProtocolSession tracks complete nodes, not just their IDs. - adapters.NodeConfig has a new method to create a complete node. These changes were needed to make swarm tests work. Note that the NodeID change makes the code incompatible with old simulation snapshots. * whisper/whisperv5, whisper/whisperv6: port to p2p/enode This port was easy because whisper uses []byte for node IDs and URL strings in the API. * eth: port to p2p/enode Again, easy to port because eth uses strings for node IDs and doesn't care about node information in any way. * les: port to p2p/enode Apart from replacing discover.NodeID with enode.ID, most changes are in the server pool code. It now deals with complete nodes instead of (Pubkey, IP, Port) triples. The database format is unchanged for now, but we should probably change it to use the node database later. * node: port to p2p/enode This change simply replaces discover.Node and discover.NodeID with their new equivalents. * swarm/network: port to p2p/enode Swarm has its own node address representation, BzzAddr, containing both an overlay address (the hash of a secp256k1 public key) and an underlay address (enode:// URL). There are no changes to the BzzAddr format in this commit, but certain operations such as creating a BzzAddr from a node ID are now impossible because node IDs aren't public keys anymore. Most swarm-related changes in the series remove uses of NewAddrFromNodeID, replacing it with NewAddr which takes a complete node as argument. ToOverlayAddr is removed because we can just use the node ID directly.
This commit is contained in:
parent
0ae462fb80
commit
30cd5c1854
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
)
|
||||
|
@ -85,7 +86,7 @@ func main() {
|
|||
}
|
||||
|
||||
if *writeAddr {
|
||||
fmt.Printf("%v\n", discover.PubkeyID(&nodeKey.PublicKey))
|
||||
fmt.Printf("%v\n", enode.PubkeyToIDV4(&nodeKey.PublicKey))
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
|
|
|
@ -54,8 +54,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"golang.org/x/net/websocket"
|
||||
|
@ -255,8 +255,10 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network u
|
|||
return nil, err
|
||||
}
|
||||
for _, boot := range enodes {
|
||||
old, _ := discover.ParseNode(boot.String())
|
||||
stack.Server().AddPeer(old)
|
||||
old, err := enode.ParseV4(boot.String())
|
||||
if err != nil {
|
||||
stack.Server().AddPeer(old)
|
||||
}
|
||||
}
|
||||
// Attach to the client and retrieve and interesting metadatas
|
||||
api, err := stack.Attach()
|
||||
|
|
|
@ -47,7 +47,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
@ -285,7 +285,7 @@ func createNode(ctx *cli.Context) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.ID = discover.PubkeyID(&privKey.PublicKey)
|
||||
config.ID = enode.PubkeyToIDV4(&privKey.PublicKey)
|
||||
config.PrivateKey = privKey
|
||||
}
|
||||
if services := ctx.String("services"); services != "" {
|
||||
|
|
|
@ -38,7 +38,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/internal/debug"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/swarm"
|
||||
bzzapi "github.com/ethereum/go-ethereum/swarm/api"
|
||||
swarmmetrics "github.com/ethereum/go-ethereum/swarm/metrics"
|
||||
|
@ -788,10 +788,10 @@ func setSwarmBootstrapNodes(ctx *cli.Context, cfg *node.Config) {
|
|||
return
|
||||
}
|
||||
|
||||
cfg.P2P.BootstrapNodes = []*discover.Node{}
|
||||
cfg.P2P.BootstrapNodes = []*enode.Node{}
|
||||
|
||||
for _, url := range SwarmBootnodes {
|
||||
node, err := discover.ParseNode(url)
|
||||
node, err := enode.ParseV4(url)
|
||||
if err != nil {
|
||||
log.Error("Bootstrap URL invalid", "enode", url, "err", err)
|
||||
}
|
||||
|
|
|
@ -234,6 +234,7 @@ func existingTestNode(t *testing.T, dir string, bzzaccount string) *testNode {
|
|||
// start the node
|
||||
node.Cmd = runSwarm(t,
|
||||
"--port", p2pPort,
|
||||
"--nat", "extip:127.0.0.1",
|
||||
"--nodiscover",
|
||||
"--datadir", dir,
|
||||
"--ipcpath", conf.IPCPath,
|
||||
|
@ -241,7 +242,7 @@ func existingTestNode(t *testing.T, dir string, bzzaccount string) *testNode {
|
|||
"--bzzaccount", bzzaccount,
|
||||
"--bzznetworkid", "321",
|
||||
"--bzzport", httpPort,
|
||||
"--verbosity", "6",
|
||||
"--verbosity", "3",
|
||||
)
|
||||
node.Cmd.InputLine(testPassphrase)
|
||||
defer func() {
|
||||
|
@ -284,8 +285,8 @@ func existingTestNode(t *testing.T, dir string, bzzaccount string) *testNode {
|
|||
if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
node.Enode = fmt.Sprintf("enode://%s@127.0.0.1:%s", nodeInfo.ID, p2pPort)
|
||||
|
||||
node.Enode = nodeInfo.Enode
|
||||
node.IpcPath = conf.IPCPath
|
||||
return node
|
||||
}
|
||||
|
||||
|
@ -309,6 +310,7 @@ func newTestNode(t *testing.T, dir string) *testNode {
|
|||
// start the node
|
||||
node.Cmd = runSwarm(t,
|
||||
"--port", p2pPort,
|
||||
"--nat", "extip:127.0.0.1",
|
||||
"--nodiscover",
|
||||
"--datadir", dir,
|
||||
"--ipcpath", conf.IPCPath,
|
||||
|
@ -316,7 +318,7 @@ func newTestNode(t *testing.T, dir string) *testNode {
|
|||
"--bzzaccount", account.Address.String(),
|
||||
"--bzznetworkid", "321",
|
||||
"--bzzport", httpPort,
|
||||
"--verbosity", "6",
|
||||
"--verbosity", "3",
|
||||
)
|
||||
node.Cmd.InputLine(testPassphrase)
|
||||
defer func() {
|
||||
|
@ -359,9 +361,8 @@ func newTestNode(t *testing.T, dir string) *testNode {
|
|||
if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
node.Enode = fmt.Sprintf("enode://%s@127.0.0.1:%s", nodeInfo.ID, p2pPort)
|
||||
node.Enode = nodeInfo.Enode
|
||||
node.IpcPath = conf.IPCPath
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
|
|
|
@ -51,8 +51,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum/metrics/influxdb"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
@ -692,9 +692,9 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
|||
return // already set, don't apply defaults.
|
||||
}
|
||||
|
||||
cfg.BootstrapNodes = make([]*discover.Node, 0, len(urls))
|
||||
cfg.BootstrapNodes = make([]*enode.Node, 0, len(urls))
|
||||
for _, url := range urls {
|
||||
node, err := discover.ParseNode(url)
|
||||
node, err := enode.ParseV4(url)
|
||||
if err != nil {
|
||||
log.Crit("Bootstrap URL invalid", "enode", url, "err", err)
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/whisper/mailserver"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||
|
@ -175,7 +175,7 @@ func initialize() {
|
|||
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*argVerbosity), log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
||||
|
||||
done = make(chan struct{})
|
||||
var peers []*discover.Node
|
||||
var peers []*enode.Node
|
||||
var err error
|
||||
|
||||
if *generateKey {
|
||||
|
@ -203,7 +203,7 @@ func initialize() {
|
|||
if len(*argEnode) == 0 {
|
||||
argEnode = scanLineA("Please enter the peer's enode: ")
|
||||
}
|
||||
peer := discover.MustParseNode(*argEnode)
|
||||
peer := enode.MustParseV4(*argEnode)
|
||||
peers = append(peers, peer)
|
||||
}
|
||||
|
||||
|
@ -747,11 +747,11 @@ func requestExpiredMessagesLoop() {
|
|||
}
|
||||
|
||||
func extractIDFromEnode(s string) []byte {
|
||||
n, err := discover.ParseNode(s)
|
||||
n, err := enode.ParseV4(s)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to parse enode: %s", err)
|
||||
}
|
||||
return n.ID[:]
|
||||
return n.ID().Bytes()
|
||||
}
|
||||
|
||||
// obfuscateBloom adds 16 random bits to the bloom
|
||||
|
|
|
@ -37,7 +37,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
@ -147,7 +147,7 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
|
|||
NodeInfo: func() interface{} {
|
||||
return manager.NodeInfo()
|
||||
},
|
||||
PeerInfo: func(id discover.NodeID) interface{} {
|
||||
PeerInfo: func(id enode.ID) interface{} {
|
||||
if p := manager.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil {
|
||||
return p.Info()
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ import (
|
|||
"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"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
|
@ -148,7 +148,7 @@ func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*te
|
|||
app, net := p2p.MsgPipe()
|
||||
|
||||
// Generate a random id and create the peer
|
||||
var id discover.NodeID
|
||||
var id enode.ID
|
||||
rand.Read(id[:])
|
||||
|
||||
peer := pm.newPeer(version, p2p.NewPeer(id, name, nil), net)
|
||||
|
|
|
@ -25,7 +25,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -64,7 +64,7 @@ func (pm *ProtocolManager) syncTransactions(p *peer) {
|
|||
// the transactions in small packs to one peer at a time.
|
||||
func (pm *ProtocolManager) txsyncLoop() {
|
||||
var (
|
||||
pending = make(map[discover.NodeID]*txsync)
|
||||
pending = make(map[enode.ID]*txsync)
|
||||
sending = false // whether a send is active
|
||||
pack = new(txsync) // the pack that is being sent
|
||||
done = make(chan error, 1) // result of the send
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
)
|
||||
|
||||
// Tests that fast sync gets disabled as soon as a real block is successfully
|
||||
|
@ -42,8 +42,8 @@ func TestFastSyncDisabling(t *testing.T) {
|
|||
// Sync up the two peers
|
||||
io1, io2 := p2p.MsgPipe()
|
||||
|
||||
go pmFull.handle(pmFull.newPeer(63, p2p.NewPeer(discover.NodeID{}, "empty", nil), io2))
|
||||
go pmEmpty.handle(pmEmpty.newPeer(63, p2p.NewPeer(discover.NodeID{}, "full", nil), io1))
|
||||
go pmFull.handle(pmFull.newPeer(63, p2p.NewPeer(enode.ID{}, "empty", nil), io2))
|
||||
go pmEmpty.handle(pmEmpty.newPeer(63, p2p.NewPeer(enode.ID{}, "full", nil), io1))
|
||||
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
pmEmpty.synchronise(pmEmpty.peers.BestPeer())
|
||||
|
|
|
@ -26,7 +26,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/light"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
|
@ -63,7 +63,7 @@ func (c *lesCommons) makeProtocols(versions []uint) []p2p.Protocol {
|
|||
Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||
return c.protocolManager.runPeer(version, p, rw)
|
||||
},
|
||||
PeerInfo: func(id discover.NodeID) interface{} {
|
||||
PeerInfo: func(id enode.ID) interface{} {
|
||||
if p := c.protocolManager.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil {
|
||||
return p.Info()
|
||||
}
|
||||
|
|
|
@ -213,8 +213,7 @@ func (pm *ProtocolManager) runPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWrit
|
|||
var entry *poolEntry
|
||||
peer := pm.newPeer(int(version), pm.networkId, p, rw)
|
||||
if pm.serverPool != nil {
|
||||
addr := p.RemoteAddr().(*net.TCPAddr)
|
||||
entry = pm.serverPool.connect(peer, addr.IP, uint16(addr.Port))
|
||||
entry = pm.serverPool.connect(peer, peer.Node())
|
||||
}
|
||||
peer.poolEntry = entry
|
||||
select {
|
||||
|
@ -382,7 +381,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
}
|
||||
|
||||
if p.requestAnnounceType == announceTypeSigned {
|
||||
if err := req.checkSignature(p.pubKey); err != nil {
|
||||
if err := req.checkSignature(p.ID()); err != nil {
|
||||
p.Log().Trace("Invalid announcement signature", "err", err)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/les/flowcontrol"
|
||||
"github.com/ethereum/go-ethereum/light"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
|
@ -221,7 +221,7 @@ func newTestPeer(t *testing.T, name string, version int, pm *ProtocolManager, sh
|
|||
app, net := p2p.MsgPipe()
|
||||
|
||||
// Generate a random id and create the peer
|
||||
var id discover.NodeID
|
||||
var id enode.ID
|
||||
rand.Read(id[:])
|
||||
|
||||
peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)
|
||||
|
@ -258,7 +258,7 @@ func newTestPeerPair(name string, version int, pm, pm2 *ProtocolManager) (*peer,
|
|||
app, net := p2p.MsgPipe()
|
||||
|
||||
// Generate a random id and create the peer
|
||||
var id discover.NodeID
|
||||
var id enode.ID
|
||||
rand.Read(id[:])
|
||||
|
||||
peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
package les
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
@ -51,7 +50,6 @@ const (
|
|||
|
||||
type peer struct {
|
||||
*p2p.Peer
|
||||
pubKey *ecdsa.PublicKey
|
||||
|
||||
rw p2p.MsgReadWriter
|
||||
|
||||
|
@ -80,11 +78,9 @@ type peer struct {
|
|||
|
||||
func newPeer(version int, network uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
|
||||
id := p.ID()
|
||||
pubKey, _ := id.Pubkey()
|
||||
|
||||
return &peer{
|
||||
Peer: p,
|
||||
pubKey: pubKey,
|
||||
rw: rw,
|
||||
version: version,
|
||||
network: network,
|
||||
|
|
|
@ -18,9 +18,7 @@
|
|||
package les
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -30,7 +28,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
|
@ -148,21 +146,20 @@ func (a *announceData) sign(privKey *ecdsa.PrivateKey) {
|
|||
}
|
||||
|
||||
// checkSignature verifies if the block announcement has a valid signature by the given pubKey
|
||||
func (a *announceData) checkSignature(pubKey *ecdsa.PublicKey) error {
|
||||
func (a *announceData) checkSignature(id enode.ID) error {
|
||||
var sig []byte
|
||||
if err := a.Update.decode().get("sign", &sig); err != nil {
|
||||
return err
|
||||
}
|
||||
rlp, _ := rlp.EncodeToBytes(announceBlock{a.Hash, a.Number, a.Td})
|
||||
recPubkey, err := secp256k1.RecoverPubkey(crypto.Keccak256(rlp), sig)
|
||||
recPubkey, err := crypto.SigToPub(crypto.Keccak256(rlp), sig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pbytes := elliptic.Marshal(pubKey.Curve, pubKey.X, pubKey.Y)
|
||||
if bytes.Equal(pbytes, recPubkey) {
|
||||
if id == enode.PubkeyToIDV4(recPubkey) {
|
||||
return nil
|
||||
}
|
||||
return errors.New("Wrong signature")
|
||||
return errors.New("wrong signature")
|
||||
}
|
||||
|
||||
type blockInfo struct {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package les
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
|
@ -28,11 +29,12 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/mclock"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
|
@ -90,8 +92,7 @@ const (
|
|||
// connReq represents a request for peer connection.
|
||||
type connReq struct {
|
||||
p *peer
|
||||
ip net.IP
|
||||
port uint16
|
||||
node *enode.Node
|
||||
result chan *poolEntry
|
||||
}
|
||||
|
||||
|
@ -122,10 +123,10 @@ type serverPool struct {
|
|||
topic discv5.Topic
|
||||
|
||||
discSetPeriod chan time.Duration
|
||||
discNodes chan *discv5.Node
|
||||
discNodes chan *enode.Node
|
||||
discLookups chan bool
|
||||
|
||||
entries map[discover.NodeID]*poolEntry
|
||||
entries map[enode.ID]*poolEntry
|
||||
timeout, enableRetry chan *poolEntry
|
||||
adjustStats chan poolStatAdjust
|
||||
|
||||
|
@ -145,7 +146,7 @@ func newServerPool(db ethdb.Database, quit chan struct{}, wg *sync.WaitGroup) *s
|
|||
db: db,
|
||||
quit: quit,
|
||||
wg: wg,
|
||||
entries: make(map[discover.NodeID]*poolEntry),
|
||||
entries: make(map[enode.ID]*poolEntry),
|
||||
timeout: make(chan *poolEntry, 1),
|
||||
adjustStats: make(chan poolStatAdjust, 100),
|
||||
enableRetry: make(chan *poolEntry, 1),
|
||||
|
@ -170,22 +171,38 @@ func (pool *serverPool) start(server *p2p.Server, topic discv5.Topic) {
|
|||
|
||||
if pool.server.DiscV5 != nil {
|
||||
pool.discSetPeriod = make(chan time.Duration, 1)
|
||||
pool.discNodes = make(chan *discv5.Node, 100)
|
||||
pool.discNodes = make(chan *enode.Node, 100)
|
||||
pool.discLookups = make(chan bool, 100)
|
||||
go pool.server.DiscV5.SearchTopic(pool.topic, pool.discSetPeriod, pool.discNodes, pool.discLookups)
|
||||
go pool.discoverNodes()
|
||||
}
|
||||
pool.checkDial()
|
||||
go pool.eventLoop()
|
||||
}
|
||||
|
||||
// discoverNodes wraps SearchTopic, converting result nodes to enode.Node.
|
||||
func (pool *serverPool) discoverNodes() {
|
||||
ch := make(chan *discv5.Node)
|
||||
go func() {
|
||||
pool.server.DiscV5.SearchTopic(pool.topic, pool.discSetPeriod, ch, pool.discLookups)
|
||||
close(ch)
|
||||
}()
|
||||
for n := range ch {
|
||||
pubkey, err := decodePubkey64(n.ID[:])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
pool.discNodes <- enode.NewV4(pubkey, n.IP, int(n.TCP), int(n.UDP))
|
||||
}
|
||||
}
|
||||
|
||||
// connect should be called upon any incoming connection. If the connection has been
|
||||
// dialed by the server pool recently, the appropriate pool entry is returned.
|
||||
// Otherwise, the connection should be rejected.
|
||||
// Note that whenever a connection has been accepted and a pool entry has been returned,
|
||||
// disconnect should also always be called.
|
||||
func (pool *serverPool) connect(p *peer, ip net.IP, port uint16) *poolEntry {
|
||||
func (pool *serverPool) connect(p *peer, node *enode.Node) *poolEntry {
|
||||
log.Debug("Connect new entry", "enode", p.id)
|
||||
req := &connReq{p: p, ip: ip, port: port, result: make(chan *poolEntry, 1)}
|
||||
req := &connReq{p: p, node: node, result: make(chan *poolEntry, 1)}
|
||||
select {
|
||||
case pool.connCh <- req:
|
||||
case <-pool.quit:
|
||||
|
@ -196,7 +213,7 @@ func (pool *serverPool) connect(p *peer, ip net.IP, port uint16) *poolEntry {
|
|||
|
||||
// registered should be called after a successful handshake
|
||||
func (pool *serverPool) registered(entry *poolEntry) {
|
||||
log.Debug("Registered new entry", "enode", entry.id)
|
||||
log.Debug("Registered new entry", "enode", entry.node.ID())
|
||||
req := ®isterReq{entry: entry, done: make(chan struct{})}
|
||||
select {
|
||||
case pool.registerCh <- req:
|
||||
|
@ -216,7 +233,7 @@ func (pool *serverPool) disconnect(entry *poolEntry) {
|
|||
stopped = true
|
||||
default:
|
||||
}
|
||||
log.Debug("Disconnected old entry", "enode", entry.id)
|
||||
log.Debug("Disconnected old entry", "enode", entry.node.ID())
|
||||
req := &disconnReq{entry: entry, stopped: stopped, done: make(chan struct{})}
|
||||
|
||||
// Block until disconnection request is served.
|
||||
|
@ -320,7 +337,7 @@ func (pool *serverPool) eventLoop() {
|
|||
}
|
||||
|
||||
case node := <-pool.discNodes:
|
||||
entry := pool.findOrNewNode(discover.NodeID(node.ID), node.IP, node.TCP)
|
||||
entry := pool.findOrNewNode(node)
|
||||
pool.updateCheckDial(entry)
|
||||
|
||||
case conv := <-pool.discLookups:
|
||||
|
@ -341,7 +358,7 @@ func (pool *serverPool) eventLoop() {
|
|||
// Handle peer connection requests.
|
||||
entry := pool.entries[req.p.ID()]
|
||||
if entry == nil {
|
||||
entry = pool.findOrNewNode(req.p.ID(), req.ip, req.port)
|
||||
entry = pool.findOrNewNode(req.node)
|
||||
}
|
||||
if entry.state == psConnected || entry.state == psRegistered {
|
||||
req.result <- nil
|
||||
|
@ -351,8 +368,8 @@ func (pool *serverPool) eventLoop() {
|
|||
entry.peer = req.p
|
||||
entry.state = psConnected
|
||||
addr := &poolEntryAddress{
|
||||
ip: req.ip,
|
||||
port: req.port,
|
||||
ip: req.node.IP(),
|
||||
port: uint16(req.node.TCP()),
|
||||
lastSeen: mclock.Now(),
|
||||
}
|
||||
entry.lastConnected = addr
|
||||
|
@ -401,18 +418,18 @@ func (pool *serverPool) eventLoop() {
|
|||
}
|
||||
}
|
||||
|
||||
func (pool *serverPool) findOrNewNode(id discover.NodeID, ip net.IP, port uint16) *poolEntry {
|
||||
func (pool *serverPool) findOrNewNode(node *enode.Node) *poolEntry {
|
||||
now := mclock.Now()
|
||||
entry := pool.entries[id]
|
||||
entry := pool.entries[node.ID()]
|
||||
if entry == nil {
|
||||
log.Debug("Discovered new entry", "id", id)
|
||||
log.Debug("Discovered new entry", "id", node.ID())
|
||||
entry = &poolEntry{
|
||||
id: id,
|
||||
node: node,
|
||||
addr: make(map[string]*poolEntryAddress),
|
||||
addrSelect: *newWeightedRandomSelect(),
|
||||
shortRetry: shortRetryCnt,
|
||||
}
|
||||
pool.entries[id] = entry
|
||||
pool.entries[node.ID()] = entry
|
||||
// initialize previously unknown peers with good statistics to give a chance to prove themselves
|
||||
entry.connectStats.add(1, initStatsWeight)
|
||||
entry.delayStats.add(0, initStatsWeight)
|
||||
|
@ -420,10 +437,7 @@ func (pool *serverPool) findOrNewNode(id discover.NodeID, ip net.IP, port uint16
|
|||
entry.timeoutStats.add(0, initStatsWeight)
|
||||
}
|
||||
entry.lastDiscovered = now
|
||||
addr := &poolEntryAddress{
|
||||
ip: ip,
|
||||
port: port,
|
||||
}
|
||||
addr := &poolEntryAddress{ip: node.IP(), port: uint16(node.TCP())}
|
||||
if a, ok := entry.addr[addr.strKey()]; ok {
|
||||
addr = a
|
||||
} else {
|
||||
|
@ -450,12 +464,12 @@ func (pool *serverPool) loadNodes() {
|
|||
return
|
||||
}
|
||||
for _, e := range list {
|
||||
log.Debug("Loaded server stats", "id", e.id, "fails", e.lastConnected.fails,
|
||||
log.Debug("Loaded server stats", "id", e.node.ID(), "fails", e.lastConnected.fails,
|
||||
"conn", fmt.Sprintf("%v/%v", e.connectStats.avg, e.connectStats.weight),
|
||||
"delay", fmt.Sprintf("%v/%v", time.Duration(e.delayStats.avg), e.delayStats.weight),
|
||||
"response", fmt.Sprintf("%v/%v", time.Duration(e.responseStats.avg), e.responseStats.weight),
|
||||
"timeout", fmt.Sprintf("%v/%v", e.timeoutStats.avg, e.timeoutStats.weight))
|
||||
pool.entries[e.id] = e
|
||||
pool.entries[e.node.ID()] = e
|
||||
pool.knownQueue.setLatest(e)
|
||||
pool.knownSelect.update((*knownEntry)(e))
|
||||
}
|
||||
|
@ -481,7 +495,7 @@ func (pool *serverPool) removeEntry(entry *poolEntry) {
|
|||
pool.newSelect.remove((*discoveredEntry)(entry))
|
||||
pool.knownSelect.remove((*knownEntry)(entry))
|
||||
entry.removed = true
|
||||
delete(pool.entries, entry.id)
|
||||
delete(pool.entries, entry.node.ID())
|
||||
}
|
||||
|
||||
// setRetryDial starts the timer which will enable dialing a certain node again
|
||||
|
@ -559,10 +573,10 @@ func (pool *serverPool) dial(entry *poolEntry, knownSelected bool) {
|
|||
pool.newSelected++
|
||||
}
|
||||
addr := entry.addrSelect.choose().(*poolEntryAddress)
|
||||
log.Debug("Dialing new peer", "lesaddr", entry.id.String()+"@"+addr.strKey(), "set", len(entry.addr), "known", knownSelected)
|
||||
log.Debug("Dialing new peer", "lesaddr", entry.node.ID().String()+"@"+addr.strKey(), "set", len(entry.addr), "known", knownSelected)
|
||||
entry.dialed = addr
|
||||
go func() {
|
||||
pool.server.AddPeer(discover.NewNode(entry.id, addr.ip, addr.port, addr.port))
|
||||
pool.server.AddPeer(entry.node)
|
||||
select {
|
||||
case <-pool.quit:
|
||||
case <-time.After(dialTimeout):
|
||||
|
@ -580,7 +594,7 @@ func (pool *serverPool) checkDialTimeout(entry *poolEntry) {
|
|||
if entry.state != psDialed {
|
||||
return
|
||||
}
|
||||
log.Debug("Dial timeout", "lesaddr", entry.id.String()+"@"+entry.dialed.strKey())
|
||||
log.Debug("Dial timeout", "lesaddr", entry.node.ID().String()+"@"+entry.dialed.strKey())
|
||||
entry.state = psNotConnected
|
||||
if entry.knownSelected {
|
||||
pool.knownSelected--
|
||||
|
@ -602,8 +616,9 @@ const (
|
|||
// poolEntry represents a server node and stores its current state and statistics.
|
||||
type poolEntry struct {
|
||||
peer *peer
|
||||
id discover.NodeID
|
||||
pubkey [64]byte // secp256k1 key of the node
|
||||
addr map[string]*poolEntryAddress
|
||||
node *enode.Node
|
||||
lastConnected, dialed *poolEntryAddress
|
||||
addrSelect weightedRandomSelect
|
||||
|
||||
|
@ -620,23 +635,39 @@ type poolEntry struct {
|
|||
shortRetry int
|
||||
}
|
||||
|
||||
// poolEntryEnc is the RLP encoding of poolEntry.
|
||||
type poolEntryEnc struct {
|
||||
Pubkey []byte
|
||||
IP net.IP
|
||||
Port uint16
|
||||
Fails uint
|
||||
CStat, DStat, RStat, TStat poolStats
|
||||
}
|
||||
|
||||
func (e *poolEntry) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, []interface{}{e.id, e.lastConnected.ip, e.lastConnected.port, e.lastConnected.fails, &e.connectStats, &e.delayStats, &e.responseStats, &e.timeoutStats})
|
||||
return rlp.Encode(w, &poolEntryEnc{
|
||||
Pubkey: encodePubkey64(e.node.Pubkey()),
|
||||
IP: e.lastConnected.ip,
|
||||
Port: e.lastConnected.port,
|
||||
Fails: e.lastConnected.fails,
|
||||
CStat: e.connectStats,
|
||||
DStat: e.delayStats,
|
||||
RStat: e.responseStats,
|
||||
TStat: e.timeoutStats,
|
||||
})
|
||||
}
|
||||
|
||||
func (e *poolEntry) DecodeRLP(s *rlp.Stream) error {
|
||||
var entry struct {
|
||||
ID discover.NodeID
|
||||
IP net.IP
|
||||
Port uint16
|
||||
Fails uint
|
||||
CStat, DStat, RStat, TStat poolStats
|
||||
}
|
||||
var entry poolEntryEnc
|
||||
if err := s.Decode(&entry); err != nil {
|
||||
return err
|
||||
}
|
||||
pubkey, err := decodePubkey64(entry.Pubkey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
addr := &poolEntryAddress{ip: entry.IP, port: entry.Port, fails: entry.Fails, lastSeen: mclock.Now()}
|
||||
e.id = entry.ID
|
||||
e.node = enode.NewV4(pubkey, entry.IP, int(entry.Port), int(entry.Port))
|
||||
e.addr = make(map[string]*poolEntryAddress)
|
||||
e.addr[addr.strKey()] = addr
|
||||
e.addrSelect = *newWeightedRandomSelect()
|
||||
|
@ -651,6 +682,14 @@ func (e *poolEntry) DecodeRLP(s *rlp.Stream) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func encodePubkey64(pub *ecdsa.PublicKey) []byte {
|
||||
return crypto.FromECDSAPub(pub)[:1]
|
||||
}
|
||||
|
||||
func decodePubkey64(b []byte) (*ecdsa.PublicKey, error) {
|
||||
return crypto.UnmarshalPubkey(append([]byte{0x04}, b...))
|
||||
}
|
||||
|
||||
// discoveredEntry implements wrsItem
|
||||
type discoveredEntry poolEntry
|
||||
|
||||
|
|
10
node/api.go
10
node/api.go
|
@ -26,7 +26,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
|
@ -51,7 +51,7 @@ func (api *PrivateAdminAPI) AddPeer(url string) (bool, error) {
|
|||
return false, ErrNodeStopped
|
||||
}
|
||||
// Try to add the url as a static peer and return
|
||||
node, err := discover.ParseNode(url)
|
||||
node, err := enode.ParseV4(url)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid enode: %v", err)
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ func (api *PrivateAdminAPI) RemovePeer(url string) (bool, error) {
|
|||
return false, ErrNodeStopped
|
||||
}
|
||||
// Try to remove the url as a static peer and return
|
||||
node, err := discover.ParseNode(url)
|
||||
node, err := enode.ParseV4(url)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid enode: %v", err)
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ func (api *PrivateAdminAPI) AddTrustedPeer(url string) (bool, error) {
|
|||
if server == nil {
|
||||
return false, ErrNodeStopped
|
||||
}
|
||||
node, err := discover.ParseNode(url)
|
||||
node, err := enode.ParseV4(url)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid enode: %v", err)
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ func (api *PrivateAdminAPI) RemoveTrustedPeer(url string) (bool, error) {
|
|||
if server == nil {
|
||||
return false, ErrNodeStopped
|
||||
}
|
||||
node, err := discover.ParseNode(url)
|
||||
node, err := enode.ParseV4(url)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid enode: %v", err)
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
|
@ -336,18 +336,18 @@ func (c *Config) NodeKey() *ecdsa.PrivateKey {
|
|||
}
|
||||
|
||||
// StaticNodes returns a list of node enode URLs configured as static nodes.
|
||||
func (c *Config) StaticNodes() []*discover.Node {
|
||||
func (c *Config) StaticNodes() []*enode.Node {
|
||||
return c.parsePersistentNodes(c.ResolvePath(datadirStaticNodes))
|
||||
}
|
||||
|
||||
// TrustedNodes returns a list of node enode URLs configured as trusted nodes.
|
||||
func (c *Config) TrustedNodes() []*discover.Node {
|
||||
func (c *Config) TrustedNodes() []*enode.Node {
|
||||
return c.parsePersistentNodes(c.ResolvePath(datadirTrustedNodes))
|
||||
}
|
||||
|
||||
// parsePersistentNodes parses a list of discovery node URLs loaded from a .json
|
||||
// file from within the data directory.
|
||||
func (c *Config) parsePersistentNodes(path string) []*discover.Node {
|
||||
func (c *Config) parsePersistentNodes(path string) []*enode.Node {
|
||||
// Short circuit if no node config is present
|
||||
if c.DataDir == "" {
|
||||
return nil
|
||||
|
@ -362,12 +362,12 @@ func (c *Config) parsePersistentNodes(path string) []*discover.Node {
|
|||
return nil
|
||||
}
|
||||
// Interpret the list as a discovery node array
|
||||
var nodes []*discover.Node
|
||||
var nodes []*enode.Node
|
||||
for _, url := range nodelist {
|
||||
if url == "" {
|
||||
continue
|
||||
}
|
||||
node, err := discover.ParseNode(url)
|
||||
node, err := enode.ParseV4(url)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Node URL %s: %v\n", url, err))
|
||||
continue
|
||||
|
|
102
p2p/dial.go
102
p2p/dial.go
|
@ -18,14 +18,13 @@ package p2p
|
|||
|
||||
import (
|
||||
"container/heap"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
)
|
||||
|
||||
|
@ -50,7 +49,7 @@ const (
|
|||
// NodeDialer is used to connect to nodes in the network, typically by using
|
||||
// an underlying net.Dialer but also using net.Pipe in tests
|
||||
type NodeDialer interface {
|
||||
Dial(*discover.Node) (net.Conn, error)
|
||||
Dial(*enode.Node) (net.Conn, error)
|
||||
}
|
||||
|
||||
// TCPDialer implements the NodeDialer interface by using a net.Dialer to
|
||||
|
@ -60,8 +59,8 @@ type TCPDialer struct {
|
|||
}
|
||||
|
||||
// Dial creates a TCP connection to the node
|
||||
func (t TCPDialer) Dial(dest *discover.Node) (net.Conn, error) {
|
||||
addr := &net.TCPAddr{IP: dest.IP, Port: int(dest.TCP)}
|
||||
func (t TCPDialer) Dial(dest *enode.Node) (net.Conn, error) {
|
||||
addr := &net.TCPAddr{IP: dest.IP(), Port: dest.TCP()}
|
||||
return t.Dialer.Dial("tcp", addr.String())
|
||||
}
|
||||
|
||||
|
@ -74,22 +73,22 @@ type dialstate struct {
|
|||
netrestrict *netutil.Netlist
|
||||
|
||||
lookupRunning bool
|
||||
dialing map[discover.NodeID]connFlag
|
||||
lookupBuf []*discover.Node // current discovery lookup results
|
||||
randomNodes []*discover.Node // filled from Table
|
||||
static map[discover.NodeID]*dialTask
|
||||
dialing map[enode.ID]connFlag
|
||||
lookupBuf []*enode.Node // current discovery lookup results
|
||||
randomNodes []*enode.Node // filled from Table
|
||||
static map[enode.ID]*dialTask
|
||||
hist *dialHistory
|
||||
|
||||
start time.Time // time when the dialer was first used
|
||||
bootnodes []*discover.Node // default dials when there are no peers
|
||||
start time.Time // time when the dialer was first used
|
||||
bootnodes []*enode.Node // default dials when there are no peers
|
||||
}
|
||||
|
||||
type discoverTable interface {
|
||||
Self() *discover.Node
|
||||
Self() *enode.Node
|
||||
Close()
|
||||
Resolve(target discover.NodeID) *discover.Node
|
||||
Lookup(target discover.NodeID) []*discover.Node
|
||||
ReadRandomNodes([]*discover.Node) int
|
||||
Resolve(*enode.Node) *enode.Node
|
||||
LookupRandom() []*enode.Node
|
||||
ReadRandomNodes([]*enode.Node) int
|
||||
}
|
||||
|
||||
// the dial history remembers recent dials.
|
||||
|
@ -97,7 +96,7 @@ type dialHistory []pastDial
|
|||
|
||||
// pastDial is an entry in the dial history.
|
||||
type pastDial struct {
|
||||
id discover.NodeID
|
||||
id enode.ID
|
||||
exp time.Time
|
||||
}
|
||||
|
||||
|
@ -109,7 +108,7 @@ type task interface {
|
|||
// fields cannot be accessed while the task is running.
|
||||
type dialTask struct {
|
||||
flags connFlag
|
||||
dest *discover.Node
|
||||
dest *enode.Node
|
||||
lastResolved time.Time
|
||||
resolveDelay time.Duration
|
||||
}
|
||||
|
@ -118,7 +117,7 @@ type dialTask struct {
|
|||
// Only one discoverTask is active at any time.
|
||||
// discoverTask.Do performs a random lookup.
|
||||
type discoverTask struct {
|
||||
results []*discover.Node
|
||||
results []*enode.Node
|
||||
}
|
||||
|
||||
// A waitExpireTask is generated if there are no other tasks
|
||||
|
@ -127,15 +126,15 @@ type waitExpireTask struct {
|
|||
time.Duration
|
||||
}
|
||||
|
||||
func newDialState(static []*discover.Node, bootnodes []*discover.Node, ntab discoverTable, maxdyn int, netrestrict *netutil.Netlist) *dialstate {
|
||||
func newDialState(static []*enode.Node, bootnodes []*enode.Node, ntab discoverTable, maxdyn int, netrestrict *netutil.Netlist) *dialstate {
|
||||
s := &dialstate{
|
||||
maxDynDials: maxdyn,
|
||||
ntab: ntab,
|
||||
netrestrict: netrestrict,
|
||||
static: make(map[discover.NodeID]*dialTask),
|
||||
dialing: make(map[discover.NodeID]connFlag),
|
||||
bootnodes: make([]*discover.Node, len(bootnodes)),
|
||||
randomNodes: make([]*discover.Node, maxdyn/2),
|
||||
static: make(map[enode.ID]*dialTask),
|
||||
dialing: make(map[enode.ID]connFlag),
|
||||
bootnodes: make([]*enode.Node, len(bootnodes)),
|
||||
randomNodes: make([]*enode.Node, maxdyn/2),
|
||||
hist: new(dialHistory),
|
||||
}
|
||||
copy(s.bootnodes, bootnodes)
|
||||
|
@ -145,32 +144,32 @@ func newDialState(static []*discover.Node, bootnodes []*discover.Node, ntab disc
|
|||
return s
|
||||
}
|
||||
|
||||
func (s *dialstate) addStatic(n *discover.Node) {
|
||||
func (s *dialstate) addStatic(n *enode.Node) {
|
||||
// This overwrites the task instead of updating an existing
|
||||
// entry, giving users the opportunity to force a resolve operation.
|
||||
s.static[n.ID] = &dialTask{flags: staticDialedConn, dest: n}
|
||||
s.static[n.ID()] = &dialTask{flags: staticDialedConn, dest: n}
|
||||
}
|
||||
|
||||
func (s *dialstate) removeStatic(n *discover.Node) {
|
||||
func (s *dialstate) removeStatic(n *enode.Node) {
|
||||
// This removes a task so future attempts to connect will not be made.
|
||||
delete(s.static, n.ID)
|
||||
delete(s.static, n.ID())
|
||||
// This removes a previous dial timestamp so that application
|
||||
// can force a server to reconnect with chosen peer immediately.
|
||||
s.hist.remove(n.ID)
|
||||
s.hist.remove(n.ID())
|
||||
}
|
||||
|
||||
func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now time.Time) []task {
|
||||
func (s *dialstate) newTasks(nRunning int, peers map[enode.ID]*Peer, now time.Time) []task {
|
||||
if s.start.IsZero() {
|
||||
s.start = now
|
||||
}
|
||||
|
||||
var newtasks []task
|
||||
addDial := func(flag connFlag, n *discover.Node) bool {
|
||||
addDial := func(flag connFlag, n *enode.Node) bool {
|
||||
if err := s.checkDial(n, peers); err != nil {
|
||||
log.Trace("Skipping dial candidate", "id", n.ID, "addr", &net.TCPAddr{IP: n.IP, Port: int(n.TCP)}, "err", err)
|
||||
log.Trace("Skipping dial candidate", "id", n.ID(), "addr", &net.TCPAddr{IP: n.IP(), Port: n.TCP()}, "err", err)
|
||||
return false
|
||||
}
|
||||
s.dialing[n.ID] = flag
|
||||
s.dialing[n.ID()] = flag
|
||||
newtasks = append(newtasks, &dialTask{flags: flag, dest: n})
|
||||
return true
|
||||
}
|
||||
|
@ -196,8 +195,8 @@ func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now
|
|||
err := s.checkDial(t.dest, peers)
|
||||
switch err {
|
||||
case errNotWhitelisted, errSelf:
|
||||
log.Warn("Removing static dial candidate", "id", t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP, Port: int(t.dest.TCP)}, "err", err)
|
||||
delete(s.static, t.dest.ID)
|
||||
log.Warn("Removing static dial candidate", "id", t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP(), Port: t.dest.TCP()}, "err", err)
|
||||
delete(s.static, t.dest.ID())
|
||||
case nil:
|
||||
s.dialing[id] = t.flags
|
||||
newtasks = append(newtasks, t)
|
||||
|
@ -260,18 +259,18 @@ var (
|
|||
errNotWhitelisted = errors.New("not contained in netrestrict whitelist")
|
||||
)
|
||||
|
||||
func (s *dialstate) checkDial(n *discover.Node, peers map[discover.NodeID]*Peer) error {
|
||||
_, dialing := s.dialing[n.ID]
|
||||
func (s *dialstate) checkDial(n *enode.Node, peers map[enode.ID]*Peer) error {
|
||||
_, dialing := s.dialing[n.ID()]
|
||||
switch {
|
||||
case dialing:
|
||||
return errAlreadyDialing
|
||||
case peers[n.ID] != nil:
|
||||
case peers[n.ID()] != nil:
|
||||
return errAlreadyConnected
|
||||
case s.ntab != nil && n.ID == s.ntab.Self().ID:
|
||||
case s.ntab != nil && n.ID() == s.ntab.Self().ID():
|
||||
return errSelf
|
||||
case s.netrestrict != nil && !s.netrestrict.Contains(n.IP):
|
||||
case s.netrestrict != nil && !s.netrestrict.Contains(n.IP()):
|
||||
return errNotWhitelisted
|
||||
case s.hist.contains(n.ID):
|
||||
case s.hist.contains(n.ID()):
|
||||
return errRecentlyDialed
|
||||
}
|
||||
return nil
|
||||
|
@ -280,8 +279,8 @@ func (s *dialstate) checkDial(n *discover.Node, peers map[discover.NodeID]*Peer)
|
|||
func (s *dialstate) taskDone(t task, now time.Time) {
|
||||
switch t := t.(type) {
|
||||
case *dialTask:
|
||||
s.hist.add(t.dest.ID, now.Add(dialHistoryExpiration))
|
||||
delete(s.dialing, t.dest.ID)
|
||||
s.hist.add(t.dest.ID(), now.Add(dialHistoryExpiration))
|
||||
delete(s.dialing, t.dest.ID())
|
||||
case *discoverTask:
|
||||
s.lookupRunning = false
|
||||
s.lookupBuf = append(s.lookupBuf, t.results...)
|
||||
|
@ -323,7 +322,7 @@ func (t *dialTask) resolve(srv *Server) bool {
|
|||
if time.Since(t.lastResolved) < t.resolveDelay {
|
||||
return false
|
||||
}
|
||||
resolved := srv.ntab.Resolve(t.dest.ID)
|
||||
resolved := srv.ntab.Resolve(t.dest)
|
||||
t.lastResolved = time.Now()
|
||||
if resolved == nil {
|
||||
t.resolveDelay *= 2
|
||||
|
@ -336,7 +335,7 @@ func (t *dialTask) resolve(srv *Server) bool {
|
|||
// The node was found.
|
||||
t.resolveDelay = initialResolveDelay
|
||||
t.dest = resolved
|
||||
log.Debug("Resolved node", "id", t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP, Port: int(t.dest.TCP)})
|
||||
log.Debug("Resolved node", "id", t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP(), Port: t.dest.TCP()})
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -345,7 +344,7 @@ type dialError struct {
|
|||
}
|
||||
|
||||
// dial performs the actual connection attempt.
|
||||
func (t *dialTask) dial(srv *Server, dest *discover.Node) error {
|
||||
func (t *dialTask) dial(srv *Server, dest *enode.Node) error {
|
||||
fd, err := srv.Dialer.Dial(dest)
|
||||
if err != nil {
|
||||
return &dialError{err}
|
||||
|
@ -355,7 +354,8 @@ func (t *dialTask) dial(srv *Server, dest *discover.Node) error {
|
|||
}
|
||||
|
||||
func (t *dialTask) String() string {
|
||||
return fmt.Sprintf("%v %x %v:%d", t.flags, t.dest.ID[:8], t.dest.IP, t.dest.TCP)
|
||||
id := t.dest.ID()
|
||||
return fmt.Sprintf("%v %x %v:%d", t.flags, id[:8], t.dest.IP(), t.dest.TCP())
|
||||
}
|
||||
|
||||
func (t *discoverTask) Do(srv *Server) {
|
||||
|
@ -367,9 +367,7 @@ func (t *discoverTask) Do(srv *Server) {
|
|||
time.Sleep(next.Sub(now))
|
||||
}
|
||||
srv.lastLookup = time.Now()
|
||||
var target discover.NodeID
|
||||
rand.Read(target[:])
|
||||
t.results = srv.ntab.Lookup(target)
|
||||
t.results = srv.ntab.LookupRandom()
|
||||
}
|
||||
|
||||
func (t *discoverTask) String() string {
|
||||
|
@ -391,11 +389,11 @@ func (t waitExpireTask) String() string {
|
|||
func (h dialHistory) min() pastDial {
|
||||
return h[0]
|
||||
}
|
||||
func (h *dialHistory) add(id discover.NodeID, exp time.Time) {
|
||||
func (h *dialHistory) add(id enode.ID, exp time.Time) {
|
||||
heap.Push(h, pastDial{id, exp})
|
||||
|
||||
}
|
||||
func (h *dialHistory) remove(id discover.NodeID) bool {
|
||||
func (h *dialHistory) remove(id enode.ID) bool {
|
||||
for i, v := range *h {
|
||||
if v.id == id {
|
||||
heap.Remove(h, i)
|
||||
|
@ -404,7 +402,7 @@ func (h *dialHistory) remove(id discover.NodeID) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
func (h dialHistory) contains(id discover.NodeID) bool {
|
||||
func (h dialHistory) contains(id enode.ID) bool {
|
||||
for _, v := range h {
|
||||
if v.id == id {
|
||||
return true
|
||||
|
|
443
p2p/dial_test.go
443
p2p/dial_test.go
|
@ -24,7 +24,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
)
|
||||
|
||||
|
@ -48,10 +49,10 @@ func runDialTest(t *testing.T, test dialtest) {
|
|||
vtime time.Time
|
||||
running int
|
||||
)
|
||||
pm := func(ps []*Peer) map[discover.NodeID]*Peer {
|
||||
m := make(map[discover.NodeID]*Peer)
|
||||
pm := func(ps []*Peer) map[enode.ID]*Peer {
|
||||
m := make(map[enode.ID]*Peer)
|
||||
for _, p := range ps {
|
||||
m[p.rw.id] = p
|
||||
m[p.ID()] = p
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
@ -69,6 +70,7 @@ func runDialTest(t *testing.T, test dialtest) {
|
|||
t.Errorf("round %d: new tasks mismatch:\ngot %v\nwant %v\nstate: %v\nrunning: %v\n",
|
||||
i, spew.Sdump(new), spew.Sdump(round.new), spew.Sdump(test.init), spew.Sdump(running))
|
||||
}
|
||||
t.Log("tasks:", spew.Sdump(new))
|
||||
|
||||
// Time advances by 16 seconds on every round.
|
||||
vtime = vtime.Add(16 * time.Second)
|
||||
|
@ -76,13 +78,13 @@ func runDialTest(t *testing.T, test dialtest) {
|
|||
}
|
||||
}
|
||||
|
||||
type fakeTable []*discover.Node
|
||||
type fakeTable []*enode.Node
|
||||
|
||||
func (t fakeTable) Self() *discover.Node { return new(discover.Node) }
|
||||
func (t fakeTable) Close() {}
|
||||
func (t fakeTable) Lookup(discover.NodeID) []*discover.Node { return nil }
|
||||
func (t fakeTable) Resolve(discover.NodeID) *discover.Node { return nil }
|
||||
func (t fakeTable) ReadRandomNodes(buf []*discover.Node) int { return copy(buf, t) }
|
||||
func (t fakeTable) Self() *enode.Node { return new(enode.Node) }
|
||||
func (t fakeTable) Close() {}
|
||||
func (t fakeTable) LookupRandom() []*enode.Node { return nil }
|
||||
func (t fakeTable) Resolve(*enode.Node) *enode.Node { return nil }
|
||||
func (t fakeTable) ReadRandomNodes(buf []*enode.Node) int { return copy(buf, t) }
|
||||
|
||||
// This test checks that dynamic dials are launched from discovery results.
|
||||
func TestDialStateDynDial(t *testing.T) {
|
||||
|
@ -92,63 +94,63 @@ func TestDialStateDynDial(t *testing.T) {
|
|||
// A discovery query is launched.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(0), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
|
||||
},
|
||||
new: []task{&discoverTask{}},
|
||||
},
|
||||
// Dynamic dials are launched when it completes.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(0), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
|
||||
},
|
||||
done: []task{
|
||||
&discoverTask{results: []*discover.Node{
|
||||
{ID: uintID(2)}, // this one is already connected and not dialed.
|
||||
{ID: uintID(3)},
|
||||
{ID: uintID(4)},
|
||||
{ID: uintID(5)},
|
||||
{ID: uintID(6)}, // these are not tried because max dyn dials is 5
|
||||
{ID: uintID(7)}, // ...
|
||||
&discoverTask{results: []*enode.Node{
|
||||
newNode(uintID(2), nil), // this one is already connected and not dialed.
|
||||
newNode(uintID(3), nil),
|
||||
newNode(uintID(4), nil),
|
||||
newNode(uintID(5), nil),
|
||||
newNode(uintID(6), nil), // these are not tried because max dyn dials is 5
|
||||
newNode(uintID(7), nil), // ...
|
||||
}},
|
||||
},
|
||||
new: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
|
||||
},
|
||||
},
|
||||
// Some of the dials complete but no new ones are launched yet because
|
||||
// the sum of active dial count and dynamic peer count is == maxDynDials.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(3)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(4)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(0), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(3), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(4), nil)}},
|
||||
},
|
||||
done: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
|
||||
},
|
||||
},
|
||||
// No new dial tasks are launched in the this round because
|
||||
// maxDynDials has been reached.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(3)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(4)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(0), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(3), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(4), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(5), nil)}},
|
||||
},
|
||||
done: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
|
||||
},
|
||||
new: []task{
|
||||
&waitExpireTask{Duration: 14 * time.Second},
|
||||
|
@ -158,14 +160,14 @@ func TestDialStateDynDial(t *testing.T) {
|
|||
// results from last discovery lookup are reused.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(3)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(4)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(0), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(3), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(4), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(5), nil)}},
|
||||
},
|
||||
new: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(6)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(6), nil)},
|
||||
},
|
||||
},
|
||||
// More peers (3,4) drop off and dial for ID 6 completes.
|
||||
|
@ -173,15 +175,15 @@ func TestDialStateDynDial(t *testing.T) {
|
|||
// and a new one is spawned because more candidates are needed.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(0), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(5), nil)}},
|
||||
},
|
||||
done: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(6)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(6), nil)},
|
||||
},
|
||||
new: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(7)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(7), nil)},
|
||||
&discoverTask{},
|
||||
},
|
||||
},
|
||||
|
@ -190,23 +192,23 @@ func TestDialStateDynDial(t *testing.T) {
|
|||
// no new is started.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(7)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(0), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(5), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(7), nil)}},
|
||||
},
|
||||
done: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(7)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(7), nil)},
|
||||
},
|
||||
},
|
||||
// Finish the running node discovery with an empty set. A new lookup
|
||||
// should be immediately requested.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(7)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(0), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(5), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(7), nil)}},
|
||||
},
|
||||
done: []task{
|
||||
&discoverTask{},
|
||||
|
@ -221,17 +223,17 @@ func TestDialStateDynDial(t *testing.T) {
|
|||
|
||||
// Tests that bootnodes are dialed if no peers are connectd, but not otherwise.
|
||||
func TestDialStateDynDialBootnode(t *testing.T) {
|
||||
bootnodes := []*discover.Node{
|
||||
{ID: uintID(1)},
|
||||
{ID: uintID(2)},
|
||||
{ID: uintID(3)},
|
||||
bootnodes := []*enode.Node{
|
||||
newNode(uintID(1), nil),
|
||||
newNode(uintID(2), nil),
|
||||
newNode(uintID(3), nil),
|
||||
}
|
||||
table := fakeTable{
|
||||
{ID: uintID(4)},
|
||||
{ID: uintID(5)},
|
||||
{ID: uintID(6)},
|
||||
{ID: uintID(7)},
|
||||
{ID: uintID(8)},
|
||||
newNode(uintID(4), nil),
|
||||
newNode(uintID(5), nil),
|
||||
newNode(uintID(6), nil),
|
||||
newNode(uintID(7), nil),
|
||||
newNode(uintID(8), nil),
|
||||
}
|
||||
runDialTest(t, dialtest{
|
||||
init: newDialState(nil, bootnodes, table, 5, nil),
|
||||
|
@ -239,16 +241,16 @@ func TestDialStateDynDialBootnode(t *testing.T) {
|
|||
// 2 dynamic dials attempted, bootnodes pending fallback interval
|
||||
{
|
||||
new: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
|
||||
&discoverTask{},
|
||||
},
|
||||
},
|
||||
// No dials succeed, bootnodes still pending fallback interval
|
||||
{
|
||||
done: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
|
||||
},
|
||||
},
|
||||
// No dials succeed, bootnodes still pending fallback interval
|
||||
|
@ -256,51 +258,51 @@ func TestDialStateDynDialBootnode(t *testing.T) {
|
|||
// No dials succeed, 2 dynamic dials attempted and 1 bootnode too as fallback interval was reached
|
||||
{
|
||||
new: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(1), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
|
||||
},
|
||||
},
|
||||
// No dials succeed, 2nd bootnode is attempted
|
||||
{
|
||||
done: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(1), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
|
||||
},
|
||||
new: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(2), nil)},
|
||||
},
|
||||
},
|
||||
// No dials succeed, 3rd bootnode is attempted
|
||||
{
|
||||
done: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(2), nil)},
|
||||
},
|
||||
new: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
|
||||
},
|
||||
},
|
||||
// No dials succeed, 1st bootnode is attempted again, expired random nodes retried
|
||||
{
|
||||
done: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
|
||||
},
|
||||
new: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(1), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
|
||||
},
|
||||
},
|
||||
// Random dial succeeds, no more bootnodes are attempted
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(4)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(4), nil)}},
|
||||
},
|
||||
done: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(1), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -311,14 +313,14 @@ func TestDialStateDynDialFromTable(t *testing.T) {
|
|||
// This table always returns the same random nodes
|
||||
// in the order given below.
|
||||
table := fakeTable{
|
||||
{ID: uintID(1)},
|
||||
{ID: uintID(2)},
|
||||
{ID: uintID(3)},
|
||||
{ID: uintID(4)},
|
||||
{ID: uintID(5)},
|
||||
{ID: uintID(6)},
|
||||
{ID: uintID(7)},
|
||||
{ID: uintID(8)},
|
||||
newNode(uintID(1), nil),
|
||||
newNode(uintID(2), nil),
|
||||
newNode(uintID(3), nil),
|
||||
newNode(uintID(4), nil),
|
||||
newNode(uintID(5), nil),
|
||||
newNode(uintID(6), nil),
|
||||
newNode(uintID(7), nil),
|
||||
newNode(uintID(8), nil),
|
||||
}
|
||||
|
||||
runDialTest(t, dialtest{
|
||||
|
@ -327,63 +329,63 @@ func TestDialStateDynDialFromTable(t *testing.T) {
|
|||
// 5 out of 8 of the nodes returned by ReadRandomNodes are dialed.
|
||||
{
|
||||
new: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(1), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(2), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
|
||||
&discoverTask{},
|
||||
},
|
||||
},
|
||||
// Dialing nodes 1,2 succeeds. Dials from the lookup are launched.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
|
||||
},
|
||||
done: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
|
||||
&discoverTask{results: []*discover.Node{
|
||||
{ID: uintID(10)},
|
||||
{ID: uintID(11)},
|
||||
{ID: uintID(12)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(1), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(2), nil)},
|
||||
&discoverTask{results: []*enode.Node{
|
||||
newNode(uintID(10), nil),
|
||||
newNode(uintID(11), nil),
|
||||
newNode(uintID(12), nil),
|
||||
}},
|
||||
},
|
||||
new: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(10), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(11), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(12), nil)},
|
||||
&discoverTask{},
|
||||
},
|
||||
},
|
||||
// Dialing nodes 3,4,5 fails. The dials from the lookup succeed.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(10)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(11)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(12)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(10), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(11), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(12), nil)}},
|
||||
},
|
||||
done: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}},
|
||||
&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(10), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(11), nil)},
|
||||
&dialTask{flags: dynDialedConn, dest: newNode(uintID(12), nil)},
|
||||
},
|
||||
},
|
||||
// Waiting for expiry. No waitExpireTask is launched because the
|
||||
// discovery query is still running.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(10)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(11)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(12)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(10), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(11), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(12), nil)}},
|
||||
},
|
||||
},
|
||||
// Nodes 3,4 are not tried again because only the first two
|
||||
|
@ -391,30 +393,38 @@ func TestDialStateDynDialFromTable(t *testing.T) {
|
|||
// already connected.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(10)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(11)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(12)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(10), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(11), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(12), nil)}},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func newNode(id enode.ID, ip net.IP) *enode.Node {
|
||||
var r enr.Record
|
||||
if ip != nil {
|
||||
r.Set(enr.IP(ip))
|
||||
}
|
||||
return enode.SignNull(&r, id)
|
||||
}
|
||||
|
||||
// This test checks that candidates that do not match the netrestrict list are not dialed.
|
||||
func TestDialStateNetRestrict(t *testing.T) {
|
||||
// This table always returns the same random nodes
|
||||
// in the order given below.
|
||||
table := fakeTable{
|
||||
{ID: uintID(1), IP: net.ParseIP("127.0.0.1")},
|
||||
{ID: uintID(2), IP: net.ParseIP("127.0.0.2")},
|
||||
{ID: uintID(3), IP: net.ParseIP("127.0.0.3")},
|
||||
{ID: uintID(4), IP: net.ParseIP("127.0.0.4")},
|
||||
{ID: uintID(5), IP: net.ParseIP("127.0.2.5")},
|
||||
{ID: uintID(6), IP: net.ParseIP("127.0.2.6")},
|
||||
{ID: uintID(7), IP: net.ParseIP("127.0.2.7")},
|
||||
{ID: uintID(8), IP: net.ParseIP("127.0.2.8")},
|
||||
newNode(uintID(1), net.ParseIP("127.0.0.1")),
|
||||
newNode(uintID(2), net.ParseIP("127.0.0.2")),
|
||||
newNode(uintID(3), net.ParseIP("127.0.0.3")),
|
||||
newNode(uintID(4), net.ParseIP("127.0.0.4")),
|
||||
newNode(uintID(5), net.ParseIP("127.0.2.5")),
|
||||
newNode(uintID(6), net.ParseIP("127.0.2.6")),
|
||||
newNode(uintID(7), net.ParseIP("127.0.2.7")),
|
||||
newNode(uintID(8), net.ParseIP("127.0.2.8")),
|
||||
}
|
||||
restrict := new(netutil.Netlist)
|
||||
restrict.Add("127.0.2.0/24")
|
||||
|
@ -434,12 +444,12 @@ func TestDialStateNetRestrict(t *testing.T) {
|
|||
|
||||
// This test checks that static dials are launched.
|
||||
func TestDialStateStaticDial(t *testing.T) {
|
||||
wantStatic := []*discover.Node{
|
||||
{ID: uintID(1)},
|
||||
{ID: uintID(2)},
|
||||
{ID: uintID(3)},
|
||||
{ID: uintID(4)},
|
||||
{ID: uintID(5)},
|
||||
wantStatic := []*enode.Node{
|
||||
newNode(uintID(1), nil),
|
||||
newNode(uintID(2), nil),
|
||||
newNode(uintID(3), nil),
|
||||
newNode(uintID(4), nil),
|
||||
newNode(uintID(5), nil),
|
||||
}
|
||||
|
||||
runDialTest(t, dialtest{
|
||||
|
@ -449,40 +459,40 @@ func TestDialStateStaticDial(t *testing.T) {
|
|||
// aren't yet connected.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
|
||||
},
|
||||
new: []task{
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(3), nil)},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(4), nil)},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(5), nil)},
|
||||
},
|
||||
},
|
||||
// No new tasks are launched in this round because all static
|
||||
// nodes are either connected or still being dialed.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(3), nil)}},
|
||||
},
|
||||
done: []task{
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(3), nil)},
|
||||
},
|
||||
},
|
||||
// No new dial tasks are launched because all static
|
||||
// nodes are now connected.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(4)}},
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(5)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(3), nil)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(4), nil)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(5), nil)}},
|
||||
},
|
||||
done: []task{
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(4), nil)},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(5), nil)},
|
||||
},
|
||||
new: []task{
|
||||
&waitExpireTask{Duration: 14 * time.Second},
|
||||
|
@ -491,24 +501,24 @@ func TestDialStateStaticDial(t *testing.T) {
|
|||
// Wait a round for dial history to expire, no new tasks should spawn.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(4)}},
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(5)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(3), nil)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(4), nil)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(5), nil)}},
|
||||
},
|
||||
},
|
||||
// If a static node is dropped, it should be immediately redialed,
|
||||
// irrespective whether it was originally static or dynamic.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(5)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(3), nil)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(5), nil)}},
|
||||
},
|
||||
new: []task{
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(2), nil)},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(4), nil)},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -517,9 +527,9 @@ func TestDialStateStaticDial(t *testing.T) {
|
|||
|
||||
// This test checks that static peers will be redialed immediately if they were re-added to a static list.
|
||||
func TestDialStaticAfterReset(t *testing.T) {
|
||||
wantStatic := []*discover.Node{
|
||||
{ID: uintID(1)},
|
||||
{ID: uintID(2)},
|
||||
wantStatic := []*enode.Node{
|
||||
newNode(uintID(1), nil),
|
||||
newNode(uintID(2), nil),
|
||||
}
|
||||
|
||||
rounds := []round{
|
||||
|
@ -527,19 +537,19 @@ func TestDialStaticAfterReset(t *testing.T) {
|
|||
{
|
||||
peers: nil,
|
||||
new: []task{
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(1), nil)},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(2), nil)},
|
||||
},
|
||||
},
|
||||
// No new dial tasks, all peers are connected.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(2)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(2), nil)}},
|
||||
},
|
||||
done: []task{
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(1), nil)},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(2), nil)},
|
||||
},
|
||||
new: []task{
|
||||
&waitExpireTask{Duration: 30 * time.Second},
|
||||
|
@ -561,10 +571,10 @@ func TestDialStaticAfterReset(t *testing.T) {
|
|||
|
||||
// This test checks that past dials are not retried for some time.
|
||||
func TestDialStateCache(t *testing.T) {
|
||||
wantStatic := []*discover.Node{
|
||||
{ID: uintID(1)},
|
||||
{ID: uintID(2)},
|
||||
{ID: uintID(3)},
|
||||
wantStatic := []*enode.Node{
|
||||
newNode(uintID(1), nil),
|
||||
newNode(uintID(2), nil),
|
||||
newNode(uintID(3), nil),
|
||||
}
|
||||
|
||||
runDialTest(t, dialtest{
|
||||
|
@ -575,32 +585,32 @@ func TestDialStateCache(t *testing.T) {
|
|||
{
|
||||
peers: nil,
|
||||
new: []task{
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(1), nil)},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(2), nil)},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(3), nil)},
|
||||
},
|
||||
},
|
||||
// No new tasks are launched in this round because all static
|
||||
// nodes are either connected or still being dialed.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: staticDialedConn, id: uintID(2)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: staticDialedConn, node: newNode(uintID(2), nil)}},
|
||||
},
|
||||
done: []task{
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(1), nil)},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(2), nil)},
|
||||
},
|
||||
},
|
||||
// A salvage task is launched to wait for node 3's history
|
||||
// entry to expire.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
|
||||
},
|
||||
done: []task{
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(3), nil)},
|
||||
},
|
||||
new: []task{
|
||||
&waitExpireTask{Duration: 14 * time.Second},
|
||||
|
@ -609,18 +619,18 @@ func TestDialStateCache(t *testing.T) {
|
|||
// Still waiting for node 3's entry to expire in the cache.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
|
||||
},
|
||||
},
|
||||
// The cache entry for node 3 has expired and is retried.
|
||||
{
|
||||
peers: []*Peer{
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
|
||||
{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
|
||||
{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
|
||||
},
|
||||
new: []task{
|
||||
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
|
||||
&dialTask{flags: staticDialedConn, dest: newNode(uintID(3), nil)},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -628,12 +638,12 @@ func TestDialStateCache(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDialResolve(t *testing.T) {
|
||||
resolved := discover.NewNode(uintID(1), net.IP{127, 0, 55, 234}, 3333, 4444)
|
||||
resolved := newNode(uintID(1), net.IP{127, 0, 55, 234})
|
||||
table := &resolveMock{answer: resolved}
|
||||
state := newDialState(nil, nil, table, 0, nil)
|
||||
|
||||
// Check that the task is generated with an incomplete ID.
|
||||
dest := discover.NewNode(uintID(1), nil, 0, 0)
|
||||
dest := newNode(uintID(1), nil)
|
||||
state.addStatic(dest)
|
||||
tasks := state.newTasks(0, nil, time.Time{})
|
||||
if !reflect.DeepEqual(tasks, []task{&dialTask{flags: staticDialedConn, dest: dest}}) {
|
||||
|
@ -644,7 +654,7 @@ func TestDialResolve(t *testing.T) {
|
|||
config := Config{Dialer: TCPDialer{&net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}}}
|
||||
srv := &Server{ntab: table, Config: config}
|
||||
tasks[0].Do(srv)
|
||||
if !reflect.DeepEqual(table.resolveCalls, []discover.NodeID{dest.ID}) {
|
||||
if !reflect.DeepEqual(table.resolveCalls, []*enode.Node{dest}) {
|
||||
t.Fatalf("wrong resolve calls, got %v", table.resolveCalls)
|
||||
}
|
||||
|
||||
|
@ -672,25 +682,24 @@ next:
|
|||
return true
|
||||
}
|
||||
|
||||
func uintID(i uint32) discover.NodeID {
|
||||
var id discover.NodeID
|
||||
func uintID(i uint32) enode.ID {
|
||||
var id enode.ID
|
||||
binary.BigEndian.PutUint32(id[:], i)
|
||||
return id
|
||||
}
|
||||
|
||||
// implements discoverTable for TestDialResolve
|
||||
type resolveMock struct {
|
||||
resolveCalls []discover.NodeID
|
||||
answer *discover.Node
|
||||
resolveCalls []*enode.Node
|
||||
answer *enode.Node
|
||||
}
|
||||
|
||||
func (t *resolveMock) Resolve(id discover.NodeID) *discover.Node {
|
||||
t.resolveCalls = append(t.resolveCalls, id)
|
||||
func (t *resolveMock) Resolve(n *enode.Node) *enode.Node {
|
||||
t.resolveCalls = append(t.resolveCalls, n)
|
||||
return t.answer
|
||||
}
|
||||
|
||||
func (t *resolveMock) Self() *discover.Node { return new(discover.Node) }
|
||||
func (t *resolveMock) Close() {}
|
||||
func (t *resolveMock) Bootstrap([]*discover.Node) {}
|
||||
func (t *resolveMock) Lookup(discover.NodeID) []*discover.Node { return nil }
|
||||
func (t *resolveMock) ReadRandomNodes(buf []*discover.Node) int { return 0 }
|
||||
func (t *resolveMock) Self() *enode.Node { return new(enode.Node) }
|
||||
func (t *resolveMock) Close() {}
|
||||
func (t *resolveMock) LookupRandom() []*enode.Node { return nil }
|
||||
func (t *resolveMock) ReadRandomNodes(buf []*enode.Node) int { return 0 }
|
||||
|
|
|
@ -18,415 +18,87 @@ package discover
|
|||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
)
|
||||
|
||||
const NodeIDBits = 512
|
||||
|
||||
// Node represents a host on the network.
|
||||
// node represents a host on the network.
|
||||
// The fields of Node may not be modified.
|
||||
type Node struct {
|
||||
IP net.IP // len 4 for IPv4 or 16 for IPv6
|
||||
UDP, TCP uint16 // port numbers
|
||||
ID NodeID // the node's public key
|
||||
|
||||
// This is a cached copy of sha3(ID) which is used for node
|
||||
// distance calculations. This is part of Node in order to make it
|
||||
// possible to write tests that need a node at a certain distance.
|
||||
// In those tests, the content of sha will not actually correspond
|
||||
// with ID.
|
||||
sha common.Hash
|
||||
|
||||
// Time when the node was added to the table.
|
||||
addedAt time.Time
|
||||
type node struct {
|
||||
enode.Node
|
||||
addedAt time.Time // time when the node was added to the table
|
||||
}
|
||||
|
||||
// NewNode creates a new node. It is mostly meant to be used for
|
||||
// testing purposes.
|
||||
func NewNode(id NodeID, ip net.IP, udpPort, tcpPort uint16) *Node {
|
||||
if ipv4 := ip.To4(); ipv4 != nil {
|
||||
ip = ipv4
|
||||
}
|
||||
return &Node{
|
||||
IP: ip,
|
||||
UDP: udpPort,
|
||||
TCP: tcpPort,
|
||||
ID: id,
|
||||
sha: crypto.Keccak256Hash(id[:]),
|
||||
}
|
||||
type encPubkey [64]byte
|
||||
|
||||
func encodePubkey(key *ecdsa.PublicKey) encPubkey {
|
||||
var e encPubkey
|
||||
math.ReadBits(key.X, e[:len(e)/2])
|
||||
math.ReadBits(key.Y, e[len(e)/2:])
|
||||
return e
|
||||
}
|
||||
|
||||
func (n *Node) addr() *net.UDPAddr {
|
||||
return &net.UDPAddr{IP: n.IP, Port: int(n.UDP)}
|
||||
}
|
||||
|
||||
// Incomplete returns true for nodes with no IP address.
|
||||
func (n *Node) Incomplete() bool {
|
||||
return n.IP == nil
|
||||
}
|
||||
|
||||
// checks whether n is a valid complete node.
|
||||
func (n *Node) validateComplete() error {
|
||||
if n.Incomplete() {
|
||||
return errors.New("incomplete node")
|
||||
}
|
||||
if n.UDP == 0 {
|
||||
return errors.New("missing UDP port")
|
||||
}
|
||||
if n.TCP == 0 {
|
||||
return errors.New("missing TCP port")
|
||||
}
|
||||
if n.IP.IsMulticast() || n.IP.IsUnspecified() {
|
||||
return errors.New("invalid IP (multicast/unspecified)")
|
||||
}
|
||||
_, err := n.ID.Pubkey() // validate the key (on curve, etc.)
|
||||
return err
|
||||
}
|
||||
|
||||
// The string representation of a Node is a URL.
|
||||
// Please see ParseNode for a description of the format.
|
||||
func (n *Node) String() string {
|
||||
u := url.URL{Scheme: "enode"}
|
||||
if n.Incomplete() {
|
||||
u.Host = fmt.Sprintf("%x", n.ID[:])
|
||||
} else {
|
||||
addr := net.TCPAddr{IP: n.IP, Port: int(n.TCP)}
|
||||
u.User = url.User(fmt.Sprintf("%x", n.ID[:]))
|
||||
u.Host = addr.String()
|
||||
if n.UDP != n.TCP {
|
||||
u.RawQuery = "discport=" + strconv.Itoa(int(n.UDP))
|
||||
}
|
||||
}
|
||||
return u.String()
|
||||
}
|
||||
|
||||
var incompleteNodeURL = regexp.MustCompile("(?i)^(?:enode://)?([0-9a-f]+)$")
|
||||
|
||||
// ParseNode parses a node designator.
|
||||
//
|
||||
// There are two basic forms of node designators
|
||||
// - incomplete nodes, which only have the public key (node ID)
|
||||
// - complete nodes, which contain the public key and IP/Port information
|
||||
//
|
||||
// For incomplete nodes, the designator must look like one of these
|
||||
//
|
||||
// enode://<hex node id>
|
||||
// <hex node id>
|
||||
//
|
||||
// For complete nodes, the node ID is encoded in the username portion
|
||||
// of the URL, separated from the host by an @ sign. The hostname can
|
||||
// only be given as an IP address, DNS domain names are not allowed.
|
||||
// The port in the host name section is the TCP listening port. If the
|
||||
// TCP and UDP (discovery) ports differ, the UDP port is specified as
|
||||
// query parameter "discport".
|
||||
//
|
||||
// In the following example, the node URL describes
|
||||
// a node with IP address 10.3.58.6, TCP listening port 30303
|
||||
// and UDP discovery port 30301.
|
||||
//
|
||||
// enode://<hex node id>@10.3.58.6:30303?discport=30301
|
||||
func ParseNode(rawurl string) (*Node, error) {
|
||||
if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil {
|
||||
id, err := HexID(m[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid node ID (%v)", err)
|
||||
}
|
||||
return NewNode(id, nil, 0, 0), nil
|
||||
}
|
||||
return parseComplete(rawurl)
|
||||
}
|
||||
|
||||
func parseComplete(rawurl string) (*Node, error) {
|
||||
var (
|
||||
id NodeID
|
||||
ip net.IP
|
||||
tcpPort, udpPort uint64
|
||||
)
|
||||
u, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if u.Scheme != "enode" {
|
||||
return nil, errors.New("invalid URL scheme, want \"enode\"")
|
||||
}
|
||||
// Parse the Node ID from the user portion.
|
||||
if u.User == nil {
|
||||
return nil, errors.New("does not contain node ID")
|
||||
}
|
||||
if id, err = HexID(u.User.String()); err != nil {
|
||||
return nil, fmt.Errorf("invalid node ID (%v)", err)
|
||||
}
|
||||
// Parse the IP address.
|
||||
host, port, err := net.SplitHostPort(u.Host)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid host: %v", err)
|
||||
}
|
||||
if ip = net.ParseIP(host); ip == nil {
|
||||
return nil, errors.New("invalid IP address")
|
||||
}
|
||||
// Ensure the IP is 4 bytes long for IPv4 addresses.
|
||||
if ipv4 := ip.To4(); ipv4 != nil {
|
||||
ip = ipv4
|
||||
}
|
||||
// Parse the port numbers.
|
||||
if tcpPort, err = strconv.ParseUint(port, 10, 16); err != nil {
|
||||
return nil, errors.New("invalid port")
|
||||
}
|
||||
udpPort = tcpPort
|
||||
qv := u.Query()
|
||||
if qv.Get("discport") != "" {
|
||||
udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid discport in query")
|
||||
}
|
||||
}
|
||||
return NewNode(id, ip, uint16(udpPort), uint16(tcpPort)), nil
|
||||
}
|
||||
|
||||
// MustParseNode parses a node URL. It panics if the URL is not valid.
|
||||
func MustParseNode(rawurl string) *Node {
|
||||
n, err := ParseNode(rawurl)
|
||||
if err != nil {
|
||||
panic("invalid node URL: " + err.Error())
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// MarshalText implements encoding.TextMarshaler.
|
||||
func (n *Node) MarshalText() ([]byte, error) {
|
||||
return []byte(n.String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||
func (n *Node) UnmarshalText(text []byte) error {
|
||||
dec, err := ParseNode(string(text))
|
||||
if err == nil {
|
||||
*n = *dec
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// NodeID is a unique identifier for each node.
|
||||
// The node identifier is a marshaled elliptic curve public key.
|
||||
type NodeID [NodeIDBits / 8]byte
|
||||
|
||||
// Bytes returns a byte slice representation of the NodeID
|
||||
func (n NodeID) Bytes() []byte {
|
||||
return n[:]
|
||||
}
|
||||
|
||||
// NodeID prints as a long hexadecimal number.
|
||||
func (n NodeID) String() string {
|
||||
return fmt.Sprintf("%x", n[:])
|
||||
}
|
||||
|
||||
// The Go syntax representation of a NodeID is a call to HexID.
|
||||
func (n NodeID) GoString() string {
|
||||
return fmt.Sprintf("discover.HexID(\"%x\")", n[:])
|
||||
}
|
||||
|
||||
// TerminalString returns a shortened hex string for terminal logging.
|
||||
func (n NodeID) TerminalString() string {
|
||||
return hex.EncodeToString(n[:8])
|
||||
}
|
||||
|
||||
// MarshalText implements the encoding.TextMarshaler interface.
|
||||
func (n NodeID) MarshalText() ([]byte, error) {
|
||||
return []byte(hex.EncodeToString(n[:])), nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
||||
func (n *NodeID) UnmarshalText(text []byte) error {
|
||||
id, err := HexID(string(text))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*n = id
|
||||
return nil
|
||||
}
|
||||
|
||||
// BytesID converts a byte slice to a NodeID
|
||||
func BytesID(b []byte) (NodeID, error) {
|
||||
var id NodeID
|
||||
if len(b) != len(id) {
|
||||
return id, fmt.Errorf("wrong length, want %d bytes", len(id))
|
||||
}
|
||||
copy(id[:], b)
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// MustBytesID converts a byte slice to a NodeID.
|
||||
// It panics if the byte slice is not a valid NodeID.
|
||||
func MustBytesID(b []byte) NodeID {
|
||||
id, err := BytesID(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// HexID converts a hex string to a NodeID.
|
||||
// The string may be prefixed with 0x.
|
||||
func HexID(in string) (NodeID, error) {
|
||||
var id NodeID
|
||||
b, err := hex.DecodeString(strings.TrimPrefix(in, "0x"))
|
||||
if err != nil {
|
||||
return id, err
|
||||
} else if len(b) != len(id) {
|
||||
return id, fmt.Errorf("wrong length, want %d hex chars", len(id)*2)
|
||||
}
|
||||
copy(id[:], b)
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// MustHexID converts a hex string to a NodeID.
|
||||
// It panics if the string is not a valid NodeID.
|
||||
func MustHexID(in string) NodeID {
|
||||
id, err := HexID(in)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// PubkeyID returns a marshaled representation of the given public key.
|
||||
func PubkeyID(pub *ecdsa.PublicKey) NodeID {
|
||||
var id NodeID
|
||||
pbytes := elliptic.Marshal(pub.Curve, pub.X, pub.Y)
|
||||
if len(pbytes)-1 != len(id) {
|
||||
panic(fmt.Errorf("need %d bit pubkey, got %d bits", (len(id)+1)*8, len(pbytes)))
|
||||
}
|
||||
copy(id[:], pbytes[1:])
|
||||
return id
|
||||
}
|
||||
|
||||
// Pubkey returns the public key represented by the node ID.
|
||||
// It returns an error if the ID is not a point on the curve.
|
||||
func (id NodeID) Pubkey() (*ecdsa.PublicKey, error) {
|
||||
func decodePubkey(e encPubkey) (*ecdsa.PublicKey, error) {
|
||||
p := &ecdsa.PublicKey{Curve: crypto.S256(), X: new(big.Int), Y: new(big.Int)}
|
||||
half := len(id) / 2
|
||||
p.X.SetBytes(id[:half])
|
||||
p.Y.SetBytes(id[half:])
|
||||
half := len(e) / 2
|
||||
p.X.SetBytes(e[:half])
|
||||
p.Y.SetBytes(e[half:])
|
||||
if !p.Curve.IsOnCurve(p.X, p.Y) {
|
||||
return nil, errors.New("id is invalid secp256k1 curve point")
|
||||
return nil, errors.New("invalid secp256k1 curve point")
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// recoverNodeID computes the public key used to sign the
|
||||
func (e encPubkey) id() enode.ID {
|
||||
return enode.ID(crypto.Keccak256Hash(e[:]))
|
||||
}
|
||||
|
||||
// recoverNodeKey computes the public key used to sign the
|
||||
// given hash from the signature.
|
||||
func recoverNodeID(hash, sig []byte) (id NodeID, err error) {
|
||||
func recoverNodeKey(hash, sig []byte) (key encPubkey, err error) {
|
||||
pubkey, err := secp256k1.RecoverPubkey(hash, sig)
|
||||
if err != nil {
|
||||
return id, err
|
||||
return key, err
|
||||
}
|
||||
if len(pubkey)-1 != len(id) {
|
||||
return id, fmt.Errorf("recovered pubkey has %d bits, want %d bits", len(pubkey)*8, (len(id)+1)*8)
|
||||
}
|
||||
for i := range id {
|
||||
id[i] = pubkey[i+1]
|
||||
}
|
||||
return id, nil
|
||||
copy(key[:], pubkey[1:])
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// distcmp compares the distances a->target and b->target.
|
||||
// Returns -1 if a is closer to target, 1 if b is closer to target
|
||||
// and 0 if they are equal.
|
||||
func distcmp(target, a, b common.Hash) int {
|
||||
for i := range target {
|
||||
da := a[i] ^ target[i]
|
||||
db := b[i] ^ target[i]
|
||||
if da > db {
|
||||
return 1
|
||||
} else if da < db {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
func wrapNode(n *enode.Node) *node {
|
||||
return &node{Node: *n}
|
||||
}
|
||||
|
||||
// table of leading zero counts for bytes [0..255]
|
||||
var lzcount = [256]int{
|
||||
8, 7, 6, 6, 5, 5, 5, 5,
|
||||
4, 4, 4, 4, 4, 4, 4, 4,
|
||||
3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3,
|
||||
2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
func wrapNodes(ns []*enode.Node) []*node {
|
||||
result := make([]*node, len(ns))
|
||||
for i, n := range ns {
|
||||
result[i] = wrapNode(n)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// logdist returns the logarithmic distance between a and b, log2(a ^ b).
|
||||
func logdist(a, b common.Hash) int {
|
||||
lz := 0
|
||||
for i := range a {
|
||||
x := a[i] ^ b[i]
|
||||
if x == 0 {
|
||||
lz += 8
|
||||
} else {
|
||||
lz += lzcount[x]
|
||||
break
|
||||
}
|
||||
}
|
||||
return len(a)*8 - lz
|
||||
func unwrapNode(n *node) *enode.Node {
|
||||
return &n.Node
|
||||
}
|
||||
|
||||
// hashAtDistance returns a random hash such that logdist(a, b) == n
|
||||
func hashAtDistance(a common.Hash, n int) (b common.Hash) {
|
||||
if n == 0 {
|
||||
return a
|
||||
func unwrapNodes(ns []*node) []*enode.Node {
|
||||
result := make([]*enode.Node, len(ns))
|
||||
for i, n := range ns {
|
||||
result[i] = unwrapNode(n)
|
||||
}
|
||||
// flip bit at position n, fill the rest with random bits
|
||||
b = a
|
||||
pos := len(a) - n/8 - 1
|
||||
bit := byte(0x01) << (byte(n%8) - 1)
|
||||
if bit == 0 {
|
||||
pos++
|
||||
bit = 0x80
|
||||
}
|
||||
b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits
|
||||
for i := pos + 1; i < len(a); i++ {
|
||||
b[i] = byte(rand.Intn(255))
|
||||
}
|
||||
return b
|
||||
return result
|
||||
}
|
||||
|
||||
func (n *node) addr() *net.UDPAddr {
|
||||
return &net.UDPAddr{IP: n.IP(), Port: n.UDP()}
|
||||
}
|
||||
|
||||
func (n *node) String() string {
|
||||
return n.Node.String()
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
package discover
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
crand "crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
@ -35,6 +36,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
)
|
||||
|
||||
|
@ -65,49 +67,44 @@ const (
|
|||
type Table struct {
|
||||
mutex sync.Mutex // protects buckets, bucket content, nursery, rand
|
||||
buckets [nBuckets]*bucket // index of known nodes by distance
|
||||
nursery []*Node // bootstrap nodes
|
||||
nursery []*node // bootstrap nodes
|
||||
rand *mrand.Rand // source of randomness, periodically reseeded
|
||||
ips netutil.DistinctNetSet
|
||||
|
||||
db *nodeDB // database of known nodes
|
||||
db *enode.DB // database of known nodes
|
||||
refreshReq chan chan struct{}
|
||||
initDone chan struct{}
|
||||
closeReq chan struct{}
|
||||
closed chan struct{}
|
||||
|
||||
nodeAddedHook func(*Node) // for testing
|
||||
nodeAddedHook func(*node) // for testing
|
||||
|
||||
net transport
|
||||
self *Node // metadata of the local node
|
||||
self *node // metadata of the local node
|
||||
}
|
||||
|
||||
// transport is implemented by the UDP transport.
|
||||
// it is an interface so we can test without opening lots of UDP
|
||||
// sockets and without generating a private key.
|
||||
type transport interface {
|
||||
ping(NodeID, *net.UDPAddr) error
|
||||
findnode(toid NodeID, addr *net.UDPAddr, target NodeID) ([]*Node, error)
|
||||
ping(enode.ID, *net.UDPAddr) error
|
||||
findnode(toid enode.ID, addr *net.UDPAddr, target encPubkey) ([]*node, error)
|
||||
close()
|
||||
}
|
||||
|
||||
// bucket contains nodes, ordered by their last activity. the entry
|
||||
// that was most recently active is the first element in entries.
|
||||
type bucket struct {
|
||||
entries []*Node // live entries, sorted by time of last contact
|
||||
replacements []*Node // recently seen nodes to be used if revalidation fails
|
||||
entries []*node // live entries, sorted by time of last contact
|
||||
replacements []*node // recently seen nodes to be used if revalidation fails
|
||||
ips netutil.DistinctNetSet
|
||||
}
|
||||
|
||||
func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string, bootnodes []*Node) (*Table, error) {
|
||||
// If no node database was given, use an in-memory one
|
||||
db, err := newNodeDB(nodeDBPath, nodeDBVersion, ourID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func newTable(t transport, self *enode.Node, db *enode.DB, bootnodes []*enode.Node) (*Table, error) {
|
||||
tab := &Table{
|
||||
net: t,
|
||||
db: db,
|
||||
self: NewNode(ourID, ourAddr.IP, uint16(ourAddr.Port), uint16(ourAddr.Port)),
|
||||
self: wrapNode(self),
|
||||
refreshReq: make(chan chan struct{}),
|
||||
initDone: make(chan struct{}),
|
||||
closeReq: make(chan struct{}),
|
||||
|
@ -125,10 +122,7 @@ func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string
|
|||
}
|
||||
tab.seedRand()
|
||||
tab.loadSeedNodes()
|
||||
// Start the background expiration goroutine after loading seeds so that the search for
|
||||
// seed nodes also considers older nodes that would otherwise be removed by the
|
||||
// expiration.
|
||||
tab.db.ensureExpirer()
|
||||
|
||||
go tab.loop()
|
||||
return tab, nil
|
||||
}
|
||||
|
@ -143,15 +137,13 @@ func (tab *Table) seedRand() {
|
|||
}
|
||||
|
||||
// Self returns the local node.
|
||||
// The returned node should not be modified by the caller.
|
||||
func (tab *Table) Self() *Node {
|
||||
return tab.self
|
||||
func (tab *Table) Self() *enode.Node {
|
||||
return unwrapNode(tab.self)
|
||||
}
|
||||
|
||||
// ReadRandomNodes fills the given slice with random nodes from the
|
||||
// table. It will not write the same node more than once. The nodes in
|
||||
// the slice are copies and can be modified by the caller.
|
||||
func (tab *Table) ReadRandomNodes(buf []*Node) (n int) {
|
||||
// ReadRandomNodes fills the given slice with random nodes from the table. The results
|
||||
// are guaranteed to be unique for a single invocation, no node will appear twice.
|
||||
func (tab *Table) ReadRandomNodes(buf []*enode.Node) (n int) {
|
||||
if !tab.isInitDone() {
|
||||
return 0
|
||||
}
|
||||
|
@ -159,7 +151,7 @@ func (tab *Table) ReadRandomNodes(buf []*Node) (n int) {
|
|||
defer tab.mutex.Unlock()
|
||||
|
||||
// Find all non-empty buckets and get a fresh slice of their entries.
|
||||
var buckets [][]*Node
|
||||
var buckets [][]*node
|
||||
for _, b := range &tab.buckets {
|
||||
if len(b.entries) > 0 {
|
||||
buckets = append(buckets, b.entries)
|
||||
|
@ -177,7 +169,7 @@ func (tab *Table) ReadRandomNodes(buf []*Node) (n int) {
|
|||
var i, j int
|
||||
for ; i < len(buf); i, j = i+1, (j+1)%len(buckets) {
|
||||
b := buckets[j]
|
||||
buf[i] = &(*b[0])
|
||||
buf[i] = unwrapNode(b[0])
|
||||
buckets[j] = b[1:]
|
||||
if len(b) == 1 {
|
||||
buckets = append(buckets[:j], buckets[j+1:]...)
|
||||
|
@ -202,20 +194,13 @@ func (tab *Table) Close() {
|
|||
// setFallbackNodes sets the initial points of contact. These nodes
|
||||
// are used to connect to the network if the table is empty and there
|
||||
// are no known nodes in the database.
|
||||
func (tab *Table) setFallbackNodes(nodes []*Node) error {
|
||||
func (tab *Table) setFallbackNodes(nodes []*enode.Node) error {
|
||||
for _, n := range nodes {
|
||||
if err := n.validateComplete(); err != nil {
|
||||
return fmt.Errorf("bad bootstrap/fallback node %q (%v)", n, err)
|
||||
if err := n.ValidateComplete(); err != nil {
|
||||
return fmt.Errorf("bad bootstrap node %q: %v", n, err)
|
||||
}
|
||||
}
|
||||
tab.nursery = make([]*Node, 0, len(nodes))
|
||||
for _, n := range nodes {
|
||||
cpy := *n
|
||||
// Recompute cpy.sha because the node might not have been
|
||||
// created by NewNode or ParseNode.
|
||||
cpy.sha = crypto.Keccak256Hash(n.ID[:])
|
||||
tab.nursery = append(tab.nursery, &cpy)
|
||||
}
|
||||
tab.nursery = wrapNodes(nodes)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -231,47 +216,48 @@ func (tab *Table) isInitDone() bool {
|
|||
|
||||
// Resolve searches for a specific node with the given ID.
|
||||
// It returns nil if the node could not be found.
|
||||
func (tab *Table) Resolve(targetID NodeID) *Node {
|
||||
func (tab *Table) Resolve(n *enode.Node) *enode.Node {
|
||||
// If the node is present in the local table, no
|
||||
// network interaction is required.
|
||||
hash := crypto.Keccak256Hash(targetID[:])
|
||||
hash := n.ID()
|
||||
tab.mutex.Lock()
|
||||
cl := tab.closest(hash, 1)
|
||||
tab.mutex.Unlock()
|
||||
if len(cl.entries) > 0 && cl.entries[0].ID == targetID {
|
||||
return cl.entries[0]
|
||||
if len(cl.entries) > 0 && cl.entries[0].ID() == hash {
|
||||
return unwrapNode(cl.entries[0])
|
||||
}
|
||||
// Otherwise, do a network lookup.
|
||||
result := tab.Lookup(targetID)
|
||||
result := tab.lookup(encodePubkey(n.Pubkey()), true)
|
||||
for _, n := range result {
|
||||
if n.ID == targetID {
|
||||
return n
|
||||
if n.ID() == hash {
|
||||
return unwrapNode(n)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Lookup performs a network search for nodes close
|
||||
// to the given target. It approaches the target by querying
|
||||
// nodes that are closer to it on each iteration.
|
||||
// The given target does not need to be an actual node
|
||||
// identifier.
|
||||
func (tab *Table) Lookup(targetID NodeID) []*Node {
|
||||
return tab.lookup(targetID, true)
|
||||
// LookupRandom finds random nodes in the network.
|
||||
func (tab *Table) LookupRandom() []*enode.Node {
|
||||
var target encPubkey
|
||||
crand.Read(target[:])
|
||||
return unwrapNodes(tab.lookup(target, true))
|
||||
}
|
||||
|
||||
func (tab *Table) lookup(targetID NodeID, refreshIfEmpty bool) []*Node {
|
||||
// lookup performs a network search for nodes close to the given target. It approaches the
|
||||
// target by querying nodes that are closer to it on each iteration. The given target does
|
||||
// not need to be an actual node identifier.
|
||||
func (tab *Table) lookup(targetKey encPubkey, refreshIfEmpty bool) []*node {
|
||||
var (
|
||||
target = crypto.Keccak256Hash(targetID[:])
|
||||
asked = make(map[NodeID]bool)
|
||||
seen = make(map[NodeID]bool)
|
||||
reply = make(chan []*Node, alpha)
|
||||
target = enode.ID(crypto.Keccak256Hash(targetKey[:]))
|
||||
asked = make(map[enode.ID]bool)
|
||||
seen = make(map[enode.ID]bool)
|
||||
reply = make(chan []*node, alpha)
|
||||
pendingQueries = 0
|
||||
result *nodesByDistance
|
||||
)
|
||||
// don't query further if we hit ourself.
|
||||
// unlikely to happen often in practice.
|
||||
asked[tab.self.ID] = true
|
||||
asked[tab.self.ID()] = true
|
||||
|
||||
for {
|
||||
tab.mutex.Lock()
|
||||
|
@ -293,10 +279,10 @@ func (tab *Table) lookup(targetID NodeID, refreshIfEmpty bool) []*Node {
|
|||
// ask the alpha closest nodes that we haven't asked yet
|
||||
for i := 0; i < len(result.entries) && pendingQueries < alpha; i++ {
|
||||
n := result.entries[i]
|
||||
if !asked[n.ID] {
|
||||
asked[n.ID] = true
|
||||
if !asked[n.ID()] {
|
||||
asked[n.ID()] = true
|
||||
pendingQueries++
|
||||
go tab.findnode(n, targetID, reply)
|
||||
go tab.findnode(n, targetKey, reply)
|
||||
}
|
||||
}
|
||||
if pendingQueries == 0 {
|
||||
|
@ -305,8 +291,8 @@ func (tab *Table) lookup(targetID NodeID, refreshIfEmpty bool) []*Node {
|
|||
}
|
||||
// wait for the next reply
|
||||
for _, n := range <-reply {
|
||||
if n != nil && !seen[n.ID] {
|
||||
seen[n.ID] = true
|
||||
if n != nil && !seen[n.ID()] {
|
||||
seen[n.ID()] = true
|
||||
result.push(n, bucketSize)
|
||||
}
|
||||
}
|
||||
|
@ -315,19 +301,19 @@ func (tab *Table) lookup(targetID NodeID, refreshIfEmpty bool) []*Node {
|
|||
return result.entries
|
||||
}
|
||||
|
||||
func (tab *Table) findnode(n *Node, targetID NodeID, reply chan<- []*Node) {
|
||||
fails := tab.db.findFails(n.ID)
|
||||
r, err := tab.net.findnode(n.ID, n.addr(), targetID)
|
||||
func (tab *Table) findnode(n *node, targetKey encPubkey, reply chan<- []*node) {
|
||||
fails := tab.db.FindFails(n.ID())
|
||||
r, err := tab.net.findnode(n.ID(), n.addr(), targetKey)
|
||||
if err != nil || len(r) == 0 {
|
||||
fails++
|
||||
tab.db.updateFindFails(n.ID, fails)
|
||||
log.Trace("Findnode failed", "id", n.ID, "failcount", fails, "err", err)
|
||||
tab.db.UpdateFindFails(n.ID(), fails)
|
||||
log.Trace("Findnode failed", "id", n.ID(), "failcount", fails, "err", err)
|
||||
if fails >= maxFindnodeFailures {
|
||||
log.Trace("Too many findnode failures, dropping", "id", n.ID, "failcount", fails)
|
||||
log.Trace("Too many findnode failures, dropping", "id", n.ID(), "failcount", fails)
|
||||
tab.delete(n)
|
||||
}
|
||||
} else if fails > 0 {
|
||||
tab.db.updateFindFails(n.ID, fails-1)
|
||||
tab.db.UpdateFindFails(n.ID(), fails-1)
|
||||
}
|
||||
|
||||
// Grab as many nodes as possible. Some of them might not be alive anymore, but we'll
|
||||
|
@ -405,7 +391,6 @@ loop:
|
|||
for _, ch := range waiting {
|
||||
close(ch)
|
||||
}
|
||||
tab.db.close()
|
||||
close(tab.closed)
|
||||
}
|
||||
|
||||
|
@ -421,7 +406,11 @@ func (tab *Table) doRefresh(done chan struct{}) {
|
|||
tab.loadSeedNodes()
|
||||
|
||||
// Run self lookup to discover new neighbor nodes.
|
||||
tab.lookup(tab.self.ID, false)
|
||||
// We can only do this if we have a secp256k1 identity.
|
||||
var key ecdsa.PublicKey
|
||||
if err := tab.self.Load((*enode.Secp256k1)(&key)); err == nil {
|
||||
tab.lookup(encodePubkey(&key), false)
|
||||
}
|
||||
|
||||
// The Kademlia paper specifies that the bucket refresh should
|
||||
// perform a lookup in the least recently used bucket. We cannot
|
||||
|
@ -430,19 +419,19 @@ func (tab *Table) doRefresh(done chan struct{}) {
|
|||
// sha3 preimage that falls into a chosen bucket.
|
||||
// We perform a few lookups with a random target instead.
|
||||
for i := 0; i < 3; i++ {
|
||||
var target NodeID
|
||||
var target encPubkey
|
||||
crand.Read(target[:])
|
||||
tab.lookup(target, false)
|
||||
}
|
||||
}
|
||||
|
||||
func (tab *Table) loadSeedNodes() {
|
||||
seeds := tab.db.querySeeds(seedCount, seedMaxAge)
|
||||
seeds := wrapNodes(tab.db.QuerySeeds(seedCount, seedMaxAge))
|
||||
seeds = append(seeds, tab.nursery...)
|
||||
for i := range seeds {
|
||||
seed := seeds[i]
|
||||
age := log.Lazy{Fn: func() interface{} { return time.Since(tab.db.lastPongReceived(seed.ID)) }}
|
||||
log.Debug("Found seed node in database", "id", seed.ID, "addr", seed.addr(), "age", age)
|
||||
age := log.Lazy{Fn: func() interface{} { return time.Since(tab.db.LastPongReceived(seed.ID())) }}
|
||||
log.Debug("Found seed node in database", "id", seed.ID(), "addr", seed.addr(), "age", age)
|
||||
tab.add(seed)
|
||||
}
|
||||
}
|
||||
|
@ -459,28 +448,28 @@ func (tab *Table) doRevalidate(done chan<- struct{}) {
|
|||
}
|
||||
|
||||
// Ping the selected node and wait for a pong.
|
||||
err := tab.net.ping(last.ID, last.addr())
|
||||
err := tab.net.ping(last.ID(), last.addr())
|
||||
|
||||
tab.mutex.Lock()
|
||||
defer tab.mutex.Unlock()
|
||||
b := tab.buckets[bi]
|
||||
if err == nil {
|
||||
// The node responded, move it to the front.
|
||||
log.Trace("Revalidated node", "b", bi, "id", last.ID)
|
||||
log.Debug("Revalidated node", "b", bi, "id", last.ID())
|
||||
b.bump(last)
|
||||
return
|
||||
}
|
||||
// No reply received, pick a replacement or delete the node if there aren't
|
||||
// any replacements.
|
||||
if r := tab.replace(b, last); r != nil {
|
||||
log.Trace("Replaced dead node", "b", bi, "id", last.ID, "ip", last.IP, "r", r.ID, "rip", r.IP)
|
||||
log.Debug("Replaced dead node", "b", bi, "id", last.ID(), "ip", last.IP(), "r", r.ID(), "rip", r.IP())
|
||||
} else {
|
||||
log.Trace("Removed dead node", "b", bi, "id", last.ID, "ip", last.IP)
|
||||
log.Debug("Removed dead node", "b", bi, "id", last.ID(), "ip", last.IP())
|
||||
}
|
||||
}
|
||||
|
||||
// nodeToRevalidate returns the last node in a random, non-empty bucket.
|
||||
func (tab *Table) nodeToRevalidate() (n *Node, bi int) {
|
||||
func (tab *Table) nodeToRevalidate() (n *node, bi int) {
|
||||
tab.mutex.Lock()
|
||||
defer tab.mutex.Unlock()
|
||||
|
||||
|
@ -511,7 +500,7 @@ func (tab *Table) copyLiveNodes() {
|
|||
for _, b := range &tab.buckets {
|
||||
for _, n := range b.entries {
|
||||
if now.Sub(n.addedAt) >= seedMinTableTime {
|
||||
tab.db.updateNode(n)
|
||||
tab.db.UpdateNode(unwrapNode(n))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -519,7 +508,7 @@ func (tab *Table) copyLiveNodes() {
|
|||
|
||||
// closest returns the n nodes in the table that are closest to the
|
||||
// given id. The caller must hold tab.mutex.
|
||||
func (tab *Table) closest(target common.Hash, nresults int) *nodesByDistance {
|
||||
func (tab *Table) closest(target enode.ID, nresults int) *nodesByDistance {
|
||||
// This is a very wasteful way to find the closest nodes but
|
||||
// obviously correct. I believe that tree-based buckets would make
|
||||
// this easier to implement efficiently.
|
||||
|
@ -540,8 +529,8 @@ func (tab *Table) len() (n int) {
|
|||
}
|
||||
|
||||
// bucket returns the bucket for the given node ID hash.
|
||||
func (tab *Table) bucket(sha common.Hash) *bucket {
|
||||
d := logdist(tab.self.sha, sha)
|
||||
func (tab *Table) bucket(id enode.ID) *bucket {
|
||||
d := enode.LogDist(tab.self.ID(), id)
|
||||
if d <= bucketMinDistance {
|
||||
return tab.buckets[0]
|
||||
}
|
||||
|
@ -553,11 +542,14 @@ func (tab *Table) bucket(sha common.Hash) *bucket {
|
|||
// least recently active node in the bucket does not respond to a ping packet.
|
||||
//
|
||||
// The caller must not hold tab.mutex.
|
||||
func (tab *Table) add(n *Node) {
|
||||
func (tab *Table) add(n *node) {
|
||||
if n.ID() == tab.self.ID() {
|
||||
return
|
||||
}
|
||||
|
||||
tab.mutex.Lock()
|
||||
defer tab.mutex.Unlock()
|
||||
|
||||
b := tab.bucket(n.sha)
|
||||
b := tab.bucket(n.ID())
|
||||
if !tab.bumpOrAdd(b, n) {
|
||||
// Node is not in table. Add it to the replacement list.
|
||||
tab.addReplacement(b, n)
|
||||
|
@ -570,7 +562,7 @@ func (tab *Table) add(n *Node) {
|
|||
// table could be filled by just sending ping repeatedly.
|
||||
//
|
||||
// The caller must not hold tab.mutex.
|
||||
func (tab *Table) addThroughPing(n *Node) {
|
||||
func (tab *Table) addThroughPing(n *node) {
|
||||
if !tab.isInitDone() {
|
||||
return
|
||||
}
|
||||
|
@ -579,15 +571,15 @@ func (tab *Table) addThroughPing(n *Node) {
|
|||
|
||||
// stuff adds nodes the table to the end of their corresponding bucket
|
||||
// if the bucket is not full. The caller must not hold tab.mutex.
|
||||
func (tab *Table) stuff(nodes []*Node) {
|
||||
func (tab *Table) stuff(nodes []*node) {
|
||||
tab.mutex.Lock()
|
||||
defer tab.mutex.Unlock()
|
||||
|
||||
for _, n := range nodes {
|
||||
if n.ID == tab.self.ID {
|
||||
if n.ID() == tab.self.ID() {
|
||||
continue // don't add self
|
||||
}
|
||||
b := tab.bucket(n.sha)
|
||||
b := tab.bucket(n.ID())
|
||||
if len(b.entries) < bucketSize {
|
||||
tab.bumpOrAdd(b, n)
|
||||
}
|
||||
|
@ -595,11 +587,11 @@ func (tab *Table) stuff(nodes []*Node) {
|
|||
}
|
||||
|
||||
// delete removes an entry from the node table. It is used to evacuate dead nodes.
|
||||
func (tab *Table) delete(node *Node) {
|
||||
func (tab *Table) delete(node *node) {
|
||||
tab.mutex.Lock()
|
||||
defer tab.mutex.Unlock()
|
||||
|
||||
tab.deleteInBucket(tab.bucket(node.sha), node)
|
||||
tab.deleteInBucket(tab.bucket(node.ID()), node)
|
||||
}
|
||||
|
||||
func (tab *Table) addIP(b *bucket, ip net.IP) bool {
|
||||
|
@ -626,27 +618,27 @@ func (tab *Table) removeIP(b *bucket, ip net.IP) {
|
|||
b.ips.Remove(ip)
|
||||
}
|
||||
|
||||
func (tab *Table) addReplacement(b *bucket, n *Node) {
|
||||
func (tab *Table) addReplacement(b *bucket, n *node) {
|
||||
for _, e := range b.replacements {
|
||||
if e.ID == n.ID {
|
||||
if e.ID() == n.ID() {
|
||||
return // already in list
|
||||
}
|
||||
}
|
||||
if !tab.addIP(b, n.IP) {
|
||||
if !tab.addIP(b, n.IP()) {
|
||||
return
|
||||
}
|
||||
var removed *Node
|
||||
var removed *node
|
||||
b.replacements, removed = pushNode(b.replacements, n, maxReplacements)
|
||||
if removed != nil {
|
||||
tab.removeIP(b, removed.IP)
|
||||
tab.removeIP(b, removed.IP())
|
||||
}
|
||||
}
|
||||
|
||||
// replace removes n from the replacement list and replaces 'last' with it if it is the
|
||||
// last entry in the bucket. If 'last' isn't the last entry, it has either been replaced
|
||||
// with someone else or became active.
|
||||
func (tab *Table) replace(b *bucket, last *Node) *Node {
|
||||
if len(b.entries) == 0 || b.entries[len(b.entries)-1].ID != last.ID {
|
||||
func (tab *Table) replace(b *bucket, last *node) *node {
|
||||
if len(b.entries) == 0 || b.entries[len(b.entries)-1].ID() != last.ID() {
|
||||
// Entry has moved, don't replace it.
|
||||
return nil
|
||||
}
|
||||
|
@ -658,15 +650,15 @@ func (tab *Table) replace(b *bucket, last *Node) *Node {
|
|||
r := b.replacements[tab.rand.Intn(len(b.replacements))]
|
||||
b.replacements = deleteNode(b.replacements, r)
|
||||
b.entries[len(b.entries)-1] = r
|
||||
tab.removeIP(b, last.IP)
|
||||
tab.removeIP(b, last.IP())
|
||||
return r
|
||||
}
|
||||
|
||||
// bump moves the given node to the front of the bucket entry list
|
||||
// if it is contained in that list.
|
||||
func (b *bucket) bump(n *Node) bool {
|
||||
func (b *bucket) bump(n *node) bool {
|
||||
for i := range b.entries {
|
||||
if b.entries[i].ID == n.ID {
|
||||
if b.entries[i].ID() == n.ID() {
|
||||
// move it to the front
|
||||
copy(b.entries[1:], b.entries[:i])
|
||||
b.entries[0] = n
|
||||
|
@ -678,11 +670,11 @@ func (b *bucket) bump(n *Node) bool {
|
|||
|
||||
// bumpOrAdd moves n to the front of the bucket entry list or adds it if the list isn't
|
||||
// full. The return value is true if n is in the bucket.
|
||||
func (tab *Table) bumpOrAdd(b *bucket, n *Node) bool {
|
||||
func (tab *Table) bumpOrAdd(b *bucket, n *node) bool {
|
||||
if b.bump(n) {
|
||||
return true
|
||||
}
|
||||
if len(b.entries) >= bucketSize || !tab.addIP(b, n.IP) {
|
||||
if len(b.entries) >= bucketSize || !tab.addIP(b, n.IP()) {
|
||||
return false
|
||||
}
|
||||
b.entries, _ = pushNode(b.entries, n, bucketSize)
|
||||
|
@ -694,13 +686,13 @@ func (tab *Table) bumpOrAdd(b *bucket, n *Node) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (tab *Table) deleteInBucket(b *bucket, n *Node) {
|
||||
func (tab *Table) deleteInBucket(b *bucket, n *node) {
|
||||
b.entries = deleteNode(b.entries, n)
|
||||
tab.removeIP(b, n.IP)
|
||||
tab.removeIP(b, n.IP())
|
||||
}
|
||||
|
||||
// pushNode adds n to the front of list, keeping at most max items.
|
||||
func pushNode(list []*Node, n *Node, max int) ([]*Node, *Node) {
|
||||
func pushNode(list []*node, n *node, max int) ([]*node, *node) {
|
||||
if len(list) < max {
|
||||
list = append(list, nil)
|
||||
}
|
||||
|
@ -711,9 +703,9 @@ func pushNode(list []*Node, n *Node, max int) ([]*Node, *Node) {
|
|||
}
|
||||
|
||||
// deleteNode removes n from list.
|
||||
func deleteNode(list []*Node, n *Node) []*Node {
|
||||
func deleteNode(list []*node, n *node) []*node {
|
||||
for i := range list {
|
||||
if list[i].ID == n.ID {
|
||||
if list[i].ID() == n.ID() {
|
||||
return append(list[:i], list[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
@ -723,14 +715,14 @@ func deleteNode(list []*Node, n *Node) []*Node {
|
|||
// nodesByDistance is a list of nodes, ordered by
|
||||
// distance to target.
|
||||
type nodesByDistance struct {
|
||||
entries []*Node
|
||||
target common.Hash
|
||||
entries []*node
|
||||
target enode.ID
|
||||
}
|
||||
|
||||
// push adds the given node to the list, keeping the total size below maxElems.
|
||||
func (h *nodesByDistance) push(n *Node, maxElems int) {
|
||||
func (h *nodesByDistance) push(n *node, maxElems int) {
|
||||
ix := sort.Search(len(h.entries), func(i int) bool {
|
||||
return distcmp(h.target, h.entries[i].sha, n.sha) > 0
|
||||
return enode.DistCmp(h.target, h.entries[i].ID(), n.ID()) > 0
|
||||
})
|
||||
if len(h.entries) < maxElems {
|
||||
h.entries = append(h.entries, n)
|
||||
|
|
|
@ -20,7 +20,6 @@ import (
|
|||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
|
||||
"net"
|
||||
"reflect"
|
||||
|
@ -28,8 +27,9 @@ import (
|
|||
"testing/quick"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
||||
)
|
||||
|
||||
func TestTable_pingReplace(t *testing.T) {
|
||||
|
@ -49,24 +49,26 @@ func TestTable_pingReplace(t *testing.T) {
|
|||
|
||||
func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding bool) {
|
||||
transport := newPingRecorder()
|
||||
tab, _ := newTable(transport, NodeID{}, &net.UDPAddr{}, "", nil)
|
||||
tab, db := newTestTable(transport)
|
||||
defer tab.Close()
|
||||
defer db.Close()
|
||||
|
||||
<-tab.initDone
|
||||
|
||||
// Fill up the sender's bucket.
|
||||
pingSender := NewNode(MustHexID("a502af0f59b2aab7746995408c79e9ca312d2793cc997e44fc55eda62f0150bbb8c59a6f9269ba3a081518b62699ee807c7c19c20125ddfccca872608af9e370"), net.IP{}, 99, 99)
|
||||
pingKey, _ := crypto.HexToECDSA("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8")
|
||||
pingSender := wrapNode(enode.NewV4(&pingKey.PublicKey, net.IP{}, 99, 99))
|
||||
last := fillBucket(tab, pingSender)
|
||||
|
||||
// Add the sender as if it just pinged us. Revalidate should replace the last node in
|
||||
// its bucket if it is unresponsive. Revalidate again to ensure that
|
||||
transport.dead[last.ID] = !lastInBucketIsResponding
|
||||
transport.dead[pingSender.ID] = !newNodeIsResponding
|
||||
transport.dead[last.ID()] = !lastInBucketIsResponding
|
||||
transport.dead[pingSender.ID()] = !newNodeIsResponding
|
||||
tab.add(pingSender)
|
||||
tab.doRevalidate(make(chan struct{}, 1))
|
||||
tab.doRevalidate(make(chan struct{}, 1))
|
||||
|
||||
if !transport.pinged[last.ID] {
|
||||
if !transport.pinged[last.ID()] {
|
||||
// Oldest node in bucket is pinged to see whether it is still alive.
|
||||
t.Error("table did not ping last node in bucket")
|
||||
}
|
||||
|
@ -77,14 +79,14 @@ func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding
|
|||
if !lastInBucketIsResponding && !newNodeIsResponding {
|
||||
wantSize--
|
||||
}
|
||||
if l := len(tab.bucket(pingSender.sha).entries); l != wantSize {
|
||||
t.Errorf("wrong bucket size after add: got %d, want %d", l, wantSize)
|
||||
if l := len(tab.bucket(pingSender.ID()).entries); l != wantSize {
|
||||
t.Errorf("wrong bucket size after bond: got %d, want %d", l, wantSize)
|
||||
}
|
||||
if found := contains(tab.bucket(pingSender.sha).entries, last.ID); found != lastInBucketIsResponding {
|
||||
if found := contains(tab.bucket(pingSender.ID()).entries, last.ID()); found != lastInBucketIsResponding {
|
||||
t.Errorf("last entry found: %t, want: %t", found, lastInBucketIsResponding)
|
||||
}
|
||||
wantNewEntry := newNodeIsResponding && !lastInBucketIsResponding
|
||||
if found := contains(tab.bucket(pingSender.sha).entries, pingSender.ID); found != wantNewEntry {
|
||||
if found := contains(tab.bucket(pingSender.ID()).entries, pingSender.ID()); found != wantNewEntry {
|
||||
t.Errorf("new entry found: %t, want: %t", found, wantNewEntry)
|
||||
}
|
||||
}
|
||||
|
@ -97,9 +99,9 @@ func TestBucket_bumpNoDuplicates(t *testing.T) {
|
|||
Values: func(args []reflect.Value, rand *rand.Rand) {
|
||||
// generate a random list of nodes. this will be the content of the bucket.
|
||||
n := rand.Intn(bucketSize-1) + 1
|
||||
nodes := make([]*Node, n)
|
||||
nodes := make([]*node, n)
|
||||
for i := range nodes {
|
||||
nodes[i] = nodeAtDistance(common.Hash{}, 200)
|
||||
nodes[i] = nodeAtDistance(enode.ID{}, 200, intIP(200))
|
||||
}
|
||||
args[0] = reflect.ValueOf(nodes)
|
||||
// generate random bump positions.
|
||||
|
@ -111,8 +113,8 @@ func TestBucket_bumpNoDuplicates(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
prop := func(nodes []*Node, bumps []int) (ok bool) {
|
||||
b := &bucket{entries: make([]*Node, len(nodes))}
|
||||
prop := func(nodes []*node, bumps []int) (ok bool) {
|
||||
b := &bucket{entries: make([]*node, len(nodes))}
|
||||
copy(b.entries, nodes)
|
||||
for i, pos := range bumps {
|
||||
b.bump(b.entries[pos])
|
||||
|
@ -134,12 +136,12 @@ func TestBucket_bumpNoDuplicates(t *testing.T) {
|
|||
// This checks that the table-wide IP limit is applied correctly.
|
||||
func TestTable_IPLimit(t *testing.T) {
|
||||
transport := newPingRecorder()
|
||||
tab, _ := newTable(transport, NodeID{}, &net.UDPAddr{}, "", nil)
|
||||
tab, db := newTestTable(transport)
|
||||
defer tab.Close()
|
||||
defer db.Close()
|
||||
|
||||
for i := 0; i < tableIPLimit+1; i++ {
|
||||
n := nodeAtDistance(tab.self.sha, i)
|
||||
n.IP = net.IP{172, 0, 1, byte(i)}
|
||||
n := nodeAtDistance(tab.self.ID(), i, net.IP{172, 0, 1, byte(i)})
|
||||
tab.add(n)
|
||||
}
|
||||
if tab.len() > tableIPLimit {
|
||||
|
@ -147,16 +149,16 @@ func TestTable_IPLimit(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// This checks that the table-wide IP limit is applied correctly.
|
||||
// This checks that the per-bucket IP limit is applied correctly.
|
||||
func TestTable_BucketIPLimit(t *testing.T) {
|
||||
transport := newPingRecorder()
|
||||
tab, _ := newTable(transport, NodeID{}, &net.UDPAddr{}, "", nil)
|
||||
tab, db := newTestTable(transport)
|
||||
defer tab.Close()
|
||||
defer db.Close()
|
||||
|
||||
d := 3
|
||||
for i := 0; i < bucketIPLimit+1; i++ {
|
||||
n := nodeAtDistance(tab.self.sha, d)
|
||||
n.IP = net.IP{172, 0, 1, byte(i)}
|
||||
n := nodeAtDistance(tab.self.ID(), d, net.IP{172, 0, 1, byte(i)})
|
||||
tab.add(n)
|
||||
}
|
||||
if tab.len() > bucketIPLimit {
|
||||
|
@ -164,69 +166,18 @@ func TestTable_BucketIPLimit(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// fillBucket inserts nodes into the given bucket until
|
||||
// it is full. The node's IDs dont correspond to their
|
||||
// hashes.
|
||||
func fillBucket(tab *Table, n *Node) (last *Node) {
|
||||
ld := logdist(tab.self.sha, n.sha)
|
||||
b := tab.bucket(n.sha)
|
||||
for len(b.entries) < bucketSize {
|
||||
b.entries = append(b.entries, nodeAtDistance(tab.self.sha, ld))
|
||||
}
|
||||
return b.entries[bucketSize-1]
|
||||
}
|
||||
|
||||
// nodeAtDistance creates a node for which logdist(base, n.sha) == ld.
|
||||
// The node's ID does not correspond to n.sha.
|
||||
func nodeAtDistance(base common.Hash, ld int) (n *Node) {
|
||||
n = new(Node)
|
||||
n.sha = hashAtDistance(base, ld)
|
||||
n.IP = net.IP{byte(ld), 0, 2, byte(ld)}
|
||||
copy(n.ID[:], n.sha[:]) // ensure the node still has a unique ID
|
||||
return n
|
||||
}
|
||||
|
||||
type pingRecorder struct {
|
||||
mu sync.Mutex
|
||||
dead, pinged map[NodeID]bool
|
||||
}
|
||||
|
||||
func newPingRecorder() *pingRecorder {
|
||||
return &pingRecorder{
|
||||
dead: make(map[NodeID]bool),
|
||||
pinged: make(map[NodeID]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *pingRecorder) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *pingRecorder) ping(toid NodeID, toaddr *net.UDPAddr) error {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
t.pinged[toid] = true
|
||||
if t.dead[toid] {
|
||||
return errTimeout
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *pingRecorder) close() {}
|
||||
|
||||
func TestTable_closest(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
test := func(test *closeTest) bool {
|
||||
// for any node table, Target and N
|
||||
transport := newPingRecorder()
|
||||
tab, _ := newTable(transport, test.Self, &net.UDPAddr{}, "", nil)
|
||||
tab, db := newTestTable(transport)
|
||||
defer tab.Close()
|
||||
defer db.Close()
|
||||
tab.stuff(test.All)
|
||||
|
||||
// check that doClosest(Target, N) returns nodes
|
||||
// check that closest(Target, N) returns nodes
|
||||
result := tab.closest(test.Target, test.N).entries
|
||||
if hasDuplicates(result) {
|
||||
t.Errorf("result contains duplicates")
|
||||
|
@ -252,15 +203,15 @@ func TestTable_closest(t *testing.T) {
|
|||
// check that the result nodes have minimum distance to target.
|
||||
for _, b := range tab.buckets {
|
||||
for _, n := range b.entries {
|
||||
if contains(result, n.ID) {
|
||||
if contains(result, n.ID()) {
|
||||
continue // don't run the check below for nodes in result
|
||||
}
|
||||
farthestResult := result[len(result)-1].sha
|
||||
if distcmp(test.Target, n.sha, farthestResult) < 0 {
|
||||
farthestResult := result[len(result)-1].ID()
|
||||
if enode.DistCmp(test.Target, n.ID(), farthestResult) < 0 {
|
||||
t.Errorf("table contains node that is closer to target but it's not in result")
|
||||
t.Logf(" Target: %v", test.Target)
|
||||
t.Logf(" Farthest Result: %v", farthestResult)
|
||||
t.Logf(" ID: %v", n.ID)
|
||||
t.Logf(" ID: %v", n.ID())
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -277,25 +228,26 @@ func TestTable_ReadRandomNodesGetAll(t *testing.T) {
|
|||
MaxCount: 200,
|
||||
Rand: rand.New(rand.NewSource(time.Now().Unix())),
|
||||
Values: func(args []reflect.Value, rand *rand.Rand) {
|
||||
args[0] = reflect.ValueOf(make([]*Node, rand.Intn(1000)))
|
||||
args[0] = reflect.ValueOf(make([]*enode.Node, rand.Intn(1000)))
|
||||
},
|
||||
}
|
||||
test := func(buf []*Node) bool {
|
||||
test := func(buf []*enode.Node) bool {
|
||||
transport := newPingRecorder()
|
||||
tab, _ := newTable(transport, NodeID{}, &net.UDPAddr{}, "", nil)
|
||||
tab, db := newTestTable(transport)
|
||||
defer tab.Close()
|
||||
defer db.Close()
|
||||
<-tab.initDone
|
||||
|
||||
for i := 0; i < len(buf); i++ {
|
||||
ld := cfg.Rand.Intn(len(tab.buckets))
|
||||
tab.stuff([]*Node{nodeAtDistance(tab.self.sha, ld)})
|
||||
tab.stuff([]*node{nodeAtDistance(tab.self.ID(), ld, intIP(ld))})
|
||||
}
|
||||
gotN := tab.ReadRandomNodes(buf)
|
||||
if gotN != tab.len() {
|
||||
t.Errorf("wrong number of nodes, got %d, want %d", gotN, tab.len())
|
||||
return false
|
||||
}
|
||||
if hasDuplicates(buf[:gotN]) {
|
||||
if hasDuplicates(wrapNodes(buf[:gotN])) {
|
||||
t.Errorf("result contains duplicates")
|
||||
return false
|
||||
}
|
||||
|
@ -307,41 +259,43 @@ func TestTable_ReadRandomNodesGetAll(t *testing.T) {
|
|||
}
|
||||
|
||||
type closeTest struct {
|
||||
Self NodeID
|
||||
Target common.Hash
|
||||
All []*Node
|
||||
Self enode.ID
|
||||
Target enode.ID
|
||||
All []*node
|
||||
N int
|
||||
}
|
||||
|
||||
func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value {
|
||||
t := &closeTest{
|
||||
Self: gen(NodeID{}, rand).(NodeID),
|
||||
Target: gen(common.Hash{}, rand).(common.Hash),
|
||||
Self: gen(enode.ID{}, rand).(enode.ID),
|
||||
Target: gen(enode.ID{}, rand).(enode.ID),
|
||||
N: rand.Intn(bucketSize),
|
||||
}
|
||||
for _, id := range gen([]NodeID{}, rand).([]NodeID) {
|
||||
t.All = append(t.All, &Node{ID: id})
|
||||
for _, id := range gen([]enode.ID{}, rand).([]enode.ID) {
|
||||
n := enode.SignNull(new(enr.Record), id)
|
||||
t.All = append(t.All, wrapNode(n))
|
||||
}
|
||||
return reflect.ValueOf(t)
|
||||
}
|
||||
|
||||
func TestTable_Lookup(t *testing.T) {
|
||||
self := nodeAtDistance(common.Hash{}, 0)
|
||||
tab, _ := newTable(lookupTestnet, self.ID, &net.UDPAddr{}, "", nil)
|
||||
tab, db := newTestTable(lookupTestnet)
|
||||
defer tab.Close()
|
||||
defer db.Close()
|
||||
|
||||
// lookup on empty table returns no nodes
|
||||
if results := tab.Lookup(lookupTestnet.target); len(results) > 0 {
|
||||
if results := tab.lookup(lookupTestnet.target, false); len(results) > 0 {
|
||||
t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results)
|
||||
}
|
||||
// seed table with initial node (otherwise lookup will terminate immediately)
|
||||
seed := NewNode(lookupTestnet.dists[256][0], net.IP{}, 256, 0)
|
||||
tab.stuff([]*Node{seed})
|
||||
seedKey, _ := decodePubkey(lookupTestnet.dists[256][0])
|
||||
seed := wrapNode(enode.NewV4(seedKey, net.IP{}, 0, 256))
|
||||
tab.stuff([]*node{seed})
|
||||
|
||||
results := tab.Lookup(lookupTestnet.target)
|
||||
results := tab.lookup(lookupTestnet.target, true)
|
||||
t.Logf("results:")
|
||||
for _, e := range results {
|
||||
t.Logf(" ld=%d, %x", logdist(lookupTestnet.targetSha, e.sha), e.sha[:])
|
||||
t.Logf(" ld=%d, %x", enode.LogDist(lookupTestnet.targetSha, e.ID()), e.ID().Bytes())
|
||||
}
|
||||
if len(results) != bucketSize {
|
||||
t.Errorf("wrong number of results: got %d, want %d", len(results), bucketSize)
|
||||
|
@ -358,235 +312,236 @@ func TestTable_Lookup(t *testing.T) {
|
|||
// This is the test network for the Lookup test.
|
||||
// The nodes were obtained by running testnet.mine with a random NodeID as target.
|
||||
var lookupTestnet = &preminedTestnet{
|
||||
target: MustHexID("166aea4f556532c6d34e8b740e5d314af7e9ac0ca79833bd751d6b665f12dfd38ec563c363b32f02aef4a80b44fd3def94612d497b99cb5f17fd24de454927ec"),
|
||||
targetSha: common.Hash{0x5c, 0x94, 0x4e, 0xe5, 0x1c, 0x5a, 0xe9, 0xf7, 0x2a, 0x95, 0xec, 0xcb, 0x8a, 0xed, 0x3, 0x74, 0xee, 0xcb, 0x51, 0x19, 0xd7, 0x20, 0xcb, 0xea, 0x68, 0x13, 0xe8, 0xe0, 0xd6, 0xad, 0x92, 0x61},
|
||||
dists: [257][]NodeID{
|
||||
target: hexEncPubkey("166aea4f556532c6d34e8b740e5d314af7e9ac0ca79833bd751d6b665f12dfd38ec563c363b32f02aef4a80b44fd3def94612d497b99cb5f17fd24de454927ec"),
|
||||
targetSha: enode.HexID("5c944ee51c5ae9f72a95eccb8aed0374eecb5119d720cbea6813e8e0d6ad9261"),
|
||||
dists: [257][]encPubkey{
|
||||
240: {
|
||||
MustHexID("2001ad5e3e80c71b952161bc0186731cf5ffe942d24a79230a0555802296238e57ea7a32f5b6f18564eadc1c65389448481f8c9338df0a3dbd18f708cbc2cbcb"),
|
||||
MustHexID("6ba3f4f57d084b6bf94cc4555b8c657e4a8ac7b7baf23c6874efc21dd1e4f56b7eb2721e07f5242d2f1d8381fc8cae535e860197c69236798ba1ad231b105794"),
|
||||
hexEncPubkey("2001ad5e3e80c71b952161bc0186731cf5ffe942d24a79230a0555802296238e57ea7a32f5b6f18564eadc1c65389448481f8c9338df0a3dbd18f708cbc2cbcb"),
|
||||
hexEncPubkey("6ba3f4f57d084b6bf94cc4555b8c657e4a8ac7b7baf23c6874efc21dd1e4f56b7eb2721e07f5242d2f1d8381fc8cae535e860197c69236798ba1ad231b105794"),
|
||||
},
|
||||
244: {
|
||||
MustHexID("696ba1f0a9d55c59246f776600542a9e6432490f0cd78f8bb55a196918df2081a9b521c3c3ba48e465a75c10768807717f8f689b0b4adce00e1c75737552a178"),
|
||||
hexEncPubkey("696ba1f0a9d55c59246f776600542a9e6432490f0cd78f8bb55a196918df2081a9b521c3c3ba48e465a75c10768807717f8f689b0b4adce00e1c75737552a178"),
|
||||
},
|
||||
246: {
|
||||
MustHexID("d6d32178bdc38416f46ffb8b3ec9e4cb2cfff8d04dd7e4311a70e403cb62b10be1b447311b60b4f9ee221a8131fc2cbd45b96dd80deba68a949d467241facfa8"),
|
||||
MustHexID("3ea3d04a43a3dfb5ac11cffc2319248cf41b6279659393c2f55b8a0a5fc9d12581a9d97ef5d8ff9b5abf3321a290e8f63a4f785f450dc8a672aba3ba2ff4fdab"),
|
||||
MustHexID("2fc897f05ae585553e5c014effd3078f84f37f9333afacffb109f00ca8e7a3373de810a3946be971cbccdfd40249f9fe7f322118ea459ac71acca85a1ef8b7f4"),
|
||||
hexEncPubkey("d6d32178bdc38416f46ffb8b3ec9e4cb2cfff8d04dd7e4311a70e403cb62b10be1b447311b60b4f9ee221a8131fc2cbd45b96dd80deba68a949d467241facfa8"),
|
||||
hexEncPubkey("3ea3d04a43a3dfb5ac11cffc2319248cf41b6279659393c2f55b8a0a5fc9d12581a9d97ef5d8ff9b5abf3321a290e8f63a4f785f450dc8a672aba3ba2ff4fdab"),
|
||||
hexEncPubkey("2fc897f05ae585553e5c014effd3078f84f37f9333afacffb109f00ca8e7a3373de810a3946be971cbccdfd40249f9fe7f322118ea459ac71acca85a1ef8b7f4"),
|
||||
},
|
||||
247: {
|
||||
MustHexID("3155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32"),
|
||||
MustHexID("312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069db"),
|
||||
MustHexID("38643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aac"),
|
||||
MustHexID("8dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73"),
|
||||
MustHexID("8b58c6073dd98bbad4e310b97186c8f822d3a5c7d57af40e2136e88e315afd115edb27d2d0685a908cfe5aa49d0debdda6e6e63972691d6bd8c5af2d771dd2a9"),
|
||||
MustHexID("2cbb718b7dc682da19652e7d9eb4fefaf7b7147d82c1c2b6805edf77b85e29fde9f6da195741467ff2638dc62c8d3e014ea5686693c15ed0080b6de90354c137"),
|
||||
MustHexID("e84027696d3f12f2de30a9311afea8fbd313c2360daff52bb5fc8c7094d5295758bec3134e4eef24e4cdf377b40da344993284628a7a346eba94f74160998feb"),
|
||||
MustHexID("f1357a4f04f9d33753a57c0b65ba20a5d8777abbffd04e906014491c9103fb08590e45548d37aa4bd70965e2e81ddba94f31860348df01469eec8c1829200a68"),
|
||||
MustHexID("4ab0a75941b12892369b4490a1928c8ca52a9ad6d3dffbd1d8c0b907bc200fe74c022d011ec39b64808a39c0ca41f1d3254386c3e7733e7044c44259486461b6"),
|
||||
MustHexID("d45150a72dc74388773e68e03133a3b5f51447fe91837d566706b3c035ee4b56f160c878c6273394daee7f56cc398985269052f22f75a8057df2fe6172765354"),
|
||||
hexEncPubkey("3155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32"),
|
||||
hexEncPubkey("312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069db"),
|
||||
hexEncPubkey("38643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aac"),
|
||||
hexEncPubkey("8dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73"),
|
||||
hexEncPubkey("8b58c6073dd98bbad4e310b97186c8f822d3a5c7d57af40e2136e88e315afd115edb27d2d0685a908cfe5aa49d0debdda6e6e63972691d6bd8c5af2d771dd2a9"),
|
||||
hexEncPubkey("2cbb718b7dc682da19652e7d9eb4fefaf7b7147d82c1c2b6805edf77b85e29fde9f6da195741467ff2638dc62c8d3e014ea5686693c15ed0080b6de90354c137"),
|
||||
hexEncPubkey("e84027696d3f12f2de30a9311afea8fbd313c2360daff52bb5fc8c7094d5295758bec3134e4eef24e4cdf377b40da344993284628a7a346eba94f74160998feb"),
|
||||
hexEncPubkey("f1357a4f04f9d33753a57c0b65ba20a5d8777abbffd04e906014491c9103fb08590e45548d37aa4bd70965e2e81ddba94f31860348df01469eec8c1829200a68"),
|
||||
hexEncPubkey("4ab0a75941b12892369b4490a1928c8ca52a9ad6d3dffbd1d8c0b907bc200fe74c022d011ec39b64808a39c0ca41f1d3254386c3e7733e7044c44259486461b6"),
|
||||
hexEncPubkey("d45150a72dc74388773e68e03133a3b5f51447fe91837d566706b3c035ee4b56f160c878c6273394daee7f56cc398985269052f22f75a8057df2fe6172765354"),
|
||||
},
|
||||
248: {
|
||||
MustHexID("6aadfce366a189bab08ac84721567483202c86590642ea6d6a14f37ca78d82bdb6509eb7b8b2f6f63c78ae3ae1d8837c89509e41497d719b23ad53dd81574afa"),
|
||||
MustHexID("a605ecfd6069a4cf4cf7f5840e5bc0ce10d23a3ac59e2aaa70c6afd5637359d2519b4524f56fc2ca180cdbebe54262f720ccaae8c1b28fd553c485675831624d"),
|
||||
MustHexID("29701451cb9448ca33fc33680b44b840d815be90146eb521641efbffed0859c154e8892d3906eae9934bfacee72cd1d2fa9dd050fd18888eea49da155ab0efd2"),
|
||||
MustHexID("3ed426322dee7572b08592e1e079f8b6c6b30e10e6243edd144a6a48fdbdb83df73a6e41b1143722cb82604f2203a32758610b5d9544f44a1a7921ba001528c1"),
|
||||
MustHexID("b2e2a2b7fdd363572a3256e75435fab1da3b16f7891a8bd2015f30995dae665d7eabfd194d87d99d5df628b4bbc7b04e5b492c596422dd8272746c7a1b0b8e4f"),
|
||||
MustHexID("0c69c9756162c593e85615b814ce57a2a8ca2df6c690b9c4e4602731b61e1531a3bbe3f7114271554427ffabea80ad8f36fa95a49fa77b675ae182c6ccac1728"),
|
||||
MustHexID("8d28be21d5a97b0876442fa4f5e5387f5bf3faad0b6f13b8607b64d6e448c0991ca28dd7fe2f64eb8eadd7150bff5d5666aa6ed868b84c71311f4ba9a38569dd"),
|
||||
MustHexID("2c677e1c64b9c9df6359348a7f5f33dc79e22f0177042486d125f8b6ca7f0dc756b1f672aceee5f1746bcff80aaf6f92a8dc0c9fbeb259b3fa0da060de5ab7e8"),
|
||||
MustHexID("3994880f94a8678f0cd247a43f474a8af375d2a072128da1ad6cae84a244105ff85e94fc7d8496f639468de7ee998908a91c7e33ef7585fff92e984b210941a1"),
|
||||
MustHexID("b45a9153c08d002a48090d15d61a7c7dad8c2af85d4ff5bd36ce23a9a11e0709bf8d56614c7b193bc028c16cbf7f20dfbcc751328b64a924995d47b41e452422"),
|
||||
MustHexID("057ab3a9e53c7a84b0f3fc586117a525cdd18e313f52a67bf31798d48078e325abe5cfee3f6c2533230cb37d0549289d692a29dd400e899b8552d4b928f6f907"),
|
||||
MustHexID("0ddf663d308791eb92e6bd88a2f8cb45e4f4f35bb16708a0e6ff7f1362aa6a73fedd0a1b1557fb3365e38e1b79d6918e2fae2788728b70c9ab6b51a3b94a4338"),
|
||||
MustHexID("f637e07ff50cc1e3731735841c4798411059f2023abcf3885674f3e8032531b0edca50fd715df6feb489b6177c345374d64f4b07d257a7745de393a107b013a5"),
|
||||
MustHexID("e24ec7c6eec094f63c7b3239f56d311ec5a3e45bc4e622a1095a65b95eea6fe13e29f3b6b7a2cbfe40906e3989f17ac834c3102dd0cadaaa26e16ee06d782b72"),
|
||||
MustHexID("b76ea1a6fd6506ef6e3506a4f1f60ed6287fff8114af6141b2ff13e61242331b54082b023cfea5b3083354a4fb3f9eb8be01fb4a518f579e731a5d0707291a6b"),
|
||||
MustHexID("9b53a37950ca8890ee349b325032d7b672cab7eced178d3060137b24ef6b92a43977922d5bdfb4a3409a2d80128e02f795f9dae6d7d99973ad0e23a2afb8442f"),
|
||||
hexEncPubkey("6aadfce366a189bab08ac84721567483202c86590642ea6d6a14f37ca78d82bdb6509eb7b8b2f6f63c78ae3ae1d8837c89509e41497d719b23ad53dd81574afa"),
|
||||
hexEncPubkey("a605ecfd6069a4cf4cf7f5840e5bc0ce10d23a3ac59e2aaa70c6afd5637359d2519b4524f56fc2ca180cdbebe54262f720ccaae8c1b28fd553c485675831624d"),
|
||||
hexEncPubkey("29701451cb9448ca33fc33680b44b840d815be90146eb521641efbffed0859c154e8892d3906eae9934bfacee72cd1d2fa9dd050fd18888eea49da155ab0efd2"),
|
||||
hexEncPubkey("3ed426322dee7572b08592e1e079f8b6c6b30e10e6243edd144a6a48fdbdb83df73a6e41b1143722cb82604f2203a32758610b5d9544f44a1a7921ba001528c1"),
|
||||
hexEncPubkey("b2e2a2b7fdd363572a3256e75435fab1da3b16f7891a8bd2015f30995dae665d7eabfd194d87d99d5df628b4bbc7b04e5b492c596422dd8272746c7a1b0b8e4f"),
|
||||
hexEncPubkey("0c69c9756162c593e85615b814ce57a2a8ca2df6c690b9c4e4602731b61e1531a3bbe3f7114271554427ffabea80ad8f36fa95a49fa77b675ae182c6ccac1728"),
|
||||
hexEncPubkey("8d28be21d5a97b0876442fa4f5e5387f5bf3faad0b6f13b8607b64d6e448c0991ca28dd7fe2f64eb8eadd7150bff5d5666aa6ed868b84c71311f4ba9a38569dd"),
|
||||
hexEncPubkey("2c677e1c64b9c9df6359348a7f5f33dc79e22f0177042486d125f8b6ca7f0dc756b1f672aceee5f1746bcff80aaf6f92a8dc0c9fbeb259b3fa0da060de5ab7e8"),
|
||||
hexEncPubkey("3994880f94a8678f0cd247a43f474a8af375d2a072128da1ad6cae84a244105ff85e94fc7d8496f639468de7ee998908a91c7e33ef7585fff92e984b210941a1"),
|
||||
hexEncPubkey("b45a9153c08d002a48090d15d61a7c7dad8c2af85d4ff5bd36ce23a9a11e0709bf8d56614c7b193bc028c16cbf7f20dfbcc751328b64a924995d47b41e452422"),
|
||||
hexEncPubkey("057ab3a9e53c7a84b0f3fc586117a525cdd18e313f52a67bf31798d48078e325abe5cfee3f6c2533230cb37d0549289d692a29dd400e899b8552d4b928f6f907"),
|
||||
hexEncPubkey("0ddf663d308791eb92e6bd88a2f8cb45e4f4f35bb16708a0e6ff7f1362aa6a73fedd0a1b1557fb3365e38e1b79d6918e2fae2788728b70c9ab6b51a3b94a4338"),
|
||||
hexEncPubkey("f637e07ff50cc1e3731735841c4798411059f2023abcf3885674f3e8032531b0edca50fd715df6feb489b6177c345374d64f4b07d257a7745de393a107b013a5"),
|
||||
hexEncPubkey("e24ec7c6eec094f63c7b3239f56d311ec5a3e45bc4e622a1095a65b95eea6fe13e29f3b6b7a2cbfe40906e3989f17ac834c3102dd0cadaaa26e16ee06d782b72"),
|
||||
hexEncPubkey("b76ea1a6fd6506ef6e3506a4f1f60ed6287fff8114af6141b2ff13e61242331b54082b023cfea5b3083354a4fb3f9eb8be01fb4a518f579e731a5d0707291a6b"),
|
||||
hexEncPubkey("9b53a37950ca8890ee349b325032d7b672cab7eced178d3060137b24ef6b92a43977922d5bdfb4a3409a2d80128e02f795f9dae6d7d99973ad0e23a2afb8442f"),
|
||||
},
|
||||
249: {
|
||||
MustHexID("675ae65567c3c72c50c73bc0fd4f61f202ea5f93346ca57b551de3411ccc614fad61cb9035493af47615311b9d44ee7a161972ee4d77c28fe1ec029d01434e6a"),
|
||||
MustHexID("8eb81408389da88536ae5800392b16ef5109d7ea132c18e9a82928047ecdb502693f6e4a4cdd18b54296caf561db937185731456c456c98bfe7de0baf0eaa495"),
|
||||
MustHexID("2adba8b1612a541771cb93a726a38a4b88e97b18eced2593eb7daf82f05a5321ca94a72cc780c306ff21e551a932fc2c6d791e4681907b5ceab7f084c3fa2944"),
|
||||
MustHexID("b1b4bfbda514d9b8f35b1c28961da5d5216fe50548f4066f69af3b7666a3b2e06eac646735e963e5c8f8138a2fb95af15b13b23ff00c6986eccc0efaa8ee6fb4"),
|
||||
MustHexID("d2139281b289ad0e4d7b4243c4364f5c51aac8b60f4806135de06b12b5b369c9e43a6eb494eab860d115c15c6fbb8c5a1b0e382972e0e460af395b8385363de7"),
|
||||
MustHexID("4a693df4b8fc5bdc7cec342c3ed2e228d7c5b4ab7321ddaa6cccbeb45b05a9f1d95766b4002e6d4791c2deacb8a667aadea6a700da28a3eea810a30395701bbc"),
|
||||
MustHexID("ab41611195ec3c62bb8cd762ee19fb182d194fd141f4a66780efbef4b07ce916246c022b841237a3a6b512a93431157edd221e854ed2a259b72e9c5351f44d0c"),
|
||||
MustHexID("68e8e26099030d10c3c703ae7045c0a48061fb88058d853b3e67880014c449d4311014da99d617d3150a20f1a3da5e34bf0f14f1c51fe4dd9d58afd222823176"),
|
||||
MustHexID("3fbcacf546fb129cd70fc48de3b593ba99d3c473798bc309292aca280320e0eacc04442c914cad5c4cf6950345ba79b0d51302df88285d4e83ee3fe41339eee7"),
|
||||
MustHexID("1d4a623659f7c8f80b6c3939596afdf42e78f892f682c768ad36eb7bfba402dbf97aea3a268f3badd8fe7636be216edf3d67ee1e08789ebbc7be625056bd7109"),
|
||||
MustHexID("a283c474ab09da02bbc96b16317241d0627646fcc427d1fe790b76a7bf1989ced90f92101a973047ae9940c92720dffbac8eff21df8cae468a50f72f9e159417"),
|
||||
MustHexID("dbf7e5ad7f87c3dfecae65d87c3039e14ed0bdc56caf00ce81931073e2e16719d746295512ff7937a15c3b03603e7c41a4f9df94fcd37bb200dd8f332767e9cb"),
|
||||
MustHexID("caaa070a26692f64fc77f30d7b5ae980d419b4393a0f442b1c821ef58c0862898b0d22f74a4f8c5d83069493e3ec0b92f17dc1fe6e4cd437c1ec25039e7ce839"),
|
||||
MustHexID("874cc8d1213beb65c4e0e1de38ef5d8165235893ac74ab5ea937c885eaab25c8d79dad0456e9fd3e9450626cac7e107b004478fb59842f067857f39a47cee695"),
|
||||
MustHexID("d94193f236105010972f5df1b7818b55846592a0445b9cdc4eaed811b8c4c0f7c27dc8cc9837a4774656d6b34682d6d329d42b6ebb55da1d475c2474dc3dfdf4"),
|
||||
MustHexID("edd9af6aded4094e9785637c28fccbd3980cbe28e2eb9a411048a23c2ace4bd6b0b7088a7817997b49a3dd05fc6929ca6c7abbb69438dbdabe65e971d2a794b2"),
|
||||
hexEncPubkey("675ae65567c3c72c50c73bc0fd4f61f202ea5f93346ca57b551de3411ccc614fad61cb9035493af47615311b9d44ee7a161972ee4d77c28fe1ec029d01434e6a"),
|
||||
hexEncPubkey("8eb81408389da88536ae5800392b16ef5109d7ea132c18e9a82928047ecdb502693f6e4a4cdd18b54296caf561db937185731456c456c98bfe7de0baf0eaa495"),
|
||||
hexEncPubkey("2adba8b1612a541771cb93a726a38a4b88e97b18eced2593eb7daf82f05a5321ca94a72cc780c306ff21e551a932fc2c6d791e4681907b5ceab7f084c3fa2944"),
|
||||
hexEncPubkey("b1b4bfbda514d9b8f35b1c28961da5d5216fe50548f4066f69af3b7666a3b2e06eac646735e963e5c8f8138a2fb95af15b13b23ff00c6986eccc0efaa8ee6fb4"),
|
||||
hexEncPubkey("d2139281b289ad0e4d7b4243c4364f5c51aac8b60f4806135de06b12b5b369c9e43a6eb494eab860d115c15c6fbb8c5a1b0e382972e0e460af395b8385363de7"),
|
||||
hexEncPubkey("4a693df4b8fc5bdc7cec342c3ed2e228d7c5b4ab7321ddaa6cccbeb45b05a9f1d95766b4002e6d4791c2deacb8a667aadea6a700da28a3eea810a30395701bbc"),
|
||||
hexEncPubkey("ab41611195ec3c62bb8cd762ee19fb182d194fd141f4a66780efbef4b07ce916246c022b841237a3a6b512a93431157edd221e854ed2a259b72e9c5351f44d0c"),
|
||||
hexEncPubkey("68e8e26099030d10c3c703ae7045c0a48061fb88058d853b3e67880014c449d4311014da99d617d3150a20f1a3da5e34bf0f14f1c51fe4dd9d58afd222823176"),
|
||||
hexEncPubkey("3fbcacf546fb129cd70fc48de3b593ba99d3c473798bc309292aca280320e0eacc04442c914cad5c4cf6950345ba79b0d51302df88285d4e83ee3fe41339eee7"),
|
||||
hexEncPubkey("1d4a623659f7c8f80b6c3939596afdf42e78f892f682c768ad36eb7bfba402dbf97aea3a268f3badd8fe7636be216edf3d67ee1e08789ebbc7be625056bd7109"),
|
||||
hexEncPubkey("a283c474ab09da02bbc96b16317241d0627646fcc427d1fe790b76a7bf1989ced90f92101a973047ae9940c92720dffbac8eff21df8cae468a50f72f9e159417"),
|
||||
hexEncPubkey("dbf7e5ad7f87c3dfecae65d87c3039e14ed0bdc56caf00ce81931073e2e16719d746295512ff7937a15c3b03603e7c41a4f9df94fcd37bb200dd8f332767e9cb"),
|
||||
hexEncPubkey("caaa070a26692f64fc77f30d7b5ae980d419b4393a0f442b1c821ef58c0862898b0d22f74a4f8c5d83069493e3ec0b92f17dc1fe6e4cd437c1ec25039e7ce839"),
|
||||
hexEncPubkey("874cc8d1213beb65c4e0e1de38ef5d8165235893ac74ab5ea937c885eaab25c8d79dad0456e9fd3e9450626cac7e107b004478fb59842f067857f39a47cee695"),
|
||||
hexEncPubkey("d94193f236105010972f5df1b7818b55846592a0445b9cdc4eaed811b8c4c0f7c27dc8cc9837a4774656d6b34682d6d329d42b6ebb55da1d475c2474dc3dfdf4"),
|
||||
hexEncPubkey("edd9af6aded4094e9785637c28fccbd3980cbe28e2eb9a411048a23c2ace4bd6b0b7088a7817997b49a3dd05fc6929ca6c7abbb69438dbdabe65e971d2a794b2"),
|
||||
},
|
||||
250: {
|
||||
MustHexID("53a5bd1215d4ab709ae8fdc2ced50bba320bced78bd9c5dc92947fb402250c914891786db0978c898c058493f86fc68b1c5de8a5cb36336150ac7a88655b6c39"),
|
||||
MustHexID("b7f79e3ab59f79262623c9ccefc8f01d682323aee56ffbe295437487e9d5acaf556a9c92e1f1c6a9601f2b9eb6b027ae1aeaebac71d61b9b78e88676efd3e1a3"),
|
||||
MustHexID("d374bf7e8d7ffff69cc00bebff38ef5bc1dcb0a8d51c1a3d70e61ac6b2e2d6617109254b0ac224354dfbf79009fe4239e09020c483cc60c071e00b9238684f30"),
|
||||
MustHexID("1e1eac1c9add703eb252eb991594f8f5a173255d526a855fab24ae57dc277e055bc3c7a7ae0b45d437c4f47a72d97eb7b126f2ba344ba6c0e14b2c6f27d4b1e6"),
|
||||
MustHexID("ae28953f63d4bc4e706712a59319c111f5ff8f312584f65d7436b4cd3d14b217b958f8486bad666b4481fe879019fb1f767cf15b3e3e2711efc33b56d460448a"),
|
||||
MustHexID("934bb1edf9c7a318b82306aca67feb3d6b434421fa275d694f0b4927afd8b1d3935b727fd4ff6e3d012e0c82f1824385174e8c6450ade59c2a43281a4b3446b6"),
|
||||
MustHexID("9eef3f28f70ce19637519a0916555bf76d26de31312ac656cf9d3e379899ea44e4dd7ffcce923b4f3563f8a00489a34bd6936db0cbb4c959d32c49f017e07d05"),
|
||||
MustHexID("82200872e8f871c48f1fad13daec6478298099b591bb3dbc4ef6890aa28ebee5860d07d70be62f4c0af85085a90ae8179ee8f937cf37915c67ea73e704b03ee7"),
|
||||
MustHexID("6c75a5834a08476b7fc37ff3dc2011dc3ea3b36524bad7a6d319b18878fad813c0ba76d1f4555cacd3890c865438c21f0e0aed1f80e0a157e642124c69f43a11"),
|
||||
MustHexID("995b873742206cb02b736e73a88580c2aacb0bd4a3c97a647b647bcab3f5e03c0e0736520a8b3600da09edf4248991fb01091ec7ff3ec7cdc8a1beae011e7aae"),
|
||||
MustHexID("c773a056594b5cdef2e850d30891ff0e927c3b1b9c35cd8e8d53a1017001e237468e1ece3ae33d612ca3e6abb0a9169aa352e9dcda358e5af2ad982b577447db"),
|
||||
MustHexID("2b46a5f6923f475c6be99ec6d134437a6d11f6bb4b4ac6bcd94572fa1092639d1c08aeefcb51f0912f0a060f71d4f38ee4da70ecc16010b05dd4a674aab14c3a"),
|
||||
MustHexID("af6ab501366debbaa0d22e20e9688f32ef6b3b644440580fd78de4fe0e99e2a16eb5636bbae0d1c259df8ddda77b35b9a35cbc36137473e9c68fbc9d203ba842"),
|
||||
MustHexID("c9f6f2dd1a941926f03f770695bda289859e85fabaf94baaae20b93e5015dc014ba41150176a36a1884adb52f405194693e63b0c464a6891cc9cc1c80d450326"),
|
||||
MustHexID("5b116f0751526868a909b61a30b0c5282c37df6925cc03ddea556ef0d0602a9595fd6c14d371f8ed7d45d89918a032dcd22be4342a8793d88fdbeb3ca3d75bd7"),
|
||||
MustHexID("50f3222fb6b82481c7c813b2172e1daea43e2710a443b9c2a57a12bd160dd37e20f87aa968c82ad639af6972185609d47036c0d93b4b7269b74ebd7073221c10"),
|
||||
hexEncPubkey("53a5bd1215d4ab709ae8fdc2ced50bba320bced78bd9c5dc92947fb402250c914891786db0978c898c058493f86fc68b1c5de8a5cb36336150ac7a88655b6c39"),
|
||||
hexEncPubkey("b7f79e3ab59f79262623c9ccefc8f01d682323aee56ffbe295437487e9d5acaf556a9c92e1f1c6a9601f2b9eb6b027ae1aeaebac71d61b9b78e88676efd3e1a3"),
|
||||
hexEncPubkey("d374bf7e8d7ffff69cc00bebff38ef5bc1dcb0a8d51c1a3d70e61ac6b2e2d6617109254b0ac224354dfbf79009fe4239e09020c483cc60c071e00b9238684f30"),
|
||||
hexEncPubkey("1e1eac1c9add703eb252eb991594f8f5a173255d526a855fab24ae57dc277e055bc3c7a7ae0b45d437c4f47a72d97eb7b126f2ba344ba6c0e14b2c6f27d4b1e6"),
|
||||
hexEncPubkey("ae28953f63d4bc4e706712a59319c111f5ff8f312584f65d7436b4cd3d14b217b958f8486bad666b4481fe879019fb1f767cf15b3e3e2711efc33b56d460448a"),
|
||||
hexEncPubkey("934bb1edf9c7a318b82306aca67feb3d6b434421fa275d694f0b4927afd8b1d3935b727fd4ff6e3d012e0c82f1824385174e8c6450ade59c2a43281a4b3446b6"),
|
||||
hexEncPubkey("9eef3f28f70ce19637519a0916555bf76d26de31312ac656cf9d3e379899ea44e4dd7ffcce923b4f3563f8a00489a34bd6936db0cbb4c959d32c49f017e07d05"),
|
||||
hexEncPubkey("82200872e8f871c48f1fad13daec6478298099b591bb3dbc4ef6890aa28ebee5860d07d70be62f4c0af85085a90ae8179ee8f937cf37915c67ea73e704b03ee7"),
|
||||
hexEncPubkey("6c75a5834a08476b7fc37ff3dc2011dc3ea3b36524bad7a6d319b18878fad813c0ba76d1f4555cacd3890c865438c21f0e0aed1f80e0a157e642124c69f43a11"),
|
||||
hexEncPubkey("995b873742206cb02b736e73a88580c2aacb0bd4a3c97a647b647bcab3f5e03c0e0736520a8b3600da09edf4248991fb01091ec7ff3ec7cdc8a1beae011e7aae"),
|
||||
hexEncPubkey("c773a056594b5cdef2e850d30891ff0e927c3b1b9c35cd8e8d53a1017001e237468e1ece3ae33d612ca3e6abb0a9169aa352e9dcda358e5af2ad982b577447db"),
|
||||
hexEncPubkey("2b46a5f6923f475c6be99ec6d134437a6d11f6bb4b4ac6bcd94572fa1092639d1c08aeefcb51f0912f0a060f71d4f38ee4da70ecc16010b05dd4a674aab14c3a"),
|
||||
hexEncPubkey("af6ab501366debbaa0d22e20e9688f32ef6b3b644440580fd78de4fe0e99e2a16eb5636bbae0d1c259df8ddda77b35b9a35cbc36137473e9c68fbc9d203ba842"),
|
||||
hexEncPubkey("c9f6f2dd1a941926f03f770695bda289859e85fabaf94baaae20b93e5015dc014ba41150176a36a1884adb52f405194693e63b0c464a6891cc9cc1c80d450326"),
|
||||
hexEncPubkey("5b116f0751526868a909b61a30b0c5282c37df6925cc03ddea556ef0d0602a9595fd6c14d371f8ed7d45d89918a032dcd22be4342a8793d88fdbeb3ca3d75bd7"),
|
||||
hexEncPubkey("50f3222fb6b82481c7c813b2172e1daea43e2710a443b9c2a57a12bd160dd37e20f87aa968c82ad639af6972185609d47036c0d93b4b7269b74ebd7073221c10"),
|
||||
},
|
||||
251: {
|
||||
MustHexID("9b8f702a62d1bee67bedfeb102eca7f37fa1713e310f0d6651cc0c33ea7c5477575289ccd463e5a2574a00a676a1fdce05658ba447bb9d2827f0ba47b947e894"),
|
||||
MustHexID("b97532eb83054ed054b4abdf413bb30c00e4205545c93521554dbe77faa3cfaa5bd31ef466a107b0b34a71ec97214c0c83919720142cddac93aa7a3e928d4708"),
|
||||
MustHexID("2f7a5e952bfb67f2f90b8441b5fadc9ee13b1dcde3afeeb3dd64bf937f86663cc5c55d1fa83952b5422763c7df1b7f2794b751c6be316ebc0beb4942e65ab8c1"),
|
||||
MustHexID("42c7483781727051a0b3660f14faf39e0d33de5e643702ae933837d036508ab856ce7eec8ec89c4929a4901256e5233a3d847d5d4893f91bcf21835a9a880fee"),
|
||||
MustHexID("873bae27bf1dc854408fba94046a53ab0c965cebe1e4e12290806fc62b88deb1f4a47f9e18f78fc0e7913a0c6e42ac4d0fc3a20cea6bc65f0c8a0ca90b67521e"),
|
||||
MustHexID("a7e3a370bbd761d413f8d209e85886f68bf73d5c3089b2dc6fa42aab1ecb5162635497eed95dee2417f3c9c74a3e76319625c48ead2e963c7de877cd4551f347"),
|
||||
MustHexID("528597534776a40df2addaaea15b6ff832ce36b9748a265768368f657e76d58569d9f30dbb91e91cf0ae7efe8f402f17aa0ae15f5c55051ba03ba830287f4c42"),
|
||||
MustHexID("461d8bd4f13c3c09031fdb84f104ed737a52f630261463ce0bdb5704259bab4b737dda688285b8444dbecaecad7f50f835190b38684ced5e90c54219e5adf1bc"),
|
||||
MustHexID("6ec50c0be3fd232737090fc0111caaf0bb6b18f72be453428087a11a97fd6b52db0344acbf789a689bd4f5f50f79017ea784f8fd6fe723ad6ae675b9e3b13e21"),
|
||||
MustHexID("12fc5e2f77a83fdcc727b79d8ae7fe6a516881138d3011847ee136b400fed7cfba1f53fd7a9730253c7aa4f39abeacd04f138417ba7fcb0f36cccc3514e0dab6"),
|
||||
MustHexID("4fdbe75914ccd0bce02101606a1ccf3657ec963e3b3c20239d5fec87673fe446d649b4f15f1fe1a40e6cfbd446dda2d31d40bb602b1093b8fcd5f139ba0eb46a"),
|
||||
MustHexID("3753668a0f6281e425ea69b52cb2d17ab97afbe6eb84cf5d25425bc5e53009388857640668fadd7c110721e6047c9697803bd8a6487b43bb343bfa32ebf24039"),
|
||||
MustHexID("2e81b16346637dec4410fd88e527346145b9c0a849dbf2628049ac7dae016c8f4305649d5659ec77f1e8a0fac0db457b6080547226f06283598e3740ad94849a"),
|
||||
MustHexID("802c3cc27f91c89213223d758f8d2ecd41135b357b6d698f24d811cdf113033a81c38e0bdff574a5c005b00a8c193dc2531f8c1fa05fa60acf0ab6f2858af09f"),
|
||||
MustHexID("fcc9a2e1ac3667026ff16192876d1813bb75abdbf39b929a92863012fe8b1d890badea7a0de36274d5c1eb1e8f975785532c50d80fd44b1a4b692f437303393f"),
|
||||
MustHexID("6d8b3efb461151dd4f6de809b62726f5b89e9b38e9ba1391967f61cde844f7528fecf821b74049207cee5a527096b31f3ad623928cd3ce51d926fa345a6b2951"),
|
||||
hexEncPubkey("9b8f702a62d1bee67bedfeb102eca7f37fa1713e310f0d6651cc0c33ea7c5477575289ccd463e5a2574a00a676a1fdce05658ba447bb9d2827f0ba47b947e894"),
|
||||
hexEncPubkey("b97532eb83054ed054b4abdf413bb30c00e4205545c93521554dbe77faa3cfaa5bd31ef466a107b0b34a71ec97214c0c83919720142cddac93aa7a3e928d4708"),
|
||||
hexEncPubkey("2f7a5e952bfb67f2f90b8441b5fadc9ee13b1dcde3afeeb3dd64bf937f86663cc5c55d1fa83952b5422763c7df1b7f2794b751c6be316ebc0beb4942e65ab8c1"),
|
||||
hexEncPubkey("42c7483781727051a0b3660f14faf39e0d33de5e643702ae933837d036508ab856ce7eec8ec89c4929a4901256e5233a3d847d5d4893f91bcf21835a9a880fee"),
|
||||
hexEncPubkey("873bae27bf1dc854408fba94046a53ab0c965cebe1e4e12290806fc62b88deb1f4a47f9e18f78fc0e7913a0c6e42ac4d0fc3a20cea6bc65f0c8a0ca90b67521e"),
|
||||
hexEncPubkey("a7e3a370bbd761d413f8d209e85886f68bf73d5c3089b2dc6fa42aab1ecb5162635497eed95dee2417f3c9c74a3e76319625c48ead2e963c7de877cd4551f347"),
|
||||
hexEncPubkey("528597534776a40df2addaaea15b6ff832ce36b9748a265768368f657e76d58569d9f30dbb91e91cf0ae7efe8f402f17aa0ae15f5c55051ba03ba830287f4c42"),
|
||||
hexEncPubkey("461d8bd4f13c3c09031fdb84f104ed737a52f630261463ce0bdb5704259bab4b737dda688285b8444dbecaecad7f50f835190b38684ced5e90c54219e5adf1bc"),
|
||||
hexEncPubkey("6ec50c0be3fd232737090fc0111caaf0bb6b18f72be453428087a11a97fd6b52db0344acbf789a689bd4f5f50f79017ea784f8fd6fe723ad6ae675b9e3b13e21"),
|
||||
hexEncPubkey("12fc5e2f77a83fdcc727b79d8ae7fe6a516881138d3011847ee136b400fed7cfba1f53fd7a9730253c7aa4f39abeacd04f138417ba7fcb0f36cccc3514e0dab6"),
|
||||
hexEncPubkey("4fdbe75914ccd0bce02101606a1ccf3657ec963e3b3c20239d5fec87673fe446d649b4f15f1fe1a40e6cfbd446dda2d31d40bb602b1093b8fcd5f139ba0eb46a"),
|
||||
hexEncPubkey("3753668a0f6281e425ea69b52cb2d17ab97afbe6eb84cf5d25425bc5e53009388857640668fadd7c110721e6047c9697803bd8a6487b43bb343bfa32ebf24039"),
|
||||
hexEncPubkey("2e81b16346637dec4410fd88e527346145b9c0a849dbf2628049ac7dae016c8f4305649d5659ec77f1e8a0fac0db457b6080547226f06283598e3740ad94849a"),
|
||||
hexEncPubkey("802c3cc27f91c89213223d758f8d2ecd41135b357b6d698f24d811cdf113033a81c38e0bdff574a5c005b00a8c193dc2531f8c1fa05fa60acf0ab6f2858af09f"),
|
||||
hexEncPubkey("fcc9a2e1ac3667026ff16192876d1813bb75abdbf39b929a92863012fe8b1d890badea7a0de36274d5c1eb1e8f975785532c50d80fd44b1a4b692f437303393f"),
|
||||
hexEncPubkey("6d8b3efb461151dd4f6de809b62726f5b89e9b38e9ba1391967f61cde844f7528fecf821b74049207cee5a527096b31f3ad623928cd3ce51d926fa345a6b2951"),
|
||||
},
|
||||
252: {
|
||||
MustHexID("f1ae93157cc48c2075dd5868fbf523e79e06caf4b8198f352f6e526680b78ff4227263de92612f7d63472bd09367bb92a636fff16fe46ccf41614f7a72495c2a"),
|
||||
MustHexID("587f482d111b239c27c0cb89b51dd5d574db8efd8de14a2e6a1400c54d4567e77c65f89c1da52841212080b91604104768350276b6682f2f961cdaf4039581c7"),
|
||||
MustHexID("e3f88274d35cefdaabdf205afe0e80e936cc982b8e3e47a84ce664c413b29016a4fb4f3a3ebae0a2f79671f8323661ed462bf4390af94c424dc8ace0c301b90f"),
|
||||
MustHexID("0ddc736077da9a12ba410dc5ea63cbcbe7659dd08596485b2bff3435221f82c10d263efd9af938e128464be64a178b7cd22e19f400d5802f4c9df54bf89f2619"),
|
||||
MustHexID("784aa34d833c6ce63fcc1279630113c3272e82c4ae8c126c5a52a88ac461b6baeed4244e607b05dc14e5b2f41c70a273c3804dea237f14f7a1e546f6d1309d14"),
|
||||
MustHexID("f253a2c354ee0e27cfcae786d726753d4ad24be6516b279a936195a487de4a59dbc296accf20463749ff55293263ed8c1b6365eecb248d44e75e9741c0d18205"),
|
||||
MustHexID("a1910b80357b3ad9b4593e0628922939614dc9056a5fbf477279c8b2c1d0b4b31d89a0c09d0d41f795271d14d3360ef08a3f821e65e7e1f56c07a36afe49c7c5"),
|
||||
MustHexID("f1168552c2efe541160f0909b0b4a9d6aeedcf595cdf0e9b165c97e3e197471a1ee6320e93389edfba28af6eaf10de98597ad56e7ab1b504ed762451996c3b98"),
|
||||
MustHexID("b0c8e5d2c8634a7930e1a6fd082e448c6cf9d2d8b7293558b59238815a4df926c286bf297d2049f14e8296a6eb3256af614ec1812c4f2bbe807673b58bf14c8c"),
|
||||
MustHexID("0fb346076396a38badc342df3679b55bd7f40a609ab103411fe45082c01f12ea016729e95914b2b5540e987ff5c9b133e85862648e7f36abdfd23100d248d234"),
|
||||
MustHexID("f736e0cc83417feaa280d9483f5d4d72d1b036cd0c6d9cbdeb8ac35ceb2604780de46dddaa32a378474e1d5ccdf79b373331c30c7911ade2ae32f98832e5de1f"),
|
||||
MustHexID("8b02991457602f42b38b342d3f2259ae4100c354b3843885f7e4e07bd644f64dab94bb7f38a3915f8b7f11d8e3f81c28e07a0078cf79d7397e38a7b7e0c857e2"),
|
||||
MustHexID("9221d9f04a8a184993d12baa91116692bb685f887671302999d69300ad103eb2d2c75a09d8979404c6dd28f12362f58a1a43619c493d9108fd47588a23ce5824"),
|
||||
MustHexID("652797801744dada833fff207d67484742eea6835d695925f3e618d71b68ec3c65bdd85b4302b2cdcb835ad3f94fd00d8da07e570b41bc0d2bcf69a8de1b3284"),
|
||||
MustHexID("d84f06fe64debc4cd0625e36d19b99014b6218375262cc2209202bdbafd7dffcc4e34ce6398e182e02fd8faeed622c3e175545864902dfd3d1ac57647cddf4c6"),
|
||||
MustHexID("d0ed87b294f38f1d741eb601020eeec30ac16331d05880fe27868f1e454446de367d7457b41c79e202eaf9525b029e4f1d7e17d85a55f83a557c005c68d7328a"),
|
||||
hexEncPubkey("f1ae93157cc48c2075dd5868fbf523e79e06caf4b8198f352f6e526680b78ff4227263de92612f7d63472bd09367bb92a636fff16fe46ccf41614f7a72495c2a"),
|
||||
hexEncPubkey("587f482d111b239c27c0cb89b51dd5d574db8efd8de14a2e6a1400c54d4567e77c65f89c1da52841212080b91604104768350276b6682f2f961cdaf4039581c7"),
|
||||
hexEncPubkey("e3f88274d35cefdaabdf205afe0e80e936cc982b8e3e47a84ce664c413b29016a4fb4f3a3ebae0a2f79671f8323661ed462bf4390af94c424dc8ace0c301b90f"),
|
||||
hexEncPubkey("0ddc736077da9a12ba410dc5ea63cbcbe7659dd08596485b2bff3435221f82c10d263efd9af938e128464be64a178b7cd22e19f400d5802f4c9df54bf89f2619"),
|
||||
hexEncPubkey("784aa34d833c6ce63fcc1279630113c3272e82c4ae8c126c5a52a88ac461b6baeed4244e607b05dc14e5b2f41c70a273c3804dea237f14f7a1e546f6d1309d14"),
|
||||
hexEncPubkey("f253a2c354ee0e27cfcae786d726753d4ad24be6516b279a936195a487de4a59dbc296accf20463749ff55293263ed8c1b6365eecb248d44e75e9741c0d18205"),
|
||||
hexEncPubkey("a1910b80357b3ad9b4593e0628922939614dc9056a5fbf477279c8b2c1d0b4b31d89a0c09d0d41f795271d14d3360ef08a3f821e65e7e1f56c07a36afe49c7c5"),
|
||||
hexEncPubkey("f1168552c2efe541160f0909b0b4a9d6aeedcf595cdf0e9b165c97e3e197471a1ee6320e93389edfba28af6eaf10de98597ad56e7ab1b504ed762451996c3b98"),
|
||||
hexEncPubkey("b0c8e5d2c8634a7930e1a6fd082e448c6cf9d2d8b7293558b59238815a4df926c286bf297d2049f14e8296a6eb3256af614ec1812c4f2bbe807673b58bf14c8c"),
|
||||
hexEncPubkey("0fb346076396a38badc342df3679b55bd7f40a609ab103411fe45082c01f12ea016729e95914b2b5540e987ff5c9b133e85862648e7f36abdfd23100d248d234"),
|
||||
hexEncPubkey("f736e0cc83417feaa280d9483f5d4d72d1b036cd0c6d9cbdeb8ac35ceb2604780de46dddaa32a378474e1d5ccdf79b373331c30c7911ade2ae32f98832e5de1f"),
|
||||
hexEncPubkey("8b02991457602f42b38b342d3f2259ae4100c354b3843885f7e4e07bd644f64dab94bb7f38a3915f8b7f11d8e3f81c28e07a0078cf79d7397e38a7b7e0c857e2"),
|
||||
hexEncPubkey("9221d9f04a8a184993d12baa91116692bb685f887671302999d69300ad103eb2d2c75a09d8979404c6dd28f12362f58a1a43619c493d9108fd47588a23ce5824"),
|
||||
hexEncPubkey("652797801744dada833fff207d67484742eea6835d695925f3e618d71b68ec3c65bdd85b4302b2cdcb835ad3f94fd00d8da07e570b41bc0d2bcf69a8de1b3284"),
|
||||
hexEncPubkey("d84f06fe64debc4cd0625e36d19b99014b6218375262cc2209202bdbafd7dffcc4e34ce6398e182e02fd8faeed622c3e175545864902dfd3d1ac57647cddf4c6"),
|
||||
hexEncPubkey("d0ed87b294f38f1d741eb601020eeec30ac16331d05880fe27868f1e454446de367d7457b41c79e202eaf9525b029e4f1d7e17d85a55f83a557c005c68d7328a"),
|
||||
},
|
||||
253: {
|
||||
MustHexID("ad4485e386e3cc7c7310366a7c38fb810b8896c0d52e55944bfd320ca294e7912d6c53c0a0cf85e7ce226e92491d60430e86f8f15cda0161ed71893fb4a9e3a1"),
|
||||
MustHexID("36d0e7e5b7734f98c6183eeeb8ac5130a85e910a925311a19c4941b1290f945d4fc3996b12ef4966960b6fa0fb29b1604f83a0f81bd5fd6398d2e1a22e46af0c"),
|
||||
MustHexID("7d307d8acb4a561afa23bdf0bd945d35c90245e26345ec3a1f9f7df354222a7cdcb81339c9ed6744526c27a1a0c8d10857e98df942fa433602facac71ac68a31"),
|
||||
MustHexID("d97bf55f88c83fae36232661af115d66ca600fc4bd6d1fb35ff9bb4dad674c02cf8c8d05f317525b5522250db58bb1ecafb7157392bf5aa61b178c61f098d995"),
|
||||
MustHexID("7045d678f1f9eb7a4613764d17bd5698796494d0bf977b16f2dbc272b8a0f7858a60805c022fc3d1fe4f31c37e63cdaca0416c0d053ef48a815f8b19121605e0"),
|
||||
MustHexID("14e1f21418d445748de2a95cd9a8c3b15b506f86a0acabd8af44bb968ce39885b19c8822af61b3dd58a34d1f265baec30e3ae56149dc7d2aa4a538f7319f69c8"),
|
||||
MustHexID("b9453d78281b66a4eac95a1546017111eaaa5f92a65d0de10b1122940e92b319728a24edf4dec6acc412321b1c95266d39c7b3a5d265c629c3e49a65fb022c09"),
|
||||
MustHexID("e8a49248419e3824a00d86af422f22f7366e2d4922b304b7169937616a01d9d6fa5abf5cc01061a352dc866f48e1fa2240dbb453d872b1d7be62bdfc1d5e248c"),
|
||||
MustHexID("bebcff24b52362f30e0589ee573ce2d86f073d58d18e6852a592fa86ceb1a6c9b96d7fb9ec7ed1ed98a51b6743039e780279f6bb49d0a04327ac7a182d9a56f6"),
|
||||
MustHexID("d0835e5a4291db249b8d2fca9f503049988180c7d247bedaa2cf3a1bad0a76709360a85d4f9a1423b2cbc82bb4d94b47c0cde20afc430224834c49fe312a9ae3"),
|
||||
MustHexID("6b087fe2a2da5e4f0b0f4777598a4a7fb66bf77dbd5bfc44e8a7eaa432ab585a6e226891f56a7d4f5ed11a7c57b90f1661bba1059590ca4267a35801c2802913"),
|
||||
MustHexID("d901e5bde52d1a0f4ddf010a686a53974cdae4ebe5c6551b3c37d6b6d635d38d5b0e5f80bc0186a2c7809dbf3a42870dd09643e68d32db896c6da8ba734579e7"),
|
||||
MustHexID("96419fb80efae4b674402bb969ebaab86c1274f29a83a311e24516d36cdf148fe21754d46c97688cdd7468f24c08b13e4727c29263393638a3b37b99ff60ebca"),
|
||||
MustHexID("7b9c1889ae916a5d5abcdfb0aaedcc9c6f9eb1c1a4f68d0c2d034fe79ac610ce917c3abc670744150fa891bfcd8ab14fed6983fca964de920aa393fa7b326748"),
|
||||
MustHexID("7a369b2b8962cc4c65900be046482fbf7c14f98a135bbbae25152c82ad168fb2097b3d1429197cf46d3ce9fdeb64808f908a489cc6019725db040060fdfe5405"),
|
||||
MustHexID("47bcae48288da5ecc7f5058dfa07cf14d89d06d6e449cb946e237aa6652ea050d9f5a24a65efdc0013ccf232bf88670979eddef249b054f63f38da9d7796dbd8"),
|
||||
hexEncPubkey("ad4485e386e3cc7c7310366a7c38fb810b8896c0d52e55944bfd320ca294e7912d6c53c0a0cf85e7ce226e92491d60430e86f8f15cda0161ed71893fb4a9e3a1"),
|
||||
hexEncPubkey("36d0e7e5b7734f98c6183eeeb8ac5130a85e910a925311a19c4941b1290f945d4fc3996b12ef4966960b6fa0fb29b1604f83a0f81bd5fd6398d2e1a22e46af0c"),
|
||||
hexEncPubkey("7d307d8acb4a561afa23bdf0bd945d35c90245e26345ec3a1f9f7df354222a7cdcb81339c9ed6744526c27a1a0c8d10857e98df942fa433602facac71ac68a31"),
|
||||
hexEncPubkey("d97bf55f88c83fae36232661af115d66ca600fc4bd6d1fb35ff9bb4dad674c02cf8c8d05f317525b5522250db58bb1ecafb7157392bf5aa61b178c61f098d995"),
|
||||
hexEncPubkey("7045d678f1f9eb7a4613764d17bd5698796494d0bf977b16f2dbc272b8a0f7858a60805c022fc3d1fe4f31c37e63cdaca0416c0d053ef48a815f8b19121605e0"),
|
||||
hexEncPubkey("14e1f21418d445748de2a95cd9a8c3b15b506f86a0acabd8af44bb968ce39885b19c8822af61b3dd58a34d1f265baec30e3ae56149dc7d2aa4a538f7319f69c8"),
|
||||
hexEncPubkey("b9453d78281b66a4eac95a1546017111eaaa5f92a65d0de10b1122940e92b319728a24edf4dec6acc412321b1c95266d39c7b3a5d265c629c3e49a65fb022c09"),
|
||||
hexEncPubkey("e8a49248419e3824a00d86af422f22f7366e2d4922b304b7169937616a01d9d6fa5abf5cc01061a352dc866f48e1fa2240dbb453d872b1d7be62bdfc1d5e248c"),
|
||||
hexEncPubkey("bebcff24b52362f30e0589ee573ce2d86f073d58d18e6852a592fa86ceb1a6c9b96d7fb9ec7ed1ed98a51b6743039e780279f6bb49d0a04327ac7a182d9a56f6"),
|
||||
hexEncPubkey("d0835e5a4291db249b8d2fca9f503049988180c7d247bedaa2cf3a1bad0a76709360a85d4f9a1423b2cbc82bb4d94b47c0cde20afc430224834c49fe312a9ae3"),
|
||||
hexEncPubkey("6b087fe2a2da5e4f0b0f4777598a4a7fb66bf77dbd5bfc44e8a7eaa432ab585a6e226891f56a7d4f5ed11a7c57b90f1661bba1059590ca4267a35801c2802913"),
|
||||
hexEncPubkey("d901e5bde52d1a0f4ddf010a686a53974cdae4ebe5c6551b3c37d6b6d635d38d5b0e5f80bc0186a2c7809dbf3a42870dd09643e68d32db896c6da8ba734579e7"),
|
||||
hexEncPubkey("96419fb80efae4b674402bb969ebaab86c1274f29a83a311e24516d36cdf148fe21754d46c97688cdd7468f24c08b13e4727c29263393638a3b37b99ff60ebca"),
|
||||
hexEncPubkey("7b9c1889ae916a5d5abcdfb0aaedcc9c6f9eb1c1a4f68d0c2d034fe79ac610ce917c3abc670744150fa891bfcd8ab14fed6983fca964de920aa393fa7b326748"),
|
||||
hexEncPubkey("7a369b2b8962cc4c65900be046482fbf7c14f98a135bbbae25152c82ad168fb2097b3d1429197cf46d3ce9fdeb64808f908a489cc6019725db040060fdfe5405"),
|
||||
hexEncPubkey("47bcae48288da5ecc7f5058dfa07cf14d89d06d6e449cb946e237aa6652ea050d9f5a24a65efdc0013ccf232bf88670979eddef249b054f63f38da9d7796dbd8"),
|
||||
},
|
||||
254: {
|
||||
MustHexID("099739d7abc8abd38ecc7a816c521a1168a4dbd359fa7212a5123ab583ffa1cf485a5fed219575d6475dbcdd541638b2d3631a6c7fce7474e7fe3cba1d4d5853"),
|
||||
MustHexID("c2b01603b088a7182d0cf7ef29fb2b04c70acb320fccf78526bf9472e10c74ee70b3fcfa6f4b11d167bd7d3bc4d936b660f2c9bff934793d97cb21750e7c3d31"),
|
||||
MustHexID("20e4d8f45f2f863e94b45548c1ef22a11f7d36f263e4f8623761e05a64c4572379b000a52211751e2561b0f14f4fc92dd4130410c8ccc71eb4f0e95a700d4ca9"),
|
||||
MustHexID("27f4a16cc085e72d86e25c98bd2eca173eaaee7565c78ec5a52e9e12b2211f35de81b5b45e9195de2ebfe29106742c59112b951a04eb7ae48822911fc1f9389e"),
|
||||
MustHexID("55db5ee7d98e7f0b1c3b9d5be6f2bc619a1b86c3cdd513160ad4dcf267037a5fffad527ac15d50aeb32c59c13d1d4c1e567ebbf4de0d25236130c8361f9aac63"),
|
||||
MustHexID("883df308b0130fc928a8559fe50667a0fff80493bc09685d18213b2db241a3ad11310ed86b0ef662b3ce21fc3d9aa7f3fc24b8d9afe17c7407e9afd3345ae548"),
|
||||
MustHexID("c7af968cc9bc8200c3ee1a387405f7563be1dce6710a3439f42ea40657d0eae9d2b3c16c42d779605351fcdece4da637b9804e60ca08cfb89aec32c197beffa6"),
|
||||
MustHexID("3e66f2b788e3ff1d04106b80597915cd7afa06c405a7ae026556b6e583dca8e05cfbab5039bb9a1b5d06083ffe8de5780b1775550e7218f5e98624bf7af9a0a8"),
|
||||
MustHexID("4fc7f53764de3337fdaec0a711d35d3a923e72fa65025444d12230b3552ed43d9b2d1ad08ccb11f2d50c58809e6dd74dde910e195294fca3b47ae5a3967cc479"),
|
||||
MustHexID("bafdfdcf6ccaa989436752fa97c77477b6baa7deb374b16c095492c529eb133e8e2f99e1977012b64767b9d34b2cf6d2048ed489bd822b5139b523f6a423167b"),
|
||||
MustHexID("7f5d78008a4312fe059104ce80202c82b8915c2eb4411c6b812b16f7642e57c00f2c9425121f5cbac4257fe0b3e81ef5dea97ea2dbaa98f6a8b6fd4d1e5980bb"),
|
||||
MustHexID("598c37fe78f922751a052f463aeb0cb0bc7f52b7c2a4cf2da72ec0931c7c32175d4165d0f8998f7320e87324ac3311c03f9382a5385c55f0407b7a66b2acd864"),
|
||||
MustHexID("f758c4136e1c148777a7f3275a76e2db0b2b04066fd738554ec398c1c6cc9fb47e14a3b4c87bd47deaeab3ffd2110514c3855685a374794daff87b605b27ee2e"),
|
||||
MustHexID("0307bb9e4fd865a49dcf1fe4333d1b944547db650ab580af0b33e53c4fef6c789531110fac801bbcbce21fc4d6f61b6d5b24abdf5b22e3030646d579f6dca9c2"),
|
||||
MustHexID("82504b6eb49bb2c0f91a7006ce9cefdbaf6df38706198502c2e06601091fc9dc91e4f15db3410d45c6af355bc270b0f268d3dff560f956985c7332d4b10bd1ed"),
|
||||
MustHexID("b39b5b677b45944ceebe76e76d1f051de2f2a0ec7b0d650da52135743e66a9a5dba45f638258f9a7545d9a790c7fe6d3fdf82c25425c7887323e45d27d06c057"),
|
||||
hexEncPubkey("099739d7abc8abd38ecc7a816c521a1168a4dbd359fa7212a5123ab583ffa1cf485a5fed219575d6475dbcdd541638b2d3631a6c7fce7474e7fe3cba1d4d5853"),
|
||||
hexEncPubkey("c2b01603b088a7182d0cf7ef29fb2b04c70acb320fccf78526bf9472e10c74ee70b3fcfa6f4b11d167bd7d3bc4d936b660f2c9bff934793d97cb21750e7c3d31"),
|
||||
hexEncPubkey("20e4d8f45f2f863e94b45548c1ef22a11f7d36f263e4f8623761e05a64c4572379b000a52211751e2561b0f14f4fc92dd4130410c8ccc71eb4f0e95a700d4ca9"),
|
||||
hexEncPubkey("27f4a16cc085e72d86e25c98bd2eca173eaaee7565c78ec5a52e9e12b2211f35de81b5b45e9195de2ebfe29106742c59112b951a04eb7ae48822911fc1f9389e"),
|
||||
hexEncPubkey("55db5ee7d98e7f0b1c3b9d5be6f2bc619a1b86c3cdd513160ad4dcf267037a5fffad527ac15d50aeb32c59c13d1d4c1e567ebbf4de0d25236130c8361f9aac63"),
|
||||
hexEncPubkey("883df308b0130fc928a8559fe50667a0fff80493bc09685d18213b2db241a3ad11310ed86b0ef662b3ce21fc3d9aa7f3fc24b8d9afe17c7407e9afd3345ae548"),
|
||||
hexEncPubkey("c7af968cc9bc8200c3ee1a387405f7563be1dce6710a3439f42ea40657d0eae9d2b3c16c42d779605351fcdece4da637b9804e60ca08cfb89aec32c197beffa6"),
|
||||
hexEncPubkey("3e66f2b788e3ff1d04106b80597915cd7afa06c405a7ae026556b6e583dca8e05cfbab5039bb9a1b5d06083ffe8de5780b1775550e7218f5e98624bf7af9a0a8"),
|
||||
hexEncPubkey("4fc7f53764de3337fdaec0a711d35d3a923e72fa65025444d12230b3552ed43d9b2d1ad08ccb11f2d50c58809e6dd74dde910e195294fca3b47ae5a3967cc479"),
|
||||
hexEncPubkey("bafdfdcf6ccaa989436752fa97c77477b6baa7deb374b16c095492c529eb133e8e2f99e1977012b64767b9d34b2cf6d2048ed489bd822b5139b523f6a423167b"),
|
||||
hexEncPubkey("7f5d78008a4312fe059104ce80202c82b8915c2eb4411c6b812b16f7642e57c00f2c9425121f5cbac4257fe0b3e81ef5dea97ea2dbaa98f6a8b6fd4d1e5980bb"),
|
||||
hexEncPubkey("598c37fe78f922751a052f463aeb0cb0bc7f52b7c2a4cf2da72ec0931c7c32175d4165d0f8998f7320e87324ac3311c03f9382a5385c55f0407b7a66b2acd864"),
|
||||
hexEncPubkey("f758c4136e1c148777a7f3275a76e2db0b2b04066fd738554ec398c1c6cc9fb47e14a3b4c87bd47deaeab3ffd2110514c3855685a374794daff87b605b27ee2e"),
|
||||
hexEncPubkey("0307bb9e4fd865a49dcf1fe4333d1b944547db650ab580af0b33e53c4fef6c789531110fac801bbcbce21fc4d6f61b6d5b24abdf5b22e3030646d579f6dca9c2"),
|
||||
hexEncPubkey("82504b6eb49bb2c0f91a7006ce9cefdbaf6df38706198502c2e06601091fc9dc91e4f15db3410d45c6af355bc270b0f268d3dff560f956985c7332d4b10bd1ed"),
|
||||
hexEncPubkey("b39b5b677b45944ceebe76e76d1f051de2f2a0ec7b0d650da52135743e66a9a5dba45f638258f9a7545d9a790c7fe6d3fdf82c25425c7887323e45d27d06c057"),
|
||||
},
|
||||
255: {
|
||||
MustHexID("5c4d58d46e055dd1f093f81ee60a675e1f02f54da6206720adee4dccef9b67a31efc5c2a2949c31a04ee31beadc79aba10da31440a1f9ff2a24093c63c36d784"),
|
||||
MustHexID("ea72161ffdd4b1e124c7b93b0684805f4c4b58d617ed498b37a145c670dbc2e04976f8785583d9c805ffbf343c31d492d79f841652bbbd01b61ed85640b23495"),
|
||||
MustHexID("51caa1d93352d47a8e531692a3612adac1e8ac68d0a200d086c1c57ae1e1a91aa285ab242e8c52ef9d7afe374c9485b122ae815f1707b875569d0433c1c3ce85"),
|
||||
MustHexID("c08397d5751b47bd3da044b908be0fb0e510d3149574dff7aeab33749b023bb171b5769990fe17469dbebc100bc150e798aeda426a2dcc766699a225fddd75c6"),
|
||||
MustHexID("0222c1c194b749736e593f937fad67ee348ac57287a15c7e42877aa38a9b87732a408bca370f812efd0eedbff13e6d5b854bf3ba1dec431a796ed47f32552b09"),
|
||||
MustHexID("03d859cd46ef02d9bfad5268461a6955426845eef4126de6be0fa4e8d7e0727ba2385b78f1a883a8239e95ebb814f2af8379632c7d5b100688eebc5841209582"),
|
||||
MustHexID("64d5004b7e043c39ff0bd10cb20094c287721d5251715884c280a612b494b3e9e1c64ba6f67614994c7d969a0d0c0295d107d53fc225d47c44c4b82852d6f960"),
|
||||
MustHexID("b0a5eefb2dab6f786670f35bf9641eefe6dd87fd3f1362bcab4aaa792903500ab23d88fae68411372e0813b057535a601d46e454323745a948017f6063a47b1f"),
|
||||
MustHexID("0cc6df0a3433d448b5684d2a3ffa9d1a825388177a18f44ad0008c7bd7702f1ec0fc38b83506f7de689c3b6ecb552599927e29699eed6bb867ff08f80068b287"),
|
||||
MustHexID("50772f7b8c03a4e153355fbbf79c8a80cf32af656ff0c7873c99911099d04a0dae0674706c357e0145ad017a0ade65e6052cb1b0d574fcd6f67da3eee0ace66b"),
|
||||
MustHexID("1ae37829c9ef41f8b508b82259ebac76b1ed900d7a45c08b7970f25d2d48ddd1829e2f11423a18749940b6dab8598c6e416cef0efd47e46e51f29a0bc65b37cd"),
|
||||
MustHexID("ba973cab31c2af091fc1644a93527d62b2394999e2b6ccbf158dd5ab9796a43d408786f1803ef4e29debfeb62fce2b6caa5ab2b24d1549c822a11c40c2856665"),
|
||||
MustHexID("bc413ad270dd6ea25bddba78f3298b03b8ba6f8608ac03d06007d4116fa78ef5a0cfe8c80155089382fc7a193243ee5500082660cb5d7793f60f2d7d18650964"),
|
||||
MustHexID("5a6a9ef07634d9eec3baa87c997b529b92652afa11473dfee41ef7037d5c06e0ddb9fe842364462d79dd31cff8a59a1b8d5bc2b810dea1d4cbbd3beb80ecec83"),
|
||||
MustHexID("f492c6ee2696d5f682f7f537757e52744c2ae560f1090a07024609e903d334e9e174fc01609c5a229ddbcac36c9d21adaf6457dab38a25bfd44f2f0ee4277998"),
|
||||
MustHexID("459e4db99298cb0467a90acee6888b08bb857450deac11015cced5104853be5adce5b69c740968bc7f931495d671a70cad9f48546d7cd203357fe9af0e8d2164"),
|
||||
hexEncPubkey("5c4d58d46e055dd1f093f81ee60a675e1f02f54da6206720adee4dccef9b67a31efc5c2a2949c31a04ee31beadc79aba10da31440a1f9ff2a24093c63c36d784"),
|
||||
hexEncPubkey("ea72161ffdd4b1e124c7b93b0684805f4c4b58d617ed498b37a145c670dbc2e04976f8785583d9c805ffbf343c31d492d79f841652bbbd01b61ed85640b23495"),
|
||||
hexEncPubkey("51caa1d93352d47a8e531692a3612adac1e8ac68d0a200d086c1c57ae1e1a91aa285ab242e8c52ef9d7afe374c9485b122ae815f1707b875569d0433c1c3ce85"),
|
||||
hexEncPubkey("c08397d5751b47bd3da044b908be0fb0e510d3149574dff7aeab33749b023bb171b5769990fe17469dbebc100bc150e798aeda426a2dcc766699a225fddd75c6"),
|
||||
hexEncPubkey("0222c1c194b749736e593f937fad67ee348ac57287a15c7e42877aa38a9b87732a408bca370f812efd0eedbff13e6d5b854bf3ba1dec431a796ed47f32552b09"),
|
||||
hexEncPubkey("03d859cd46ef02d9bfad5268461a6955426845eef4126de6be0fa4e8d7e0727ba2385b78f1a883a8239e95ebb814f2af8379632c7d5b100688eebc5841209582"),
|
||||
hexEncPubkey("64d5004b7e043c39ff0bd10cb20094c287721d5251715884c280a612b494b3e9e1c64ba6f67614994c7d969a0d0c0295d107d53fc225d47c44c4b82852d6f960"),
|
||||
hexEncPubkey("b0a5eefb2dab6f786670f35bf9641eefe6dd87fd3f1362bcab4aaa792903500ab23d88fae68411372e0813b057535a601d46e454323745a948017f6063a47b1f"),
|
||||
hexEncPubkey("0cc6df0a3433d448b5684d2a3ffa9d1a825388177a18f44ad0008c7bd7702f1ec0fc38b83506f7de689c3b6ecb552599927e29699eed6bb867ff08f80068b287"),
|
||||
hexEncPubkey("50772f7b8c03a4e153355fbbf79c8a80cf32af656ff0c7873c99911099d04a0dae0674706c357e0145ad017a0ade65e6052cb1b0d574fcd6f67da3eee0ace66b"),
|
||||
hexEncPubkey("1ae37829c9ef41f8b508b82259ebac76b1ed900d7a45c08b7970f25d2d48ddd1829e2f11423a18749940b6dab8598c6e416cef0efd47e46e51f29a0bc65b37cd"),
|
||||
hexEncPubkey("ba973cab31c2af091fc1644a93527d62b2394999e2b6ccbf158dd5ab9796a43d408786f1803ef4e29debfeb62fce2b6caa5ab2b24d1549c822a11c40c2856665"),
|
||||
hexEncPubkey("bc413ad270dd6ea25bddba78f3298b03b8ba6f8608ac03d06007d4116fa78ef5a0cfe8c80155089382fc7a193243ee5500082660cb5d7793f60f2d7d18650964"),
|
||||
hexEncPubkey("5a6a9ef07634d9eec3baa87c997b529b92652afa11473dfee41ef7037d5c06e0ddb9fe842364462d79dd31cff8a59a1b8d5bc2b810dea1d4cbbd3beb80ecec83"),
|
||||
hexEncPubkey("f492c6ee2696d5f682f7f537757e52744c2ae560f1090a07024609e903d334e9e174fc01609c5a229ddbcac36c9d21adaf6457dab38a25bfd44f2f0ee4277998"),
|
||||
hexEncPubkey("459e4db99298cb0467a90acee6888b08bb857450deac11015cced5104853be5adce5b69c740968bc7f931495d671a70cad9f48546d7cd203357fe9af0e8d2164"),
|
||||
},
|
||||
256: {
|
||||
MustHexID("a8593af8a4aef7b806b5197612017951bac8845a1917ca9a6a15dd6086d608505144990b245785c4cd2d67a295701c7aac2aa18823fb0033987284b019656268"),
|
||||
MustHexID("d2eebef914928c3aad77fc1b2a495f52d2294acf5edaa7d8a530b540f094b861a68fe8348a46a7c302f08ab609d85912a4968eacfea0740847b29421b4795d9e"),
|
||||
MustHexID("b14bfcb31495f32b650b63cf7d08492e3e29071fdc73cf2da0da48d4b191a70ba1a65f42ad8c343206101f00f8a48e8db4b08bf3f622c0853e7323b250835b91"),
|
||||
MustHexID("7feaee0d818c03eb30e4e0bf03ade0f3c21ca38e938a761aa1781cf70bda8cc5cd631a6cc53dd44f1d4a6d3e2dae6513c6c66ee50cb2f0e9ad6f7e319b309fd9"),
|
||||
MustHexID("4ca3b657b139311db8d583c25dd5963005e46689e1317620496cc64129c7f3e52870820e0ec7941d28809311df6db8a2867bbd4f235b4248af24d7a9c22d1232"),
|
||||
MustHexID("1181defb1d16851d42dd951d84424d6bd1479137f587fa184d5a8152be6b6b16ed08bcdb2c2ed8539bcde98c80c432875f9f724737c316a2bd385a39d3cab1d8"),
|
||||
MustHexID("d9dd818769fa0c3ec9f553c759b92476f082817252a04a47dc1777740b1731d280058c66f982812f173a294acf4944a85ba08346e2de153ba3ba41ce8a62cb64"),
|
||||
MustHexID("bd7c4f8a9e770aa915c771b15e107ca123d838762da0d3ffc53aa6b53e9cd076cffc534ec4d2e4c334c683f1f5ea72e0e123f6c261915ed5b58ac1b59f003d88"),
|
||||
MustHexID("3dd5739c73649d510456a70e9d6b46a855864a4a3f744e088fd8c8da11b18e4c9b5f2d7da50b1c147b2bae5ca9609ae01f7a3cdea9dce34f80a91d29cd82f918"),
|
||||
MustHexID("f0d7df1efc439b4bcc0b762118c1cfa99b2a6143a9f4b10e3c9465125f4c9fca4ab88a2504169bbcad65492cf2f50da9dd5d077c39574a944f94d8246529066b"),
|
||||
MustHexID("dd598b9ba441448e5fb1a6ec6c5f5aa9605bad6e223297c729b1705d11d05f6bfd3d41988b694681ae69bb03b9a08bff4beab5596503d12a39bffb5cd6e94c7c"),
|
||||
MustHexID("3fce284ac97e567aebae681b15b7a2b6df9d873945536335883e4bbc26460c064370537f323fd1ada828ea43154992d14ac0cec0940a2bd2a3f42ec156d60c83"),
|
||||
MustHexID("7c8dfa8c1311cb14fb29a8ac11bca23ecc115e56d9fcf7b7ac1db9066aa4eb39f8b1dabf46e192a65be95ebfb4e839b5ab4533fef414921825e996b210dd53bd"),
|
||||
MustHexID("cafa6934f82120456620573d7f801390ed5e16ed619613a37e409e44ab355ef755e83565a913b48a9466db786f8d4fbd590bfec474c2524d4a2608d4eafd6abd"),
|
||||
MustHexID("9d16600d0dd310d77045769fed2cb427f32db88cd57d86e49390c2ba8a9698cfa856f775be2013237226e7bf47b248871cf865d23015937d1edeb20db5e3e760"),
|
||||
MustHexID("17be6b6ba54199b1d80eff866d348ea11d8a4b341d63ad9a6681d3ef8a43853ac564d153eb2a8737f0afc9ab320f6f95c55aa11aaa13bbb1ff422fd16bdf8188"),
|
||||
hexEncPubkey("a8593af8a4aef7b806b5197612017951bac8845a1917ca9a6a15dd6086d608505144990b245785c4cd2d67a295701c7aac2aa18823fb0033987284b019656268"),
|
||||
hexEncPubkey("d2eebef914928c3aad77fc1b2a495f52d2294acf5edaa7d8a530b540f094b861a68fe8348a46a7c302f08ab609d85912a4968eacfea0740847b29421b4795d9e"),
|
||||
hexEncPubkey("b14bfcb31495f32b650b63cf7d08492e3e29071fdc73cf2da0da48d4b191a70ba1a65f42ad8c343206101f00f8a48e8db4b08bf3f622c0853e7323b250835b91"),
|
||||
hexEncPubkey("7feaee0d818c03eb30e4e0bf03ade0f3c21ca38e938a761aa1781cf70bda8cc5cd631a6cc53dd44f1d4a6d3e2dae6513c6c66ee50cb2f0e9ad6f7e319b309fd9"),
|
||||
hexEncPubkey("4ca3b657b139311db8d583c25dd5963005e46689e1317620496cc64129c7f3e52870820e0ec7941d28809311df6db8a2867bbd4f235b4248af24d7a9c22d1232"),
|
||||
hexEncPubkey("1181defb1d16851d42dd951d84424d6bd1479137f587fa184d5a8152be6b6b16ed08bcdb2c2ed8539bcde98c80c432875f9f724737c316a2bd385a39d3cab1d8"),
|
||||
hexEncPubkey("d9dd818769fa0c3ec9f553c759b92476f082817252a04a47dc1777740b1731d280058c66f982812f173a294acf4944a85ba08346e2de153ba3ba41ce8a62cb64"),
|
||||
hexEncPubkey("bd7c4f8a9e770aa915c771b15e107ca123d838762da0d3ffc53aa6b53e9cd076cffc534ec4d2e4c334c683f1f5ea72e0e123f6c261915ed5b58ac1b59f003d88"),
|
||||
hexEncPubkey("3dd5739c73649d510456a70e9d6b46a855864a4a3f744e088fd8c8da11b18e4c9b5f2d7da50b1c147b2bae5ca9609ae01f7a3cdea9dce34f80a91d29cd82f918"),
|
||||
hexEncPubkey("f0d7df1efc439b4bcc0b762118c1cfa99b2a6143a9f4b10e3c9465125f4c9fca4ab88a2504169bbcad65492cf2f50da9dd5d077c39574a944f94d8246529066b"),
|
||||
hexEncPubkey("dd598b9ba441448e5fb1a6ec6c5f5aa9605bad6e223297c729b1705d11d05f6bfd3d41988b694681ae69bb03b9a08bff4beab5596503d12a39bffb5cd6e94c7c"),
|
||||
hexEncPubkey("3fce284ac97e567aebae681b15b7a2b6df9d873945536335883e4bbc26460c064370537f323fd1ada828ea43154992d14ac0cec0940a2bd2a3f42ec156d60c83"),
|
||||
hexEncPubkey("7c8dfa8c1311cb14fb29a8ac11bca23ecc115e56d9fcf7b7ac1db9066aa4eb39f8b1dabf46e192a65be95ebfb4e839b5ab4533fef414921825e996b210dd53bd"),
|
||||
hexEncPubkey("cafa6934f82120456620573d7f801390ed5e16ed619613a37e409e44ab355ef755e83565a913b48a9466db786f8d4fbd590bfec474c2524d4a2608d4eafd6abd"),
|
||||
hexEncPubkey("9d16600d0dd310d77045769fed2cb427f32db88cd57d86e49390c2ba8a9698cfa856f775be2013237226e7bf47b248871cf865d23015937d1edeb20db5e3e760"),
|
||||
hexEncPubkey("17be6b6ba54199b1d80eff866d348ea11d8a4b341d63ad9a6681d3ef8a43853ac564d153eb2a8737f0afc9ab320f6f95c55aa11aaa13bbb1ff422fd16bdf8188"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type preminedTestnet struct {
|
||||
target NodeID
|
||||
targetSha common.Hash // sha3(target)
|
||||
dists [hashBits + 1][]NodeID
|
||||
target encPubkey
|
||||
targetSha enode.ID // sha3(target)
|
||||
dists [hashBits + 1][]encPubkey
|
||||
}
|
||||
|
||||
func (tn *preminedTestnet) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) {
|
||||
func (tn *preminedTestnet) findnode(toid enode.ID, toaddr *net.UDPAddr, target encPubkey) ([]*node, error) {
|
||||
// current log distance is encoded in port number
|
||||
// fmt.Println("findnode query at dist", toaddr.Port)
|
||||
if toaddr.Port == 0 {
|
||||
panic("query to node at distance 0")
|
||||
}
|
||||
next := uint16(toaddr.Port) - 1
|
||||
var result []*Node
|
||||
for i, id := range tn.dists[toaddr.Port] {
|
||||
result = append(result, NewNode(id, net.ParseIP("127.0.0.1"), next, uint16(i)))
|
||||
next := toaddr.Port - 1
|
||||
var result []*node
|
||||
for i, ekey := range tn.dists[toaddr.Port] {
|
||||
key, _ := decodePubkey(ekey)
|
||||
node := wrapNode(enode.NewV4(key, net.ParseIP("127.0.0.1"), i, next))
|
||||
result = append(result, node)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (*preminedTestnet) close() {}
|
||||
func (*preminedTestnet) waitping(from NodeID) error { return nil }
|
||||
func (*preminedTestnet) ping(toid NodeID, toaddr *net.UDPAddr) error { return nil }
|
||||
func (*preminedTestnet) close() {}
|
||||
func (*preminedTestnet) waitping(from enode.ID) error { return nil }
|
||||
func (*preminedTestnet) ping(toid enode.ID, toaddr *net.UDPAddr) error { return nil }
|
||||
|
||||
// mine generates a testnet struct literal with nodes at
|
||||
// various distances to the given target.
|
||||
func (tn *preminedTestnet) mine(target NodeID) {
|
||||
func (tn *preminedTestnet) mine(target encPubkey) {
|
||||
tn.target = target
|
||||
tn.targetSha = crypto.Keccak256Hash(tn.target[:])
|
||||
tn.targetSha = tn.target.id()
|
||||
found := 0
|
||||
for found < bucketSize*10 {
|
||||
k := newkey()
|
||||
id := PubkeyID(&k.PublicKey)
|
||||
sha := crypto.Keccak256Hash(id[:])
|
||||
ld := logdist(tn.targetSha, sha)
|
||||
key := encodePubkey(&k.PublicKey)
|
||||
ld := enode.LogDist(tn.targetSha, key.id())
|
||||
if len(tn.dists[ld]) < bucketSize {
|
||||
tn.dists[ld] = append(tn.dists[ld], id)
|
||||
tn.dists[ld] = append(tn.dists[ld], key)
|
||||
fmt.Println("found ID with ld", ld)
|
||||
found++
|
||||
}
|
||||
|
@ -594,14 +549,14 @@ func (tn *preminedTestnet) mine(target NodeID) {
|
|||
fmt.Println("&preminedTestnet{")
|
||||
fmt.Printf(" target: %#v,\n", tn.target)
|
||||
fmt.Printf(" targetSha: %#v,\n", tn.targetSha)
|
||||
fmt.Printf(" dists: [%d][]NodeID{\n", len(tn.dists))
|
||||
fmt.Printf(" dists: [%d][]encPubkey{\n", len(tn.dists))
|
||||
for ld, ns := range tn.dists {
|
||||
if len(ns) == 0 {
|
||||
continue
|
||||
}
|
||||
fmt.Printf(" %d: []NodeID{\n", ld)
|
||||
fmt.Printf(" %d: []encPubkey{\n", ld)
|
||||
for _, n := range ns {
|
||||
fmt.Printf(" MustHexID(\"%x\"),\n", n[:])
|
||||
fmt.Printf(" hexEncPubkey(\"%x\"),\n", n[:])
|
||||
}
|
||||
fmt.Println(" },")
|
||||
}
|
||||
|
@ -609,40 +564,6 @@ func (tn *preminedTestnet) mine(target NodeID) {
|
|||
fmt.Println("}")
|
||||
}
|
||||
|
||||
func hasDuplicates(slice []*Node) bool {
|
||||
seen := make(map[NodeID]bool)
|
||||
for i, e := range slice {
|
||||
if e == nil {
|
||||
panic(fmt.Sprintf("nil *Node at %d", i))
|
||||
}
|
||||
if seen[e.ID] {
|
||||
return true
|
||||
}
|
||||
seen[e.ID] = true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func sortedByDistanceTo(distbase common.Hash, slice []*Node) bool {
|
||||
var last common.Hash
|
||||
for i, e := range slice {
|
||||
if i > 0 && distcmp(distbase, e.sha, last) < 0 {
|
||||
return false
|
||||
}
|
||||
last = e.sha
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func contains(ns []*Node, id NodeID) bool {
|
||||
for _, n := range ns {
|
||||
if n.ID == id {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// gen wraps quick.Value so it's easier to use.
|
||||
// it generates a random value of the given value's type.
|
||||
func gen(typ interface{}, rand *rand.Rand) interface{} {
|
||||
|
@ -653,6 +574,13 @@ func gen(typ interface{}, rand *rand.Rand) interface{} {
|
|||
return v.Interface()
|
||||
}
|
||||
|
||||
func quickcfg() *quick.Config {
|
||||
return &quick.Config{
|
||||
MaxCount: 5000,
|
||||
Rand: rand.New(rand.NewSource(time.Now().Unix())),
|
||||
}
|
||||
}
|
||||
|
||||
func newkey() *ecdsa.PrivateKey {
|
||||
key, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package discover
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
||||
)
|
||||
|
||||
func newTestTable(t transport) (*Table, *enode.DB) {
|
||||
var r enr.Record
|
||||
r.Set(enr.IP{0, 0, 0, 0})
|
||||
n := enode.SignNull(&r, enode.ID{})
|
||||
db, _ := enode.OpenDB("")
|
||||
tab, _ := newTable(t, n, db, nil)
|
||||
return tab, db
|
||||
}
|
||||
|
||||
// nodeAtDistance creates a node for which enode.LogDist(base, n.id) == ld.
|
||||
func nodeAtDistance(base enode.ID, ld int, ip net.IP) *node {
|
||||
var r enr.Record
|
||||
r.Set(enr.IP(ip))
|
||||
return wrapNode(enode.SignNull(&r, idAtDistance(base, ld)))
|
||||
}
|
||||
|
||||
// idAtDistance returns a random hash such that enode.LogDist(a, b) == n
|
||||
func idAtDistance(a enode.ID, n int) (b enode.ID) {
|
||||
if n == 0 {
|
||||
return a
|
||||
}
|
||||
// flip bit at position n, fill the rest with random bits
|
||||
b = a
|
||||
pos := len(a) - n/8 - 1
|
||||
bit := byte(0x01) << (byte(n%8) - 1)
|
||||
if bit == 0 {
|
||||
pos++
|
||||
bit = 0x80
|
||||
}
|
||||
b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits
|
||||
for i := pos + 1; i < len(a); i++ {
|
||||
b[i] = byte(rand.Intn(255))
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func intIP(i int) net.IP {
|
||||
return net.IP{byte(i), 0, 2, byte(i)}
|
||||
}
|
||||
|
||||
// fillBucket inserts nodes into the given bucket until it is full.
|
||||
func fillBucket(tab *Table, n *node) (last *node) {
|
||||
ld := enode.LogDist(tab.self.ID(), n.ID())
|
||||
b := tab.bucket(n.ID())
|
||||
for len(b.entries) < bucketSize {
|
||||
b.entries = append(b.entries, nodeAtDistance(tab.self.ID(), ld, intIP(ld)))
|
||||
}
|
||||
return b.entries[bucketSize-1]
|
||||
}
|
||||
|
||||
type pingRecorder struct {
|
||||
mu sync.Mutex
|
||||
dead, pinged map[enode.ID]bool
|
||||
}
|
||||
|
||||
func newPingRecorder() *pingRecorder {
|
||||
return &pingRecorder{
|
||||
dead: make(map[enode.ID]bool),
|
||||
pinged: make(map[enode.ID]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *pingRecorder) findnode(toid enode.ID, toaddr *net.UDPAddr, target encPubkey) ([]*node, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *pingRecorder) waitping(from enode.ID) error {
|
||||
return nil // remote always pings
|
||||
}
|
||||
|
||||
func (t *pingRecorder) ping(toid enode.ID, toaddr *net.UDPAddr) error {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
t.pinged[toid] = true
|
||||
if t.dead[toid] {
|
||||
return errTimeout
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *pingRecorder) close() {}
|
||||
|
||||
func hasDuplicates(slice []*node) bool {
|
||||
seen := make(map[enode.ID]bool)
|
||||
for i, e := range slice {
|
||||
if e == nil {
|
||||
panic(fmt.Sprintf("nil *Node at %d", i))
|
||||
}
|
||||
if seen[e.ID()] {
|
||||
return true
|
||||
}
|
||||
seen[e.ID()] = true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func contains(ns []*node, id enode.ID) bool {
|
||||
for _, n := range ns {
|
||||
if n.ID() == id {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func sortedByDistanceTo(distbase enode.ID, slice []*node) bool {
|
||||
var last enode.ID
|
||||
for i, e := range slice {
|
||||
if i > 0 && enode.DistCmp(distbase, e.ID(), last) < 0 {
|
||||
return false
|
||||
}
|
||||
last = e.ID()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func hexEncPubkey(h string) (ret encPubkey) {
|
||||
b, err := hex.DecodeString(h)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if len(b) != len(ret) {
|
||||
panic("invalid length")
|
||||
}
|
||||
copy(ret[:], b)
|
||||
return ret
|
||||
}
|
||||
|
||||
func hexPubkey(h string) *ecdsa.PublicKey {
|
||||
k, err := decodePubkey(hexEncPubkey(h))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return k
|
||||
}
|
|
@ -27,6 +27,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
|
@ -46,8 +47,9 @@ var (
|
|||
|
||||
// Timeouts
|
||||
const (
|
||||
respTimeout = 500 * time.Millisecond
|
||||
expiration = 20 * time.Second
|
||||
respTimeout = 500 * time.Millisecond
|
||||
expiration = 20 * time.Second
|
||||
bondExpiration = 24 * time.Hour
|
||||
|
||||
ntpFailureThreshold = 32 // Continuous timeouts after which to check NTP
|
||||
ntpWarningCooldown = 10 * time.Minute // Minimum amount of time to pass before repeating NTP warning
|
||||
|
@ -87,7 +89,7 @@ type (
|
|||
|
||||
// findnode is a query for nodes close to the given target.
|
||||
findnode struct {
|
||||
Target NodeID // doesn't need to be an actual public key
|
||||
Target encPubkey
|
||||
Expiration uint64
|
||||
// Ignore additional fields (for forward compatibility).
|
||||
Rest []rlp.RawValue `rlp:"tail"`
|
||||
|
@ -105,7 +107,7 @@ type (
|
|||
IP net.IP // len 4 for IPv4 or 16 for IPv6
|
||||
UDP uint16 // for discovery protocol
|
||||
TCP uint16 // for RLPx protocol
|
||||
ID NodeID
|
||||
ID encPubkey
|
||||
}
|
||||
|
||||
rpcEndpoint struct {
|
||||
|
@ -123,7 +125,7 @@ func makeEndpoint(addr *net.UDPAddr, tcpPort uint16) rpcEndpoint {
|
|||
return rpcEndpoint{IP: ip, UDP: uint16(addr.Port), TCP: tcpPort}
|
||||
}
|
||||
|
||||
func (t *udp) nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*Node, error) {
|
||||
func (t *udp) nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*node, error) {
|
||||
if rn.UDP <= 1024 {
|
||||
return nil, errors.New("low port")
|
||||
}
|
||||
|
@ -133,17 +135,26 @@ func (t *udp) nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*Node, error) {
|
|||
if t.netrestrict != nil && !t.netrestrict.Contains(rn.IP) {
|
||||
return nil, errors.New("not contained in netrestrict whitelist")
|
||||
}
|
||||
n := NewNode(rn.ID, rn.IP, rn.UDP, rn.TCP)
|
||||
err := n.validateComplete()
|
||||
key, err := decodePubkey(rn.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n := wrapNode(enode.NewV4(key, rn.IP, int(rn.TCP), int(rn.UDP)))
|
||||
err = n.ValidateComplete()
|
||||
return n, err
|
||||
}
|
||||
|
||||
func nodeToRPC(n *Node) rpcNode {
|
||||
return rpcNode{ID: n.ID, IP: n.IP, UDP: n.UDP, TCP: n.TCP}
|
||||
func nodeToRPC(n *node) rpcNode {
|
||||
var key ecdsa.PublicKey
|
||||
var ekey encPubkey
|
||||
if err := n.Load((*enode.Secp256k1)(&key)); err == nil {
|
||||
ekey = encodePubkey(&key)
|
||||
}
|
||||
return rpcNode{ID: ekey, IP: n.IP(), UDP: uint16(n.UDP()), TCP: uint16(n.TCP())}
|
||||
}
|
||||
|
||||
type packet interface {
|
||||
handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error
|
||||
handle(t *udp, from *net.UDPAddr, fromKey encPubkey, mac []byte) error
|
||||
name() string
|
||||
}
|
||||
|
||||
|
@ -181,7 +192,7 @@ type udp struct {
|
|||
// to all the callback functions for that node.
|
||||
type pending struct {
|
||||
// these fields must match in the reply.
|
||||
from NodeID
|
||||
from enode.ID
|
||||
ptype byte
|
||||
|
||||
// time when the request must complete
|
||||
|
@ -199,7 +210,7 @@ type pending struct {
|
|||
}
|
||||
|
||||
type reply struct {
|
||||
from NodeID
|
||||
from enode.ID
|
||||
ptype byte
|
||||
data interface{}
|
||||
// loop indicates whether there was
|
||||
|
@ -222,7 +233,7 @@ type Config struct {
|
|||
AnnounceAddr *net.UDPAddr // local address announced in the DHT
|
||||
NodeDBPath string // if set, the node database is stored at this filesystem location
|
||||
NetRestrict *netutil.Netlist // network whitelist
|
||||
Bootnodes []*Node // list of bootstrap nodes
|
||||
Bootnodes []*enode.Node // list of bootstrap nodes
|
||||
Unhandled chan<- ReadPacket // unhandled packets are sent on this channel
|
||||
}
|
||||
|
||||
|
@ -237,6 +248,16 @@ func ListenUDP(c conn, cfg Config) (*Table, error) {
|
|||
}
|
||||
|
||||
func newUDP(c conn, cfg Config) (*Table, *udp, error) {
|
||||
realaddr := c.LocalAddr().(*net.UDPAddr)
|
||||
if cfg.AnnounceAddr != nil {
|
||||
realaddr = cfg.AnnounceAddr
|
||||
}
|
||||
self := enode.NewV4(&cfg.PrivateKey.PublicKey, realaddr.IP, realaddr.Port, realaddr.Port)
|
||||
db, err := enode.OpenDB(cfg.NodeDBPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
udp := &udp{
|
||||
conn: c,
|
||||
priv: cfg.PrivateKey,
|
||||
|
@ -245,13 +266,9 @@ func newUDP(c conn, cfg Config) (*Table, *udp, error) {
|
|||
gotreply: make(chan reply),
|
||||
addpending: make(chan *pending),
|
||||
}
|
||||
realaddr := c.LocalAddr().(*net.UDPAddr)
|
||||
if cfg.AnnounceAddr != nil {
|
||||
realaddr = cfg.AnnounceAddr
|
||||
}
|
||||
// TODO: separate TCP port
|
||||
udp.ourEndpoint = makeEndpoint(realaddr, uint16(realaddr.Port))
|
||||
tab, err := newTable(udp, PubkeyID(&cfg.PrivateKey.PublicKey), realaddr, cfg.NodeDBPath, cfg.Bootnodes)
|
||||
tab, err := newTable(udp, self, db, cfg.Bootnodes)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -265,17 +282,18 @@ func newUDP(c conn, cfg Config) (*Table, *udp, error) {
|
|||
func (t *udp) close() {
|
||||
close(t.closing)
|
||||
t.conn.Close()
|
||||
t.db.Close()
|
||||
// TODO: wait for the loops to end.
|
||||
}
|
||||
|
||||
// ping sends a ping message to the given node and waits for a reply.
|
||||
func (t *udp) ping(toid NodeID, toaddr *net.UDPAddr) error {
|
||||
func (t *udp) ping(toid enode.ID, toaddr *net.UDPAddr) error {
|
||||
return <-t.sendPing(toid, toaddr, nil)
|
||||
}
|
||||
|
||||
// sendPing sends a ping message to the given node and invokes the callback
|
||||
// when the reply arrives.
|
||||
func (t *udp) sendPing(toid NodeID, toaddr *net.UDPAddr, callback func()) <-chan error {
|
||||
func (t *udp) sendPing(toid enode.ID, toaddr *net.UDPAddr, callback func()) <-chan error {
|
||||
req := &ping{
|
||||
Version: 4,
|
||||
From: t.ourEndpoint,
|
||||
|
@ -299,21 +317,21 @@ func (t *udp) sendPing(toid NodeID, toaddr *net.UDPAddr, callback func()) <-chan
|
|||
return errc
|
||||
}
|
||||
|
||||
func (t *udp) waitping(from NodeID) error {
|
||||
func (t *udp) waitping(from enode.ID) error {
|
||||
return <-t.pending(from, pingPacket, func(interface{}) bool { return true })
|
||||
}
|
||||
|
||||
// findnode sends a findnode request to the given node and waits until
|
||||
// the node has sent up to k neighbors.
|
||||
func (t *udp) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) {
|
||||
func (t *udp) findnode(toid enode.ID, toaddr *net.UDPAddr, target encPubkey) ([]*node, error) {
|
||||
// If we haven't seen a ping from the destination node for a while, it won't remember
|
||||
// our endpoint proof and reject findnode. Solicit a ping first.
|
||||
if time.Since(t.db.lastPingReceived(toid)) > nodeDBNodeExpiration {
|
||||
if time.Since(t.db.LastPingReceived(toid)) > bondExpiration {
|
||||
t.ping(toid, toaddr)
|
||||
t.waitping(toid)
|
||||
}
|
||||
|
||||
nodes := make([]*Node, 0, bucketSize)
|
||||
nodes := make([]*node, 0, bucketSize)
|
||||
nreceived := 0
|
||||
errc := t.pending(toid, neighborsPacket, func(r interface{}) bool {
|
||||
reply := r.(*neighbors)
|
||||
|
@ -337,7 +355,7 @@ func (t *udp) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node
|
|||
|
||||
// pending adds a reply callback to the pending reply queue.
|
||||
// see the documentation of type pending for a detailed explanation.
|
||||
func (t *udp) pending(id NodeID, ptype byte, callback func(interface{}) bool) <-chan error {
|
||||
func (t *udp) pending(id enode.ID, ptype byte, callback func(interface{}) bool) <-chan error {
|
||||
ch := make(chan error, 1)
|
||||
p := &pending{from: id, ptype: ptype, callback: callback, errc: ch}
|
||||
select {
|
||||
|
@ -349,7 +367,7 @@ func (t *udp) pending(id NodeID, ptype byte, callback func(interface{}) bool) <-
|
|||
return ch
|
||||
}
|
||||
|
||||
func (t *udp) handleReply(from NodeID, ptype byte, req packet) bool {
|
||||
func (t *udp) handleReply(from enode.ID, ptype byte, req packet) bool {
|
||||
matched := make(chan bool, 1)
|
||||
select {
|
||||
case t.gotreply <- reply{from, ptype, req, matched}:
|
||||
|
@ -563,19 +581,20 @@ func (t *udp) handlePacket(from *net.UDPAddr, buf []byte) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func decodePacket(buf []byte) (packet, NodeID, []byte, error) {
|
||||
func decodePacket(buf []byte) (packet, encPubkey, []byte, error) {
|
||||
if len(buf) < headSize+1 {
|
||||
return nil, NodeID{}, nil, errPacketTooSmall
|
||||
return nil, encPubkey{}, nil, errPacketTooSmall
|
||||
}
|
||||
hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:]
|
||||
shouldhash := crypto.Keccak256(buf[macSize:])
|
||||
if !bytes.Equal(hash, shouldhash) {
|
||||
return nil, NodeID{}, nil, errBadHash
|
||||
return nil, encPubkey{}, nil, errBadHash
|
||||
}
|
||||
fromID, err := recoverNodeID(crypto.Keccak256(buf[headSize:]), sig)
|
||||
fromKey, err := recoverNodeKey(crypto.Keccak256(buf[headSize:]), sig)
|
||||
if err != nil {
|
||||
return nil, NodeID{}, hash, err
|
||||
return nil, fromKey, hash, err
|
||||
}
|
||||
|
||||
var req packet
|
||||
switch ptype := sigdata[0]; ptype {
|
||||
case pingPacket:
|
||||
|
@ -587,56 +606,59 @@ func decodePacket(buf []byte) (packet, NodeID, []byte, error) {
|
|||
case neighborsPacket:
|
||||
req = new(neighbors)
|
||||
default:
|
||||
return nil, fromID, hash, fmt.Errorf("unknown type: %d", ptype)
|
||||
return nil, fromKey, hash, fmt.Errorf("unknown type: %d", ptype)
|
||||
}
|
||||
s := rlp.NewStream(bytes.NewReader(sigdata[1:]), 0)
|
||||
err = s.Decode(req)
|
||||
return req, fromID, hash, err
|
||||
return req, fromKey, hash, err
|
||||
}
|
||||
|
||||
func (req *ping) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error {
|
||||
func (req *ping) handle(t *udp, from *net.UDPAddr, fromKey encPubkey, mac []byte) error {
|
||||
if expired(req.Expiration) {
|
||||
return errExpired
|
||||
}
|
||||
key, err := decodePubkey(fromKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid public key: %v", err)
|
||||
}
|
||||
t.send(from, pongPacket, &pong{
|
||||
To: makeEndpoint(from, req.From.TCP),
|
||||
ReplyTok: mac,
|
||||
Expiration: uint64(time.Now().Add(expiration).Unix()),
|
||||
})
|
||||
t.handleReply(fromID, pingPacket, req)
|
||||
|
||||
// Add the node to the table. Before doing so, ensure that we have a recent enough pong
|
||||
// recorded in the database so their findnode requests will be accepted later.
|
||||
n := NewNode(fromID, from.IP, uint16(from.Port), req.From.TCP)
|
||||
if time.Since(t.db.lastPongReceived(fromID)) > nodeDBNodeExpiration {
|
||||
t.sendPing(fromID, from, func() { t.addThroughPing(n) })
|
||||
n := wrapNode(enode.NewV4(key, from.IP, int(req.From.TCP), from.Port))
|
||||
t.handleReply(n.ID(), pingPacket, req)
|
||||
if time.Since(t.db.LastPongReceived(n.ID())) > bondExpiration {
|
||||
t.sendPing(n.ID(), from, func() { t.addThroughPing(n) })
|
||||
} else {
|
||||
t.addThroughPing(n)
|
||||
}
|
||||
t.db.updateLastPingReceived(fromID, time.Now())
|
||||
t.db.UpdateLastPingReceived(n.ID(), time.Now())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (req *ping) name() string { return "PING/v4" }
|
||||
|
||||
func (req *pong) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error {
|
||||
func (req *pong) handle(t *udp, from *net.UDPAddr, fromKey encPubkey, mac []byte) error {
|
||||
if expired(req.Expiration) {
|
||||
return errExpired
|
||||
}
|
||||
fromID := fromKey.id()
|
||||
if !t.handleReply(fromID, pongPacket, req) {
|
||||
return errUnsolicitedReply
|
||||
}
|
||||
t.db.updateLastPongReceived(fromID, time.Now())
|
||||
t.db.UpdateLastPongReceived(fromID, time.Now())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (req *pong) name() string { return "PONG/v4" }
|
||||
|
||||
func (req *findnode) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error {
|
||||
func (req *findnode) handle(t *udp, from *net.UDPAddr, fromKey encPubkey, mac []byte) error {
|
||||
if expired(req.Expiration) {
|
||||
return errExpired
|
||||
}
|
||||
if !t.db.hasBond(fromID) {
|
||||
fromID := fromKey.id()
|
||||
if time.Since(t.db.LastPongReceived(fromID)) > bondExpiration {
|
||||
// No endpoint proof pong exists, we don't process the packet. This prevents an
|
||||
// attack vector where the discovery protocol could be used to amplify traffic in a
|
||||
// DDOS attack. A malicious actor would send a findnode request with the IP address
|
||||
|
@ -645,7 +667,7 @@ func (req *findnode) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte
|
|||
// findnode) to the victim.
|
||||
return errUnknownNode
|
||||
}
|
||||
target := crypto.Keccak256Hash(req.Target[:])
|
||||
target := enode.ID(crypto.Keccak256Hash(req.Target[:]))
|
||||
t.mutex.Lock()
|
||||
closest := t.closest(target, bucketSize).entries
|
||||
t.mutex.Unlock()
|
||||
|
@ -655,7 +677,7 @@ func (req *findnode) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte
|
|||
// Send neighbors in chunks with at most maxNeighbors per packet
|
||||
// to stay below the 1280 byte limit.
|
||||
for _, n := range closest {
|
||||
if netutil.CheckRelayIP(from.IP, n.IP) == nil {
|
||||
if netutil.CheckRelayIP(from.IP, n.IP()) == nil {
|
||||
p.Nodes = append(p.Nodes, nodeToRPC(n))
|
||||
}
|
||||
if len(p.Nodes) == maxNeighbors {
|
||||
|
@ -672,11 +694,11 @@ func (req *findnode) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte
|
|||
|
||||
func (req *findnode) name() string { return "FINDNODE/v4" }
|
||||
|
||||
func (req *neighbors) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error {
|
||||
func (req *neighbors) handle(t *udp, from *net.UDPAddr, fromKey encPubkey, mac []byte) error {
|
||||
if expired(req.Expiration) {
|
||||
return errExpired
|
||||
}
|
||||
if !t.handleReply(fromID, neighborsPacket, req) {
|
||||
if !t.handleReply(fromKey.id(), neighborsPacket, req) {
|
||||
return errUnsolicitedReply
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -36,6 +36,7 @@ import (
|
|||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
|
@ -46,7 +47,7 @@ func init() {
|
|||
// shared test variables
|
||||
var (
|
||||
futureExp = uint64(time.Now().Add(10 * time.Hour).Unix())
|
||||
testTarget = NodeID{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}
|
||||
testTarget = encPubkey{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}
|
||||
testRemote = rpcEndpoint{IP: net.ParseIP("1.1.1.1").To4(), UDP: 1, TCP: 2}
|
||||
testLocalAnnounced = rpcEndpoint{IP: net.ParseIP("2.2.2.2").To4(), UDP: 3, TCP: 4}
|
||||
testLocal = rpcEndpoint{IP: net.ParseIP("3.3.3.3").To4(), UDP: 5, TCP: 6}
|
||||
|
@ -136,7 +137,7 @@ func TestUDP_pingTimeout(t *testing.T) {
|
|||
defer test.table.Close()
|
||||
|
||||
toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222}
|
||||
toid := NodeID{1, 2, 3, 4}
|
||||
toid := enode.ID{1, 2, 3, 4}
|
||||
if err := test.udp.ping(toid, toaddr); err != errTimeout {
|
||||
t.Error("expected timeout error, got", err)
|
||||
}
|
||||
|
@ -220,8 +221,8 @@ func TestUDP_findnodeTimeout(t *testing.T) {
|
|||
defer test.table.Close()
|
||||
|
||||
toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222}
|
||||
toid := NodeID{1, 2, 3, 4}
|
||||
target := NodeID{4, 5, 6, 7}
|
||||
toid := enode.ID{1, 2, 3, 4}
|
||||
target := encPubkey{4, 5, 6, 7}
|
||||
result, err := test.udp.findnode(toid, toaddr, target)
|
||||
if err != errTimeout {
|
||||
t.Error("expected timeout error, got", err)
|
||||
|
@ -238,28 +239,30 @@ func TestUDP_findnode(t *testing.T) {
|
|||
// put a few nodes into the table. their exact
|
||||
// distribution shouldn't matter much, although we need to
|
||||
// take care not to overflow any bucket.
|
||||
targetHash := crypto.Keccak256Hash(testTarget[:])
|
||||
nodes := &nodesByDistance{target: targetHash}
|
||||
nodes := &nodesByDistance{target: testTarget.id()}
|
||||
for i := 0; i < bucketSize; i++ {
|
||||
nodes.push(nodeAtDistance(test.table.self.sha, i+2), bucketSize)
|
||||
key := newkey()
|
||||
n := wrapNode(enode.NewV4(&key.PublicKey, net.IP{10, 13, 0, 1}, 0, i))
|
||||
nodes.push(n, bucketSize)
|
||||
}
|
||||
test.table.stuff(nodes.entries)
|
||||
|
||||
// ensure there's a bond with the test node,
|
||||
// findnode won't be accepted otherwise.
|
||||
test.table.db.updateLastPongReceived(PubkeyID(&test.remotekey.PublicKey), time.Now())
|
||||
remoteID := encodePubkey(&test.remotekey.PublicKey).id()
|
||||
test.table.db.UpdateLastPongReceived(remoteID, time.Now())
|
||||
|
||||
// check that closest neighbors are returned.
|
||||
test.packetIn(nil, findnodePacket, &findnode{Target: testTarget, Expiration: futureExp})
|
||||
expected := test.table.closest(targetHash, bucketSize)
|
||||
expected := test.table.closest(testTarget.id(), bucketSize)
|
||||
|
||||
waitNeighbors := func(want []*Node) {
|
||||
waitNeighbors := func(want []*node) {
|
||||
test.waitPacketOut(func(p *neighbors) {
|
||||
if len(p.Nodes) != len(want) {
|
||||
t.Errorf("wrong number of results: got %d, want %d", len(p.Nodes), bucketSize)
|
||||
}
|
||||
for i := range p.Nodes {
|
||||
if p.Nodes[i].ID != want[i].ID {
|
||||
if p.Nodes[i].ID.id() != want[i].ID() {
|
||||
t.Errorf("result mismatch at %d:\n got: %v\n want: %v", i, p.Nodes[i], expected.entries[i])
|
||||
}
|
||||
}
|
||||
|
@ -273,12 +276,13 @@ func TestUDP_findnodeMultiReply(t *testing.T) {
|
|||
test := newUDPTest(t)
|
||||
defer test.table.Close()
|
||||
|
||||
rid := PubkeyID(&test.remotekey.PublicKey)
|
||||
test.table.db.updateLastPingReceived(rid, time.Now())
|
||||
rid := enode.PubkeyToIDV4(&test.remotekey.PublicKey)
|
||||
test.table.db.UpdateLastPingReceived(rid, time.Now())
|
||||
|
||||
// queue a pending findnode request
|
||||
resultc, errc := make(chan []*Node), make(chan error)
|
||||
resultc, errc := make(chan []*node), make(chan error)
|
||||
go func() {
|
||||
rid := encodePubkey(&test.remotekey.PublicKey).id()
|
||||
ns, err := test.udp.findnode(rid, test.remoteaddr, testTarget)
|
||||
if err != nil && len(ns) == 0 {
|
||||
errc <- err
|
||||
|
@ -296,11 +300,11 @@ func TestUDP_findnodeMultiReply(t *testing.T) {
|
|||
})
|
||||
|
||||
// send the reply as two packets.
|
||||
list := []*Node{
|
||||
MustParseNode("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304"),
|
||||
MustParseNode("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303"),
|
||||
MustParseNode("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17"),
|
||||
MustParseNode("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303"),
|
||||
list := []*node{
|
||||
wrapNode(enode.MustParseV4("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304")),
|
||||
wrapNode(enode.MustParseV4("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303")),
|
||||
wrapNode(enode.MustParseV4("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17")),
|
||||
wrapNode(enode.MustParseV4("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303")),
|
||||
}
|
||||
rpclist := make([]rpcNode, len(list))
|
||||
for i := range list {
|
||||
|
@ -325,8 +329,8 @@ func TestUDP_findnodeMultiReply(t *testing.T) {
|
|||
|
||||
func TestUDP_successfulPing(t *testing.T) {
|
||||
test := newUDPTest(t)
|
||||
added := make(chan *Node, 1)
|
||||
test.table.nodeAddedHook = func(n *Node) { added <- n }
|
||||
added := make(chan *node, 1)
|
||||
test.table.nodeAddedHook = func(n *node) { added <- n }
|
||||
defer test.table.Close()
|
||||
|
||||
// The remote side sends a ping packet to initiate the exchange.
|
||||
|
@ -370,18 +374,18 @@ func TestUDP_successfulPing(t *testing.T) {
|
|||
// pong packet.
|
||||
select {
|
||||
case n := <-added:
|
||||
rid := PubkeyID(&test.remotekey.PublicKey)
|
||||
if n.ID != rid {
|
||||
t.Errorf("node has wrong ID: got %v, want %v", n.ID, rid)
|
||||
rid := encodePubkey(&test.remotekey.PublicKey).id()
|
||||
if n.ID() != rid {
|
||||
t.Errorf("node has wrong ID: got %v, want %v", n.ID(), rid)
|
||||
}
|
||||
if !n.IP.Equal(test.remoteaddr.IP) {
|
||||
t.Errorf("node has wrong IP: got %v, want: %v", n.IP, test.remoteaddr.IP)
|
||||
if !n.IP().Equal(test.remoteaddr.IP) {
|
||||
t.Errorf("node has wrong IP: got %v, want: %v", n.IP(), test.remoteaddr.IP)
|
||||
}
|
||||
if int(n.UDP) != test.remoteaddr.Port {
|
||||
t.Errorf("node has wrong UDP port: got %v, want: %v", n.UDP, test.remoteaddr.Port)
|
||||
if int(n.UDP()) != test.remoteaddr.Port {
|
||||
t.Errorf("node has wrong UDP port: got %v, want: %v", n.UDP(), test.remoteaddr.Port)
|
||||
}
|
||||
if n.TCP != testRemote.TCP {
|
||||
t.Errorf("node has wrong TCP port: got %v, want: %v", n.TCP, testRemote.TCP)
|
||||
if n.TCP() != int(testRemote.TCP) {
|
||||
t.Errorf("node has wrong TCP port: got %v, want: %v", n.TCP(), testRemote.TCP)
|
||||
}
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Errorf("node was not added within 2 seconds")
|
||||
|
@ -434,7 +438,7 @@ var testPackets = []struct {
|
|||
{
|
||||
input: "c7c44041b9f7c7e41934417ebac9a8e1a4c6298f74553f2fcfdcae6ed6fe53163eb3d2b52e39fe91831b8a927bf4fc222c3902202027e5e9eb812195f95d20061ef5cd31d502e47ecb61183f74a504fe04c51e73df81f25c4d506b26db4517490103f84eb840ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f8443b9a35582999983999999280dc62cc8255c73471e0a61da0c89acdc0e035e260add7fc0c04ad9ebf3919644c91cb247affc82b69bd2ca235c71eab8e49737c937a2c396",
|
||||
wantPacket: &findnode{
|
||||
Target: MustHexID("ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f"),
|
||||
Target: hexEncPubkey("ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f"),
|
||||
Expiration: 1136239445,
|
||||
Rest: []rlp.RawValue{{0x82, 0x99, 0x99}, {0x83, 0x99, 0x99, 0x99}},
|
||||
},
|
||||
|
@ -444,25 +448,25 @@ var testPackets = []struct {
|
|||
wantPacket: &neighbors{
|
||||
Nodes: []rpcNode{
|
||||
{
|
||||
ID: MustHexID("3155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32"),
|
||||
ID: hexEncPubkey("3155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32"),
|
||||
IP: net.ParseIP("99.33.22.55").To4(),
|
||||
UDP: 4444,
|
||||
TCP: 4445,
|
||||
},
|
||||
{
|
||||
ID: MustHexID("312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069db"),
|
||||
ID: hexEncPubkey("312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069db"),
|
||||
IP: net.ParseIP("1.2.3.4").To4(),
|
||||
UDP: 1,
|
||||
TCP: 1,
|
||||
},
|
||||
{
|
||||
ID: MustHexID("38643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aac"),
|
||||
ID: hexEncPubkey("38643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aac"),
|
||||
IP: net.ParseIP("2001:db8:3c4d:15::abcd:ef12"),
|
||||
UDP: 3333,
|
||||
TCP: 3333,
|
||||
},
|
||||
{
|
||||
ID: MustHexID("8dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73"),
|
||||
ID: hexEncPubkey("8dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73"),
|
||||
IP: net.ParseIP("2001:db8:85a3:8d3:1319:8a2e:370:7348"),
|
||||
UDP: 999,
|
||||
TCP: 1000,
|
||||
|
@ -476,14 +480,14 @@ var testPackets = []struct {
|
|||
|
||||
func TestForwardCompatibility(t *testing.T) {
|
||||
testkey, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
wantNodeID := PubkeyID(&testkey.PublicKey)
|
||||
wantNodeKey := encodePubkey(&testkey.PublicKey)
|
||||
|
||||
for _, test := range testPackets {
|
||||
input, err := hex.DecodeString(test.input)
|
||||
if err != nil {
|
||||
t.Fatalf("invalid hex: %s", test.input)
|
||||
}
|
||||
packet, nodeid, _, err := decodePacket(input)
|
||||
packet, nodekey, _, err := decodePacket(input)
|
||||
if err != nil {
|
||||
t.Errorf("did not accept packet %s\n%v", test.input, err)
|
||||
continue
|
||||
|
@ -491,8 +495,8 @@ func TestForwardCompatibility(t *testing.T) {
|
|||
if !reflect.DeepEqual(packet, test.wantPacket) {
|
||||
t.Errorf("got %s\nwant %s", spew.Sdump(packet), spew.Sdump(test.wantPacket))
|
||||
}
|
||||
if nodeid != wantNodeID {
|
||||
t.Errorf("got id %v\nwant id %v", nodeid, wantNodeID)
|
||||
if nodekey != wantNodeKey {
|
||||
t.Errorf("got id %v\nwant id %v", nodekey, wantNodeKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,57 +14,38 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package enr
|
||||
package enode
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"sync"
|
||||
"io"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// Registry of known identity schemes.
|
||||
var schemes sync.Map
|
||||
|
||||
// An IdentityScheme is capable of verifying record signatures and
|
||||
// deriving node addresses.
|
||||
type IdentityScheme interface {
|
||||
Verify(r *Record, sig []byte) error
|
||||
NodeAddr(r *Record) []byte
|
||||
// List of known secure identity schemes.
|
||||
var ValidSchemes = enr.SchemeMap{
|
||||
"v4": V4ID{},
|
||||
}
|
||||
|
||||
// RegisterIdentityScheme adds an identity scheme to the global registry.
|
||||
func RegisterIdentityScheme(name string, scheme IdentityScheme) {
|
||||
if _, loaded := schemes.LoadOrStore(name, scheme); loaded {
|
||||
panic("identity scheme " + name + " already registered")
|
||||
}
|
||||
}
|
||||
|
||||
// FindIdentityScheme resolves name to an identity scheme in the global registry.
|
||||
func FindIdentityScheme(name string) IdentityScheme {
|
||||
s, ok := schemes.Load(name)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return s.(IdentityScheme)
|
||||
var ValidSchemesForTesting = enr.SchemeMap{
|
||||
"v4": V4ID{},
|
||||
"null": NullID{},
|
||||
}
|
||||
|
||||
// v4ID is the "v4" identity scheme.
|
||||
type v4ID struct{}
|
||||
|
||||
func init() {
|
||||
RegisterIdentityScheme("v4", v4ID{})
|
||||
}
|
||||
type V4ID struct{}
|
||||
|
||||
// SignV4 signs a record using the v4 scheme.
|
||||
func SignV4(r *Record, privkey *ecdsa.PrivateKey) error {
|
||||
func SignV4(r *enr.Record, privkey *ecdsa.PrivateKey) error {
|
||||
// Copy r to avoid modifying it if signing fails.
|
||||
cpy := *r
|
||||
cpy.Set(ID("v4"))
|
||||
cpy.Set(enr.ID("v4"))
|
||||
cpy.Set(Secp256k1(privkey.PublicKey))
|
||||
|
||||
h := sha3.NewKeccak256()
|
||||
|
@ -74,18 +55,13 @@ func SignV4(r *Record, privkey *ecdsa.PrivateKey) error {
|
|||
return err
|
||||
}
|
||||
sig = sig[:len(sig)-1] // remove v
|
||||
if err = cpy.SetSig("v4", sig); err == nil {
|
||||
if err = cpy.SetSig(V4ID{}, sig); err == nil {
|
||||
*r = cpy
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// s256raw is an unparsed secp256k1 public key entry.
|
||||
type s256raw []byte
|
||||
|
||||
func (s256raw) ENRKey() string { return "secp256k1" }
|
||||
|
||||
func (v4ID) Verify(r *Record, sig []byte) error {
|
||||
func (V4ID) Verify(r *enr.Record, sig []byte) error {
|
||||
var entry s256raw
|
||||
if err := r.Load(&entry); err != nil {
|
||||
return err
|
||||
|
@ -96,12 +72,12 @@ func (v4ID) Verify(r *Record, sig []byte) error {
|
|||
h := sha3.NewKeccak256()
|
||||
rlp.Encode(h, r.AppendElements(nil))
|
||||
if !crypto.VerifySignature(entry, h.Sum(nil), sig) {
|
||||
return errInvalidSig
|
||||
return enr.ErrInvalidSig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v4ID) NodeAddr(r *Record) []byte {
|
||||
func (V4ID) NodeAddr(r *enr.Record) []byte {
|
||||
var pubkey Secp256k1
|
||||
err := r.Load(&pubkey)
|
||||
if err != nil {
|
||||
|
@ -112,3 +88,73 @@ func (v4ID) NodeAddr(r *Record) []byte {
|
|||
math.ReadBits(pubkey.Y, buf[32:])
|
||||
return crypto.Keccak256(buf)
|
||||
}
|
||||
|
||||
// Secp256k1 is the "secp256k1" key, which holds a public key.
|
||||
type Secp256k1 ecdsa.PublicKey
|
||||
|
||||
func (v Secp256k1) ENRKey() string { return "secp256k1" }
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
func (v Secp256k1) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, crypto.CompressPubkey((*ecdsa.PublicKey)(&v)))
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder.
|
||||
func (v *Secp256k1) DecodeRLP(s *rlp.Stream) error {
|
||||
buf, err := s.Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pk, err := crypto.DecompressPubkey(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = (Secp256k1)(*pk)
|
||||
return nil
|
||||
}
|
||||
|
||||
// s256raw is an unparsed secp256k1 public key entry.
|
||||
type s256raw []byte
|
||||
|
||||
func (s256raw) ENRKey() string { return "secp256k1" }
|
||||
|
||||
// v4CompatID is a weaker and insecure version of the "v4" scheme which only checks for the
|
||||
// presence of a secp256k1 public key, but doesn't verify the signature.
|
||||
type v4CompatID struct {
|
||||
V4ID
|
||||
}
|
||||
|
||||
func (v4CompatID) Verify(r *enr.Record, sig []byte) error {
|
||||
var pubkey Secp256k1
|
||||
return r.Load(&pubkey)
|
||||
}
|
||||
|
||||
func signV4Compat(r *enr.Record, pubkey *ecdsa.PublicKey) {
|
||||
r.Set((*Secp256k1)(pubkey))
|
||||
if err := r.SetSig(v4CompatID{}, []byte{}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// NullID is the "null" ENR identity scheme. This scheme stores the node
|
||||
// ID in the record without any signature.
|
||||
type NullID struct{}
|
||||
|
||||
func (NullID) Verify(r *enr.Record, sig []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (NullID) NodeAddr(r *enr.Record) []byte {
|
||||
var id ID
|
||||
r.Load(enr.WithEntry("nulladdr", &id))
|
||||
return id[:]
|
||||
}
|
||||
|
||||
func SignNull(r *enr.Record, id ID) *Node {
|
||||
r.Set(enr.ID("null"))
|
||||
r.Set(enr.WithEntry("nulladdr", id))
|
||||
if err := r.SetSig(NullID{}, []byte{}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &Node{r: *r, id: id}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package enode
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
privkey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
pubkey = &privkey.PublicKey
|
||||
)
|
||||
|
||||
func TestEmptyNodeID(t *testing.T) {
|
||||
var r enr.Record
|
||||
if addr := ValidSchemes.NodeAddr(&r); addr != nil {
|
||||
t.Errorf("wrong address on empty record: got %v, want %v", addr, nil)
|
||||
}
|
||||
|
||||
require.NoError(t, SignV4(&r, privkey))
|
||||
expected := "a448f24c6d18e575453db13171562b71999873db5b286df957af199ec94617f7"
|
||||
assert.Equal(t, expected, hex.EncodeToString(ValidSchemes.NodeAddr(&r)))
|
||||
}
|
||||
|
||||
// Checks that failure to sign leaves the record unmodified.
|
||||
func TestSignError(t *testing.T) {
|
||||
invalidKey := &ecdsa.PrivateKey{D: new(big.Int), PublicKey: *pubkey}
|
||||
|
||||
var r enr.Record
|
||||
emptyEnc, _ := rlp.EncodeToBytes(&r)
|
||||
if err := SignV4(&r, invalidKey); err == nil {
|
||||
t.Fatal("expected error from SignV4")
|
||||
}
|
||||
newEnc, _ := rlp.EncodeToBytes(&r)
|
||||
if !bytes.Equal(newEnc, emptyEnc) {
|
||||
t.Fatal("record modified even though signing failed")
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetSetSecp256k1 tests encoding/decoding and setting/getting of the Secp256k1 key.
|
||||
func TestGetSetSecp256k1(t *testing.T) {
|
||||
var r enr.Record
|
||||
if err := SignV4(&r, privkey); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var pk Secp256k1
|
||||
require.NoError(t, r.Load(&pk))
|
||||
assert.EqualValues(t, pubkey, &pk)
|
||||
}
|
|
@ -0,0 +1,248 @@
|
|||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package enode
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/bits"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
||||
)
|
||||
|
||||
// Node represents a host on the network.
|
||||
type Node struct {
|
||||
r enr.Record
|
||||
id ID
|
||||
}
|
||||
|
||||
// New wraps a node record. The record must be valid according to the given
|
||||
// identity scheme.
|
||||
func New(validSchemes enr.IdentityScheme, r *enr.Record) (*Node, error) {
|
||||
if err := r.VerifySignature(validSchemes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
node := &Node{r: *r}
|
||||
if n := copy(node.id[:], validSchemes.NodeAddr(&node.r)); n != len(ID{}) {
|
||||
return nil, fmt.Errorf("invalid node ID length %d, need %d", n, len(ID{}))
|
||||
}
|
||||
return node, nil
|
||||
}
|
||||
|
||||
// ID returns the node identifier.
|
||||
func (n *Node) ID() ID {
|
||||
return n.id
|
||||
}
|
||||
|
||||
// Seq returns the sequence number of the underlying record.
|
||||
func (n *Node) Seq() uint64 {
|
||||
return n.r.Seq()
|
||||
}
|
||||
|
||||
// Incomplete returns true for nodes with no IP address.
|
||||
func (n *Node) Incomplete() bool {
|
||||
return n.IP() == nil
|
||||
}
|
||||
|
||||
// Load retrieves an entry from the underlying record.
|
||||
func (n *Node) Load(k enr.Entry) error {
|
||||
return n.r.Load(k)
|
||||
}
|
||||
|
||||
// IP returns the IP address of the node.
|
||||
func (n *Node) IP() net.IP {
|
||||
var ip net.IP
|
||||
n.Load((*enr.IP)(&ip))
|
||||
return ip
|
||||
}
|
||||
|
||||
// UDP returns the UDP port of the node.
|
||||
func (n *Node) UDP() int {
|
||||
var port enr.UDP
|
||||
n.Load(&port)
|
||||
return int(port)
|
||||
}
|
||||
|
||||
// UDP returns the TCP port of the node.
|
||||
func (n *Node) TCP() int {
|
||||
var port enr.TCP
|
||||
n.Load(&port)
|
||||
return int(port)
|
||||
}
|
||||
|
||||
// Pubkey returns the secp256k1 public key of the node, if present.
|
||||
func (n *Node) Pubkey() *ecdsa.PublicKey {
|
||||
var key ecdsa.PublicKey
|
||||
if n.Load((*Secp256k1)(&key)) != nil {
|
||||
return nil
|
||||
}
|
||||
return &key
|
||||
}
|
||||
|
||||
// checks whether n is a valid complete node.
|
||||
func (n *Node) ValidateComplete() error {
|
||||
if n.Incomplete() {
|
||||
return errors.New("incomplete node")
|
||||
}
|
||||
if n.UDP() == 0 {
|
||||
return errors.New("missing UDP port")
|
||||
}
|
||||
ip := n.IP()
|
||||
if ip.IsMulticast() || ip.IsUnspecified() {
|
||||
return errors.New("invalid IP (multicast/unspecified)")
|
||||
}
|
||||
// Validate the node key (on curve, etc.).
|
||||
var key Secp256k1
|
||||
return n.Load(&key)
|
||||
}
|
||||
|
||||
// The string representation of a Node is a URL.
|
||||
// Please see ParseNode for a description of the format.
|
||||
func (n *Node) String() string {
|
||||
return n.v4URL()
|
||||
}
|
||||
|
||||
// MarshalText implements encoding.TextMarshaler.
|
||||
func (n *Node) MarshalText() ([]byte, error) {
|
||||
return []byte(n.v4URL()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||
func (n *Node) UnmarshalText(text []byte) error {
|
||||
dec, err := ParseV4(string(text))
|
||||
if err == nil {
|
||||
*n = *dec
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ID is a unique identifier for each node.
|
||||
type ID [32]byte
|
||||
|
||||
// Bytes returns a byte slice representation of the ID
|
||||
func (n ID) Bytes() []byte {
|
||||
return n[:]
|
||||
}
|
||||
|
||||
// ID prints as a long hexadecimal number.
|
||||
func (n ID) String() string {
|
||||
return fmt.Sprintf("%x", n[:])
|
||||
}
|
||||
|
||||
// The Go syntax representation of a ID is a call to HexID.
|
||||
func (n ID) GoString() string {
|
||||
return fmt.Sprintf("enode.HexID(\"%x\")", n[:])
|
||||
}
|
||||
|
||||
// TerminalString returns a shortened hex string for terminal logging.
|
||||
func (n ID) TerminalString() string {
|
||||
return hex.EncodeToString(n[:8])
|
||||
}
|
||||
|
||||
// MarshalText implements the encoding.TextMarshaler interface.
|
||||
func (n ID) MarshalText() ([]byte, error) {
|
||||
return []byte(hex.EncodeToString(n[:])), nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
||||
func (n *ID) UnmarshalText(text []byte) error {
|
||||
id, err := parseID(string(text))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*n = id
|
||||
return nil
|
||||
}
|
||||
|
||||
// HexID converts a hex string to an ID.
|
||||
// The string may be prefixed with 0x.
|
||||
// It panics if the string is not a valid ID.
|
||||
func HexID(in string) ID {
|
||||
id, err := parseID(in)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
func parseID(in string) (ID, error) {
|
||||
var id ID
|
||||
b, err := hex.DecodeString(strings.TrimPrefix(in, "0x"))
|
||||
if err != nil {
|
||||
return id, err
|
||||
} else if len(b) != len(id) {
|
||||
return id, fmt.Errorf("wrong length, want %d hex chars", len(id)*2)
|
||||
}
|
||||
copy(id[:], b)
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// DistCmp compares the distances a->target and b->target.
|
||||
// Returns -1 if a is closer to target, 1 if b is closer to target
|
||||
// and 0 if they are equal.
|
||||
func DistCmp(target, a, b ID) int {
|
||||
for i := range target {
|
||||
da := a[i] ^ target[i]
|
||||
db := b[i] ^ target[i]
|
||||
if da > db {
|
||||
return 1
|
||||
} else if da < db {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// LogDist returns the logarithmic distance between a and b, log2(a ^ b).
|
||||
func LogDist(a, b ID) int {
|
||||
lz := 0
|
||||
for i := range a {
|
||||
x := a[i] ^ b[i]
|
||||
if x == 0 {
|
||||
lz += 8
|
||||
} else {
|
||||
lz += bits.LeadingZeros8(x)
|
||||
break
|
||||
}
|
||||
}
|
||||
return len(a)*8 - lz
|
||||
}
|
||||
|
||||
// RandomID returns a random ID b such that logdist(a, b) == n.
|
||||
func RandomID(a ID, n int) (b ID) {
|
||||
if n == 0 {
|
||||
return a
|
||||
}
|
||||
// flip bit at position n, fill the rest with random bits
|
||||
b = a
|
||||
pos := len(a) - n/8 - 1
|
||||
bit := byte(0x01) << (byte(n%8) - 1)
|
||||
if bit == 0 {
|
||||
pos++
|
||||
bit = 0x80
|
||||
}
|
||||
b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits
|
||||
for i := pos + 1; i < len(a); i++ {
|
||||
b[i] = byte(rand.Intn(255))
|
||||
}
|
||||
return b
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package enode
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var pyRecord, _ = hex.DecodeString("f884b8407098ad865b00a582051940cb9cf36836572411a47278783077011599ed5cd16b76f2635f4e234738f30813a89eb9137e3e3df5266e3a1f11df72ecf1145ccb9c01826964827634826970847f00000189736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31388375647082765f")
|
||||
|
||||
// TestPythonInterop checks that we can decode and verify a record produced by the Python
|
||||
// implementation.
|
||||
func TestPythonInterop(t *testing.T) {
|
||||
var r enr.Record
|
||||
if err := rlp.DecodeBytes(pyRecord, &r); err != nil {
|
||||
t.Fatalf("can't decode: %v", err)
|
||||
}
|
||||
n, err := New(ValidSchemes, &r)
|
||||
if err != nil {
|
||||
t.Fatalf("can't verify record: %v", err)
|
||||
}
|
||||
|
||||
var (
|
||||
wantID = HexID("a448f24c6d18e575453db13171562b71999873db5b286df957af199ec94617f7")
|
||||
wantSeq = uint64(1)
|
||||
wantIP = enr.IP{127, 0, 0, 1}
|
||||
wantUDP = enr.UDP(30303)
|
||||
)
|
||||
if n.Seq() != wantSeq {
|
||||
t.Errorf("wrong seq: got %d, want %d", n.Seq(), wantSeq)
|
||||
}
|
||||
if n.ID() != wantID {
|
||||
t.Errorf("wrong id: got %x, want %x", n.ID(), wantID)
|
||||
}
|
||||
want := map[enr.Entry]interface{}{new(enr.IP): &wantIP, new(enr.UDP): &wantUDP}
|
||||
for k, v := range want {
|
||||
desc := fmt.Sprintf("loading key %q", k.ENRKey())
|
||||
if assert.NoError(t, n.Load(k), desc) {
|
||||
assert.Equal(t, k, v, desc)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,20 +14,17 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Contains the node database, storing previously seen nodes and any collected
|
||||
// metadata about them for QoS purposes.
|
||||
|
||||
package discover
|
||||
package enode
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
|
@ -39,16 +36,16 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
nodeDBNilNodeID = NodeID{} // Special node ID to use as a nil element.
|
||||
nodeDBNilID = ID{} // Special node ID to use as a nil element.
|
||||
nodeDBNodeExpiration = 24 * time.Hour // Time after which an unseen node should be dropped.
|
||||
nodeDBCleanupCycle = time.Hour // Time period for running the expiration task.
|
||||
nodeDBVersion = 5
|
||||
nodeDBVersion = 6
|
||||
)
|
||||
|
||||
// nodeDB stores all nodes we know about.
|
||||
type nodeDB struct {
|
||||
// DB is the node database, storing previously seen nodes and any collected metadata about
|
||||
// them for QoS purposes.
|
||||
type DB struct {
|
||||
lvl *leveldb.DB // Interface to the database itself
|
||||
self NodeID // Own node id to prevent adding it into the database
|
||||
runner sync.Once // Ensures we can start at most one expirer
|
||||
quit chan struct{} // Channel to signal the expiring thread to stop
|
||||
}
|
||||
|
@ -64,33 +61,27 @@ var (
|
|||
nodeDBDiscoverFindFails = nodeDBDiscoverRoot + ":findfail"
|
||||
)
|
||||
|
||||
// newNodeDB creates a new node database for storing and retrieving infos about
|
||||
// known peers in the network. If no path is given, an in-memory, temporary
|
||||
// database is constructed.
|
||||
func newNodeDB(path string, version int, self NodeID) (*nodeDB, error) {
|
||||
// OpenDB opens a node database for storing and retrieving infos about known peers in the
|
||||
// network. If no path is given an in-memory, temporary database is constructed.
|
||||
func OpenDB(path string) (*DB, error) {
|
||||
if path == "" {
|
||||
return newMemoryNodeDB(self)
|
||||
return newMemoryDB()
|
||||
}
|
||||
return newPersistentNodeDB(path, version, self)
|
||||
return newPersistentDB(path)
|
||||
}
|
||||
|
||||
// newMemoryNodeDB creates a new in-memory node database without a persistent
|
||||
// backend.
|
||||
func newMemoryNodeDB(self NodeID) (*nodeDB, error) {
|
||||
// newMemoryNodeDB creates a new in-memory node database without a persistent backend.
|
||||
func newMemoryDB() (*DB, error) {
|
||||
db, err := leveldb.Open(storage.NewMemStorage(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &nodeDB{
|
||||
lvl: db,
|
||||
self: self,
|
||||
quit: make(chan struct{}),
|
||||
}, nil
|
||||
return &DB{lvl: db, quit: make(chan struct{})}, nil
|
||||
}
|
||||
|
||||
// newPersistentNodeDB creates/opens a leveldb backed persistent node database,
|
||||
// also flushing its contents in case of a version mismatch.
|
||||
func newPersistentNodeDB(path string, version int, self NodeID) (*nodeDB, error) {
|
||||
func newPersistentDB(path string) (*DB, error) {
|
||||
opts := &opt.Options{OpenFilesCacheCapacity: 5}
|
||||
db, err := leveldb.OpenFile(path, opts)
|
||||
if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted {
|
||||
|
@ -102,7 +93,7 @@ func newPersistentNodeDB(path string, version int, self NodeID) (*nodeDB, error)
|
|||
// The nodes contained in the cache correspond to a certain protocol version.
|
||||
// Flush all nodes if the version doesn't match.
|
||||
currentVer := make([]byte, binary.MaxVarintLen64)
|
||||
currentVer = currentVer[:binary.PutVarint(currentVer, int64(version))]
|
||||
currentVer = currentVer[:binary.PutVarint(currentVer, int64(nodeDBVersion))]
|
||||
|
||||
blob, err := db.Get(nodeDBVersionKey, nil)
|
||||
switch err {
|
||||
|
@ -120,30 +111,26 @@ func newPersistentNodeDB(path string, version int, self NodeID) (*nodeDB, error)
|
|||
if err = os.RemoveAll(path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newPersistentNodeDB(path, version, self)
|
||||
return newPersistentDB(path)
|
||||
}
|
||||
}
|
||||
return &nodeDB{
|
||||
lvl: db,
|
||||
self: self,
|
||||
quit: make(chan struct{}),
|
||||
}, nil
|
||||
return &DB{lvl: db, quit: make(chan struct{})}, nil
|
||||
}
|
||||
|
||||
// makeKey generates the leveldb key-blob from a node id and its particular
|
||||
// field of interest.
|
||||
func makeKey(id NodeID, field string) []byte {
|
||||
if bytes.Equal(id[:], nodeDBNilNodeID[:]) {
|
||||
func makeKey(id ID, field string) []byte {
|
||||
if bytes.Equal(id[:], nodeDBNilID[:]) {
|
||||
return []byte(field)
|
||||
}
|
||||
return append(nodeDBItemPrefix, append(id[:], field...)...)
|
||||
}
|
||||
|
||||
// splitKey tries to split a database key into a node id and a field part.
|
||||
func splitKey(key []byte) (id NodeID, field string) {
|
||||
func splitKey(key []byte) (id ID, field string) {
|
||||
// If the key is not of a node, return it plainly
|
||||
if !bytes.HasPrefix(key, nodeDBItemPrefix) {
|
||||
return NodeID{}, string(key)
|
||||
return ID{}, string(key)
|
||||
}
|
||||
// Otherwise split the id and field
|
||||
item := key[len(nodeDBItemPrefix):]
|
||||
|
@ -155,7 +142,7 @@ func splitKey(key []byte) (id NodeID, field string) {
|
|||
|
||||
// fetchInt64 retrieves an integer instance associated with a particular
|
||||
// database key.
|
||||
func (db *nodeDB) fetchInt64(key []byte) int64 {
|
||||
func (db *DB) fetchInt64(key []byte) int64 {
|
||||
blob, err := db.lvl.Get(key, nil)
|
||||
if err != nil {
|
||||
return 0
|
||||
|
@ -169,39 +156,43 @@ func (db *nodeDB) fetchInt64(key []byte) int64 {
|
|||
|
||||
// storeInt64 update a specific database entry to the current time instance as a
|
||||
// unix timestamp.
|
||||
func (db *nodeDB) storeInt64(key []byte, n int64) error {
|
||||
func (db *DB) storeInt64(key []byte, n int64) error {
|
||||
blob := make([]byte, binary.MaxVarintLen64)
|
||||
blob = blob[:binary.PutVarint(blob, n)]
|
||||
|
||||
return db.lvl.Put(key, blob, nil)
|
||||
}
|
||||
|
||||
// node retrieves a node with a given id from the database.
|
||||
func (db *nodeDB) node(id NodeID) *Node {
|
||||
// Node retrieves a node with a given id from the database.
|
||||
func (db *DB) Node(id ID) *Node {
|
||||
blob, err := db.lvl.Get(makeKey(id, nodeDBDiscoverRoot), nil)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return mustDecodeNode(id[:], blob)
|
||||
}
|
||||
|
||||
func mustDecodeNode(id, data []byte) *Node {
|
||||
node := new(Node)
|
||||
if err := rlp.DecodeBytes(blob, node); err != nil {
|
||||
log.Error("Failed to decode node RLP", "err", err)
|
||||
return nil
|
||||
if err := rlp.DecodeBytes(data, &node.r); err != nil {
|
||||
panic(fmt.Errorf("p2p/enode: can't decode node %x in DB: %v", id, err))
|
||||
}
|
||||
node.sha = crypto.Keccak256Hash(node.ID[:])
|
||||
// Restore node id cache.
|
||||
copy(node.id[:], id)
|
||||
return node
|
||||
}
|
||||
|
||||
// updateNode inserts - potentially overwriting - a node into the peer database.
|
||||
func (db *nodeDB) updateNode(node *Node) error {
|
||||
blob, err := rlp.EncodeToBytes(node)
|
||||
// UpdateNode inserts - potentially overwriting - a node into the peer database.
|
||||
func (db *DB) UpdateNode(node *Node) error {
|
||||
blob, err := rlp.EncodeToBytes(&node.r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return db.lvl.Put(makeKey(node.ID, nodeDBDiscoverRoot), blob, nil)
|
||||
return db.lvl.Put(makeKey(node.ID(), nodeDBDiscoverRoot), blob, nil)
|
||||
}
|
||||
|
||||
// deleteNode deletes all information/keys associated with a node.
|
||||
func (db *nodeDB) deleteNode(id NodeID) error {
|
||||
// DeleteNode deletes all information/keys associated with a node.
|
||||
func (db *DB) DeleteNode(id ID) error {
|
||||
deleter := db.lvl.NewIterator(util.BytesPrefix(makeKey(id, "")), nil)
|
||||
for deleter.Next() {
|
||||
if err := db.lvl.Delete(deleter.Key(), nil); err != nil {
|
||||
|
@ -220,13 +211,13 @@ func (db *nodeDB) deleteNode(id NodeID) error {
|
|||
// it would require significant overhead to exactly trace the first successful
|
||||
// convergence, it's simpler to "ensure" the correct state when an appropriate
|
||||
// condition occurs (i.e. a successful bonding), and discard further events.
|
||||
func (db *nodeDB) ensureExpirer() {
|
||||
func (db *DB) ensureExpirer() {
|
||||
db.runner.Do(func() { go db.expirer() })
|
||||
}
|
||||
|
||||
// expirer should be started in a go routine, and is responsible for looping ad
|
||||
// infinitum and dropping stale data from the database.
|
||||
func (db *nodeDB) expirer() {
|
||||
func (db *DB) expirer() {
|
||||
tick := time.NewTicker(nodeDBCleanupCycle)
|
||||
defer tick.Stop()
|
||||
for {
|
||||
|
@ -243,7 +234,7 @@ func (db *nodeDB) expirer() {
|
|||
|
||||
// expireNodes iterates over the database and deletes all nodes that have not
|
||||
// been seen (i.e. received a pong from) for some allotted time.
|
||||
func (db *nodeDB) expireNodes() error {
|
||||
func (db *DB) expireNodes() error {
|
||||
threshold := time.Now().Add(-nodeDBNodeExpiration)
|
||||
|
||||
// Find discovered nodes that are older than the allowance
|
||||
|
@ -257,60 +248,56 @@ func (db *nodeDB) expireNodes() error {
|
|||
continue
|
||||
}
|
||||
// Skip the node if not expired yet (and not self)
|
||||
if !bytes.Equal(id[:], db.self[:]) {
|
||||
if seen := db.lastPongReceived(id); seen.After(threshold) {
|
||||
continue
|
||||
}
|
||||
if seen := db.LastPongReceived(id); seen.After(threshold) {
|
||||
continue
|
||||
}
|
||||
// Otherwise delete all associated information
|
||||
db.deleteNode(id)
|
||||
db.DeleteNode(id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// lastPingReceived retrieves the time of the last ping packet sent by the remote node.
|
||||
func (db *nodeDB) lastPingReceived(id NodeID) time.Time {
|
||||
// LastPingReceived retrieves the time of the last ping packet received from
|
||||
// a remote node.
|
||||
func (db *DB) LastPingReceived(id ID) time.Time {
|
||||
return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPing)), 0)
|
||||
}
|
||||
|
||||
// updateLastPing updates the last time remote node pinged us.
|
||||
func (db *nodeDB) updateLastPingReceived(id NodeID, instance time.Time) error {
|
||||
// UpdateLastPingReceived updates the last time we tried contacting a remote node.
|
||||
func (db *DB) UpdateLastPingReceived(id ID, instance time.Time) error {
|
||||
return db.storeInt64(makeKey(id, nodeDBDiscoverPing), instance.Unix())
|
||||
}
|
||||
|
||||
// lastPongReceived retrieves the time of the last successful pong from remote node.
|
||||
func (db *nodeDB) lastPongReceived(id NodeID) time.Time {
|
||||
// LastPongReceived retrieves the time of the last successful pong from remote node.
|
||||
func (db *DB) LastPongReceived(id ID) time.Time {
|
||||
// Launch expirer
|
||||
db.ensureExpirer()
|
||||
return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPong)), 0)
|
||||
}
|
||||
|
||||
// hasBond reports whether the given node is considered bonded.
|
||||
func (db *nodeDB) hasBond(id NodeID) bool {
|
||||
return time.Since(db.lastPongReceived(id)) < nodeDBNodeExpiration
|
||||
}
|
||||
|
||||
// updateLastPongReceived updates the last pong time of a node.
|
||||
func (db *nodeDB) updateLastPongReceived(id NodeID, instance time.Time) error {
|
||||
// UpdateLastPongReceived updates the last pong time of a node.
|
||||
func (db *DB) UpdateLastPongReceived(id ID, instance time.Time) error {
|
||||
return db.storeInt64(makeKey(id, nodeDBDiscoverPong), instance.Unix())
|
||||
}
|
||||
|
||||
// findFails retrieves the number of findnode failures since bonding.
|
||||
func (db *nodeDB) findFails(id NodeID) int {
|
||||
// FindFails retrieves the number of findnode failures since bonding.
|
||||
func (db *DB) FindFails(id ID) int {
|
||||
return int(db.fetchInt64(makeKey(id, nodeDBDiscoverFindFails)))
|
||||
}
|
||||
|
||||
// updateFindFails updates the number of findnode failures since bonding.
|
||||
func (db *nodeDB) updateFindFails(id NodeID, fails int) error {
|
||||
// UpdateFindFails updates the number of findnode failures since bonding.
|
||||
func (db *DB) UpdateFindFails(id ID, fails int) error {
|
||||
return db.storeInt64(makeKey(id, nodeDBDiscoverFindFails), int64(fails))
|
||||
}
|
||||
|
||||
// querySeeds retrieves random nodes to be used as potential seed nodes
|
||||
// QuerySeeds retrieves random nodes to be used as potential seed nodes
|
||||
// for bootstrapping.
|
||||
func (db *nodeDB) querySeeds(n int, maxAge time.Duration) []*Node {
|
||||
func (db *DB) QuerySeeds(n int, maxAge time.Duration) []*Node {
|
||||
var (
|
||||
now = time.Now()
|
||||
nodes = make([]*Node, 0, n)
|
||||
it = db.lvl.NewIterator(nil, nil)
|
||||
id NodeID
|
||||
id ID
|
||||
)
|
||||
defer it.Release()
|
||||
|
||||
|
@ -329,14 +316,11 @@ seek:
|
|||
id[0] = 0
|
||||
continue seek // iterator exhausted
|
||||
}
|
||||
if n.ID == db.self {
|
||||
continue seek
|
||||
}
|
||||
if now.Sub(db.lastPongReceived(n.ID)) > maxAge {
|
||||
if now.Sub(db.LastPongReceived(n.ID())) > maxAge {
|
||||
continue seek
|
||||
}
|
||||
for i := range nodes {
|
||||
if nodes[i].ID == n.ID {
|
||||
if nodes[i].ID() == n.ID() {
|
||||
continue seek // duplicate
|
||||
}
|
||||
}
|
||||
|
@ -353,18 +337,13 @@ func nextNode(it iterator.Iterator) *Node {
|
|||
if field != nodeDBDiscoverRoot {
|
||||
continue
|
||||
}
|
||||
var n Node
|
||||
if err := rlp.DecodeBytes(it.Value(), &n); err != nil {
|
||||
log.Warn("Failed to decode node RLP", "id", id, "err", err)
|
||||
continue
|
||||
}
|
||||
return &n
|
||||
return mustDecodeNode(id[:], it.Value())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// close flushes and closes the database files.
|
||||
func (db *nodeDB) close() {
|
||||
func (db *DB) Close() {
|
||||
close(db.quit)
|
||||
db.lvl.Close()
|
||||
}
|
|
@ -14,10 +14,11 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package discover
|
||||
package enode
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
|
@ -28,24 +29,21 @@ import (
|
|||
)
|
||||
|
||||
var nodeDBKeyTests = []struct {
|
||||
id NodeID
|
||||
id ID
|
||||
field string
|
||||
key []byte
|
||||
}{
|
||||
{
|
||||
id: NodeID{},
|
||||
id: ID{},
|
||||
field: "version",
|
||||
key: []byte{0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e}, // field
|
||||
},
|
||||
{
|
||||
id: MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
id: HexID("51232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
field: ":discover",
|
||||
key: []byte{0x6e, 0x3a, // prefix
|
||||
0x1d, 0xd9, 0xd6, 0x5c, 0x45, 0x52, 0xb5, 0xeb, // node id
|
||||
0x43, 0xd5, 0xad, 0x55, 0xa2, 0xee, 0x3f, 0x56, //
|
||||
0xc6, 0xcb, 0xc1, 0xc6, 0x4a, 0x5c, 0x8d, 0x65, //
|
||||
0x9f, 0x51, 0xfc, 0xd5, 0x1b, 0xac, 0xe2, 0x43, //
|
||||
0x51, 0x23, 0x2b, 0x8d, 0x78, 0x21, 0x61, 0x7d, //
|
||||
key: []byte{
|
||||
0x6e, 0x3a, // prefix
|
||||
0x51, 0x23, 0x2b, 0x8d, 0x78, 0x21, 0x61, 0x7d, // node id
|
||||
0x2b, 0x29, 0xb5, 0x4b, 0x81, 0xcd, 0xef, 0xb9, //
|
||||
0xb3, 0xe9, 0xc3, 0x7d, 0x7f, 0xd5, 0xf6, 0x32, //
|
||||
0x70, 0xbc, 0xc9, 0xe1, 0xa6, 0xf6, 0xa4, 0x39, //
|
||||
|
@ -54,7 +52,7 @@ var nodeDBKeyTests = []struct {
|
|||
},
|
||||
}
|
||||
|
||||
func TestNodeDBKeys(t *testing.T) {
|
||||
func TestDBKeys(t *testing.T) {
|
||||
for i, tt := range nodeDBKeyTests {
|
||||
if key := makeKey(tt.id, tt.field); !bytes.Equal(key, tt.key) {
|
||||
t.Errorf("make test %d: key mismatch: have 0x%x, want 0x%x", i, key, tt.key)
|
||||
|
@ -78,9 +76,9 @@ var nodeDBInt64Tests = []struct {
|
|||
{key: []byte{0x03}, value: 3},
|
||||
}
|
||||
|
||||
func TestNodeDBInt64(t *testing.T) {
|
||||
db, _ := newNodeDB("", nodeDBVersion, NodeID{})
|
||||
defer db.close()
|
||||
func TestDBInt64(t *testing.T) {
|
||||
db, _ := OpenDB("")
|
||||
defer db.Close()
|
||||
|
||||
tests := nodeDBInt64Tests
|
||||
for i := 0; i < len(tests); i++ {
|
||||
|
@ -101,9 +99,9 @@ func TestNodeDBInt64(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNodeDBFetchStore(t *testing.T) {
|
||||
node := NewNode(
|
||||
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
func TestDBFetchStore(t *testing.T) {
|
||||
node := NewV4(
|
||||
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
net.IP{192, 168, 0, 1},
|
||||
30303,
|
||||
30303,
|
||||
|
@ -111,47 +109,47 @@ func TestNodeDBFetchStore(t *testing.T) {
|
|||
inst := time.Now()
|
||||
num := 314
|
||||
|
||||
db, _ := newNodeDB("", nodeDBVersion, NodeID{})
|
||||
defer db.close()
|
||||
db, _ := OpenDB("")
|
||||
defer db.Close()
|
||||
|
||||
// Check fetch/store operations on a node ping object
|
||||
if stored := db.lastPingReceived(node.ID); stored.Unix() != 0 {
|
||||
if stored := db.LastPingReceived(node.ID()); stored.Unix() != 0 {
|
||||
t.Errorf("ping: non-existing object: %v", stored)
|
||||
}
|
||||
if err := db.updateLastPingReceived(node.ID, inst); err != nil {
|
||||
if err := db.UpdateLastPingReceived(node.ID(), inst); err != nil {
|
||||
t.Errorf("ping: failed to update: %v", err)
|
||||
}
|
||||
if stored := db.lastPingReceived(node.ID); stored.Unix() != inst.Unix() {
|
||||
if stored := db.LastPingReceived(node.ID()); stored.Unix() != inst.Unix() {
|
||||
t.Errorf("ping: value mismatch: have %v, want %v", stored, inst)
|
||||
}
|
||||
// Check fetch/store operations on a node pong object
|
||||
if stored := db.lastPongReceived(node.ID); stored.Unix() != 0 {
|
||||
if stored := db.LastPongReceived(node.ID()); stored.Unix() != 0 {
|
||||
t.Errorf("pong: non-existing object: %v", stored)
|
||||
}
|
||||
if err := db.updateLastPongReceived(node.ID, inst); err != nil {
|
||||
if err := db.UpdateLastPongReceived(node.ID(), inst); err != nil {
|
||||
t.Errorf("pong: failed to update: %v", err)
|
||||
}
|
||||
if stored := db.lastPongReceived(node.ID); stored.Unix() != inst.Unix() {
|
||||
if stored := db.LastPongReceived(node.ID()); stored.Unix() != inst.Unix() {
|
||||
t.Errorf("pong: value mismatch: have %v, want %v", stored, inst)
|
||||
}
|
||||
// Check fetch/store operations on a node findnode-failure object
|
||||
if stored := db.findFails(node.ID); stored != 0 {
|
||||
if stored := db.FindFails(node.ID()); stored != 0 {
|
||||
t.Errorf("find-node fails: non-existing object: %v", stored)
|
||||
}
|
||||
if err := db.updateFindFails(node.ID, num); err != nil {
|
||||
if err := db.UpdateFindFails(node.ID(), num); err != nil {
|
||||
t.Errorf("find-node fails: failed to update: %v", err)
|
||||
}
|
||||
if stored := db.findFails(node.ID); stored != num {
|
||||
if stored := db.FindFails(node.ID()); stored != num {
|
||||
t.Errorf("find-node fails: value mismatch: have %v, want %v", stored, num)
|
||||
}
|
||||
// Check fetch/store operations on an actual node object
|
||||
if stored := db.node(node.ID); stored != nil {
|
||||
if stored := db.Node(node.ID()); stored != nil {
|
||||
t.Errorf("node: non-existing object: %v", stored)
|
||||
}
|
||||
if err := db.updateNode(node); err != nil {
|
||||
if err := db.UpdateNode(node); err != nil {
|
||||
t.Errorf("node: failed to update: %v", err)
|
||||
}
|
||||
if stored := db.node(node.ID); stored == nil {
|
||||
if stored := db.Node(node.ID()); stored == nil {
|
||||
t.Errorf("node: not found")
|
||||
} else if !reflect.DeepEqual(stored, node) {
|
||||
t.Errorf("node: data mismatch: have %v, want %v", stored, node)
|
||||
|
@ -165,8 +163,8 @@ var nodeDBSeedQueryNodes = []struct {
|
|||
// This one should not be in the result set because its last
|
||||
// pong time is too far in the past.
|
||||
{
|
||||
node: NewNode(
|
||||
MustHexID("0x84d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
node: NewV4(
|
||||
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
net.IP{127, 0, 0, 3},
|
||||
30303,
|
||||
30303,
|
||||
|
@ -176,8 +174,8 @@ var nodeDBSeedQueryNodes = []struct {
|
|||
// This one shouldn't be in in the result set because its
|
||||
// nodeID is the local node's ID.
|
||||
{
|
||||
node: NewNode(
|
||||
MustHexID("0x57d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
node: NewV4(
|
||||
hexPubkey("ff93ff820abacd4351b0f14e47b324bc82ff014c226f3f66a53535734a3c150e7e38ca03ef0964ba55acddc768f5e99cd59dea95ddd4defbab1339c92fa319b2"),
|
||||
net.IP{127, 0, 0, 3},
|
||||
30303,
|
||||
30303,
|
||||
|
@ -187,8 +185,8 @@ var nodeDBSeedQueryNodes = []struct {
|
|||
|
||||
// These should be in the result set.
|
||||
{
|
||||
node: NewNode(
|
||||
MustHexID("0x22d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
node: NewV4(
|
||||
hexPubkey("c2b5eb3f5dde05f815b63777809ee3e7e0cbb20035a6b00ce327191e6eaa8f26a8d461c9112b7ab94698e7361fa19fd647e603e73239002946d76085b6f928d6"),
|
||||
net.IP{127, 0, 0, 1},
|
||||
30303,
|
||||
30303,
|
||||
|
@ -196,8 +194,8 @@ var nodeDBSeedQueryNodes = []struct {
|
|||
pong: time.Now().Add(-2 * time.Second),
|
||||
},
|
||||
{
|
||||
node: NewNode(
|
||||
MustHexID("0x44d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
node: NewV4(
|
||||
hexPubkey("6ca1d400c8ddf8acc94bcb0dd254911ad71a57bed5e0ae5aa205beed59b28c2339908e97990c493499613cff8ecf6c3dc7112a8ead220cdcd00d8847ca3db755"),
|
||||
net.IP{127, 0, 0, 2},
|
||||
30303,
|
||||
30303,
|
||||
|
@ -205,56 +203,91 @@ var nodeDBSeedQueryNodes = []struct {
|
|||
pong: time.Now().Add(-3 * time.Second),
|
||||
},
|
||||
{
|
||||
node: NewNode(
|
||||
MustHexID("0xe2d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
node: NewV4(
|
||||
hexPubkey("234dc63fe4d131212b38236c4c3411288d7bec61cbf7b120ff12c43dc60c96182882f4291d209db66f8a38e986c9c010ff59231a67f9515c7d1668b86b221a47"),
|
||||
net.IP{127, 0, 0, 3},
|
||||
30303,
|
||||
30303,
|
||||
),
|
||||
pong: time.Now().Add(-1 * time.Second),
|
||||
},
|
||||
{
|
||||
node: NewV4(
|
||||
hexPubkey("c013a50b4d1ebce5c377d8af8cb7114fd933ffc9627f96ad56d90fef5b7253ec736fd07ef9a81dc2955a997e54b7bf50afd0aa9f110595e2bec5bb7ce1657004"),
|
||||
net.IP{127, 0, 0, 3},
|
||||
30303,
|
||||
30303,
|
||||
),
|
||||
pong: time.Now().Add(-2 * time.Second),
|
||||
},
|
||||
{
|
||||
node: NewV4(
|
||||
hexPubkey("f141087e3e08af1aeec261ff75f48b5b1637f594ea9ad670e50051646b0416daa3b134c28788cbe98af26992a47652889cd8577ccc108ac02c6a664db2dc1283"),
|
||||
net.IP{127, 0, 0, 3},
|
||||
30303,
|
||||
30303,
|
||||
),
|
||||
pong: time.Now().Add(-2 * time.Second),
|
||||
},
|
||||
}
|
||||
|
||||
func TestNodeDBSeedQuery(t *testing.T) {
|
||||
db, _ := newNodeDB("", nodeDBVersion, nodeDBSeedQueryNodes[1].node.ID)
|
||||
defer db.close()
|
||||
func TestDBSeedQuery(t *testing.T) {
|
||||
// Querying seeds uses seeks an might not find all nodes
|
||||
// every time when the database is small. Run the test multiple
|
||||
// times to avoid flakes.
|
||||
const attempts = 15
|
||||
var err error
|
||||
for i := 0; i < attempts; i++ {
|
||||
if err = testSeedQuery(); err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("no successful run in %d attempts: %v", attempts, err)
|
||||
}
|
||||
}
|
||||
|
||||
func testSeedQuery() error {
|
||||
db, _ := OpenDB("")
|
||||
defer db.Close()
|
||||
|
||||
// Insert a batch of nodes for querying
|
||||
for i, seed := range nodeDBSeedQueryNodes {
|
||||
if err := db.updateNode(seed.node); err != nil {
|
||||
t.Fatalf("node %d: failed to insert: %v", i, err)
|
||||
if err := db.UpdateNode(seed.node); err != nil {
|
||||
return fmt.Errorf("node %d: failed to insert: %v", i, err)
|
||||
}
|
||||
if err := db.updateLastPongReceived(seed.node.ID, seed.pong); err != nil {
|
||||
t.Fatalf("node %d: failed to insert bondTime: %v", i, err)
|
||||
if err := db.UpdateLastPongReceived(seed.node.ID(), seed.pong); err != nil {
|
||||
return fmt.Errorf("node %d: failed to insert bondTime: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve the entire batch and check for duplicates
|
||||
seeds := db.querySeeds(len(nodeDBSeedQueryNodes)*2, time.Hour)
|
||||
have := make(map[NodeID]struct{})
|
||||
seeds := db.QuerySeeds(len(nodeDBSeedQueryNodes)*2, time.Hour)
|
||||
have := make(map[ID]struct{})
|
||||
for _, seed := range seeds {
|
||||
have[seed.ID] = struct{}{}
|
||||
have[seed.ID()] = struct{}{}
|
||||
}
|
||||
want := make(map[NodeID]struct{})
|
||||
for _, seed := range nodeDBSeedQueryNodes[2:] {
|
||||
want[seed.node.ID] = struct{}{}
|
||||
want := make(map[ID]struct{})
|
||||
for _, seed := range nodeDBSeedQueryNodes[1:] {
|
||||
want[seed.node.ID()] = struct{}{}
|
||||
}
|
||||
if len(seeds) != len(want) {
|
||||
t.Errorf("seed count mismatch: have %v, want %v", len(seeds), len(want))
|
||||
return fmt.Errorf("seed count mismatch: have %v, want %v", len(seeds), len(want))
|
||||
}
|
||||
for id := range have {
|
||||
if _, ok := want[id]; !ok {
|
||||
t.Errorf("extra seed: %v", id)
|
||||
return fmt.Errorf("extra seed: %v", id)
|
||||
}
|
||||
}
|
||||
for id := range want {
|
||||
if _, ok := have[id]; !ok {
|
||||
t.Errorf("missing seed: %v", id)
|
||||
return fmt.Errorf("missing seed: %v", id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestNodeDBPersistency(t *testing.T) {
|
||||
func TestDBPersistency(t *testing.T) {
|
||||
root, err := ioutil.TempDir("", "nodedb-")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temporary data folder: %v", err)
|
||||
|
@ -267,34 +300,24 @@ func TestNodeDBPersistency(t *testing.T) {
|
|||
)
|
||||
|
||||
// Create a persistent database and store some values
|
||||
db, err := newNodeDB(filepath.Join(root, "database"), nodeDBVersion, NodeID{})
|
||||
db, err := OpenDB(filepath.Join(root, "database"))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create persistent database: %v", err)
|
||||
}
|
||||
if err := db.storeInt64(testKey, testInt); err != nil {
|
||||
t.Fatalf("failed to store value: %v.", err)
|
||||
}
|
||||
db.close()
|
||||
db.Close()
|
||||
|
||||
// Reopen the database and check the value
|
||||
db, err = newNodeDB(filepath.Join(root, "database"), nodeDBVersion, NodeID{})
|
||||
db, err = OpenDB(filepath.Join(root, "database"))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open persistent database: %v", err)
|
||||
}
|
||||
if val := db.fetchInt64(testKey); val != testInt {
|
||||
t.Fatalf("value mismatch: have %v, want %v", val, testInt)
|
||||
}
|
||||
db.close()
|
||||
|
||||
// Change the database version and check flush
|
||||
db, err = newNodeDB(filepath.Join(root, "database"), nodeDBVersion+1, NodeID{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open persistent database: %v", err)
|
||||
}
|
||||
if val := db.fetchInt64(testKey); val != 0 {
|
||||
t.Fatalf("value mismatch: have %v, want %v", val, 0)
|
||||
}
|
||||
db.close()
|
||||
db.Close()
|
||||
}
|
||||
|
||||
var nodeDBExpirationNodes = []struct {
|
||||
|
@ -303,8 +326,8 @@ var nodeDBExpirationNodes = []struct {
|
|||
exp bool
|
||||
}{
|
||||
{
|
||||
node: NewNode(
|
||||
MustHexID("0x01d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
node: NewV4(
|
||||
hexPubkey("8d110e2ed4b446d9b5fb50f117e5f37fb7597af455e1dab0e6f045a6eeaa786a6781141659020d38bdc5e698ed3d4d2bafa8b5061810dfa63e8ac038db2e9b67"),
|
||||
net.IP{127, 0, 0, 1},
|
||||
30303,
|
||||
30303,
|
||||
|
@ -312,8 +335,8 @@ var nodeDBExpirationNodes = []struct {
|
|||
pong: time.Now().Add(-nodeDBNodeExpiration + time.Minute),
|
||||
exp: false,
|
||||
}, {
|
||||
node: NewNode(
|
||||
MustHexID("0x02d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
node: NewV4(
|
||||
hexPubkey("913a205579c32425b220dfba999d215066e5bdbf900226b11da1907eae5e93eb40616d47412cf819664e9eacbdfcca6b0c6e07e09847a38472d4be46ab0c3672"),
|
||||
net.IP{127, 0, 0, 2},
|
||||
30303,
|
||||
30303,
|
||||
|
@ -323,16 +346,16 @@ var nodeDBExpirationNodes = []struct {
|
|||
},
|
||||
}
|
||||
|
||||
func TestNodeDBExpiration(t *testing.T) {
|
||||
db, _ := newNodeDB("", nodeDBVersion, NodeID{})
|
||||
defer db.close()
|
||||
func TestDBExpiration(t *testing.T) {
|
||||
db, _ := OpenDB("")
|
||||
defer db.Close()
|
||||
|
||||
// Add all the test nodes and set their last pong time
|
||||
for i, seed := range nodeDBExpirationNodes {
|
||||
if err := db.updateNode(seed.node); err != nil {
|
||||
if err := db.UpdateNode(seed.node); err != nil {
|
||||
t.Fatalf("node %d: failed to insert: %v", i, err)
|
||||
}
|
||||
if err := db.updateLastPongReceived(seed.node.ID, seed.pong); err != nil {
|
||||
if err := db.UpdateLastPongReceived(seed.node.ID(), seed.pong); err != nil {
|
||||
t.Fatalf("node %d: failed to update bondTime: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
@ -341,40 +364,9 @@ func TestNodeDBExpiration(t *testing.T) {
|
|||
t.Fatalf("failed to expire nodes: %v", err)
|
||||
}
|
||||
for i, seed := range nodeDBExpirationNodes {
|
||||
node := db.node(seed.node.ID)
|
||||
node := db.Node(seed.node.ID())
|
||||
if (node == nil && !seed.exp) || (node != nil && seed.exp) {
|
||||
t.Errorf("node %d: expiration mismatch: have %v, want %v", i, node, seed.exp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeDBSelfExpiration(t *testing.T) {
|
||||
// Find a node in the tests that shouldn't expire, and assign it as self
|
||||
var self NodeID
|
||||
for _, node := range nodeDBExpirationNodes {
|
||||
if !node.exp {
|
||||
self = node.node.ID
|
||||
break
|
||||
}
|
||||
}
|
||||
db, _ := newNodeDB("", nodeDBVersion, self)
|
||||
defer db.close()
|
||||
|
||||
// Add all the test nodes and set their last pong time
|
||||
for i, seed := range nodeDBExpirationNodes {
|
||||
if err := db.updateNode(seed.node); err != nil {
|
||||
t.Fatalf("node %d: failed to insert: %v", i, err)
|
||||
}
|
||||
if err := db.updateLastPongReceived(seed.node.ID, seed.pong); err != nil {
|
||||
t.Fatalf("node %d: failed to update bondTime: %v", i, err)
|
||||
}
|
||||
}
|
||||
// Expire the nodes and make sure self has been evacuated too
|
||||
if err := db.expireNodes(); err != nil {
|
||||
t.Fatalf("failed to expire nodes: %v", err)
|
||||
}
|
||||
node := db.node(self)
|
||||
if node != nil {
|
||||
t.Errorf("self not evacuated")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package enode
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
||||
)
|
||||
|
||||
var incompleteNodeURL = regexp.MustCompile("(?i)^(?:enode://)?([0-9a-f]+)$")
|
||||
|
||||
// MustParseV4 parses a node URL. It panics if the URL is not valid.
|
||||
func MustParseV4(rawurl string) *Node {
|
||||
n, err := ParseV4(rawurl)
|
||||
if err != nil {
|
||||
panic("invalid node URL: " + err.Error())
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// ParseV4 parses a node URL.
|
||||
//
|
||||
// There are two basic forms of node URLs:
|
||||
//
|
||||
// - incomplete nodes, which only have the public key (node ID)
|
||||
// - complete nodes, which contain the public key and IP/Port information
|
||||
//
|
||||
// For incomplete nodes, the designator must look like one of these
|
||||
//
|
||||
// enode://<hex node id>
|
||||
// <hex node id>
|
||||
//
|
||||
// For complete nodes, the node ID is encoded in the username portion
|
||||
// of the URL, separated from the host by an @ sign. The hostname can
|
||||
// only be given as an IP address, DNS domain names are not allowed.
|
||||
// The port in the host name section is the TCP listening port. If the
|
||||
// TCP and UDP (discovery) ports differ, the UDP port is specified as
|
||||
// query parameter "discport".
|
||||
//
|
||||
// In the following example, the node URL describes
|
||||
// a node with IP address 10.3.58.6, TCP listening port 30303
|
||||
// and UDP discovery port 30301.
|
||||
//
|
||||
// enode://<hex node id>@10.3.58.6:30303?discport=30301
|
||||
func ParseV4(rawurl string) (*Node, error) {
|
||||
if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil {
|
||||
id, err := parsePubkey(m[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid node ID (%v)", err)
|
||||
}
|
||||
return NewV4(id, nil, 0, 0), nil
|
||||
}
|
||||
return parseComplete(rawurl)
|
||||
}
|
||||
|
||||
// NewV4 creates a node from discovery v4 node information. The record
|
||||
// contained in the node has a zero-length signature.
|
||||
func NewV4(pubkey *ecdsa.PublicKey, ip net.IP, tcp, udp int) *Node {
|
||||
var r enr.Record
|
||||
if ip != nil {
|
||||
r.Set(enr.IP(ip))
|
||||
}
|
||||
if udp != 0 {
|
||||
r.Set(enr.UDP(udp))
|
||||
}
|
||||
if tcp != 0 {
|
||||
r.Set(enr.TCP(tcp))
|
||||
}
|
||||
signV4Compat(&r, pubkey)
|
||||
n, err := New(v4CompatID{}, &r)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func parseComplete(rawurl string) (*Node, error) {
|
||||
var (
|
||||
id *ecdsa.PublicKey
|
||||
ip net.IP
|
||||
tcpPort, udpPort uint64
|
||||
)
|
||||
u, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if u.Scheme != "enode" {
|
||||
return nil, errors.New("invalid URL scheme, want \"enode\"")
|
||||
}
|
||||
// Parse the Node ID from the user portion.
|
||||
if u.User == nil {
|
||||
return nil, errors.New("does not contain node ID")
|
||||
}
|
||||
if id, err = parsePubkey(u.User.String()); err != nil {
|
||||
return nil, fmt.Errorf("invalid node ID (%v)", err)
|
||||
}
|
||||
// Parse the IP address.
|
||||
host, port, err := net.SplitHostPort(u.Host)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid host: %v", err)
|
||||
}
|
||||
if ip = net.ParseIP(host); ip == nil {
|
||||
return nil, errors.New("invalid IP address")
|
||||
}
|
||||
// Ensure the IP is 4 bytes long for IPv4 addresses.
|
||||
if ipv4 := ip.To4(); ipv4 != nil {
|
||||
ip = ipv4
|
||||
}
|
||||
// Parse the port numbers.
|
||||
if tcpPort, err = strconv.ParseUint(port, 10, 16); err != nil {
|
||||
return nil, errors.New("invalid port")
|
||||
}
|
||||
udpPort = tcpPort
|
||||
qv := u.Query()
|
||||
if qv.Get("discport") != "" {
|
||||
udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid discport in query")
|
||||
}
|
||||
}
|
||||
return NewV4(id, ip, int(tcpPort), int(udpPort)), nil
|
||||
}
|
||||
|
||||
// parsePubkey parses a hex-encoded secp256k1 public key.
|
||||
func parsePubkey(in string) (*ecdsa.PublicKey, error) {
|
||||
b, err := hex.DecodeString(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if len(b) != 64 {
|
||||
return nil, fmt.Errorf("wrong length, want %d hex chars", 128)
|
||||
}
|
||||
b = append([]byte{0x4}, b...)
|
||||
return crypto.UnmarshalPubkey(b)
|
||||
}
|
||||
|
||||
func (n *Node) v4URL() string {
|
||||
var (
|
||||
scheme enr.ID
|
||||
nodeid string
|
||||
key ecdsa.PublicKey
|
||||
)
|
||||
n.Load(&scheme)
|
||||
n.Load((*Secp256k1)(&key))
|
||||
switch {
|
||||
case scheme == "v4" || key != ecdsa.PublicKey{}:
|
||||
nodeid = fmt.Sprintf("%x", crypto.FromECDSAPub(&key)[1:])
|
||||
default:
|
||||
nodeid = fmt.Sprintf("%s.%x", scheme, n.id[:])
|
||||
}
|
||||
u := url.URL{Scheme: "enode"}
|
||||
if n.Incomplete() {
|
||||
u.Host = nodeid
|
||||
} else {
|
||||
addr := net.TCPAddr{IP: n.IP(), Port: n.TCP()}
|
||||
u.User = url.User(nodeid)
|
||||
u.Host = addr.String()
|
||||
if n.UDP() != n.TCP() {
|
||||
u.RawQuery = "discport=" + strconv.Itoa(n.UDP())
|
||||
}
|
||||
}
|
||||
return u.String()
|
||||
}
|
||||
|
||||
// PubkeyToIDV4 derives the v4 node address from the given public key.
|
||||
func PubkeyToIDV4(key *ecdsa.PublicKey) ID {
|
||||
e := make([]byte, 64)
|
||||
math.ReadBits(key.X, e[:len(e)/2])
|
||||
math.ReadBits(key.Y, e[len(e)/2:])
|
||||
return ID(crypto.Keccak256Hash(e))
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2015 The go-ethereum Authors
|
||||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
|
@ -14,45 +14,19 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package discover
|
||||
package enode
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"crypto/ecdsa"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"net"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
func ExampleNewNode() {
|
||||
id := MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439")
|
||||
|
||||
// Complete nodes contain UDP and TCP endpoints:
|
||||
n1 := NewNode(id, net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), 52150, 30303)
|
||||
fmt.Println("n1:", n1)
|
||||
fmt.Println("n1.Incomplete() ->", n1.Incomplete())
|
||||
|
||||
// An incomplete node can be created by passing zero values
|
||||
// for all parameters except id.
|
||||
n2 := NewNode(id, nil, 0, 0)
|
||||
fmt.Println("n2:", n2)
|
||||
fmt.Println("n2.Incomplete() ->", n2.Incomplete())
|
||||
|
||||
// Output:
|
||||
// n1: enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:30303?discport=52150
|
||||
// n1.Incomplete() -> false
|
||||
// n2: enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439
|
||||
// n2.Incomplete() -> true
|
||||
}
|
||||
|
||||
var parseNodeTests = []struct {
|
||||
rawurl string
|
||||
wantError string
|
||||
|
@ -81,8 +55,8 @@ var parseNodeTests = []struct {
|
|||
},
|
||||
{
|
||||
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150",
|
||||
wantResult: NewNode(
|
||||
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
wantResult: NewV4(
|
||||
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
net.IP{0x7f, 0x0, 0x0, 0x1},
|
||||
52150,
|
||||
52150,
|
||||
|
@ -90,8 +64,8 @@ var parseNodeTests = []struct {
|
|||
},
|
||||
{
|
||||
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150",
|
||||
wantResult: NewNode(
|
||||
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
wantResult: NewV4(
|
||||
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
net.ParseIP("::"),
|
||||
52150,
|
||||
52150,
|
||||
|
@ -99,8 +73,8 @@ var parseNodeTests = []struct {
|
|||
},
|
||||
{
|
||||
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:52150",
|
||||
wantResult: NewNode(
|
||||
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
wantResult: NewV4(
|
||||
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
net.ParseIP("2001:db8:3c4d:15::abcd:ef12"),
|
||||
52150,
|
||||
52150,
|
||||
|
@ -108,25 +82,25 @@ var parseNodeTests = []struct {
|
|||
},
|
||||
{
|
||||
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150?discport=22334",
|
||||
wantResult: NewNode(
|
||||
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
wantResult: NewV4(
|
||||
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
net.IP{0x7f, 0x0, 0x0, 0x1},
|
||||
22334,
|
||||
52150,
|
||||
22334,
|
||||
),
|
||||
},
|
||||
// Incomplete nodes with no address.
|
||||
{
|
||||
rawurl: "1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439",
|
||||
wantResult: NewNode(
|
||||
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
wantResult: NewV4(
|
||||
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
nil, 0, 0,
|
||||
),
|
||||
},
|
||||
{
|
||||
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439",
|
||||
wantResult: NewNode(
|
||||
MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
wantResult: NewV4(
|
||||
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
nil, 0, 0,
|
||||
),
|
||||
},
|
||||
|
@ -146,9 +120,17 @@ var parseNodeTests = []struct {
|
|||
},
|
||||
}
|
||||
|
||||
func hexPubkey(h string) *ecdsa.PublicKey {
|
||||
k, err := parsePubkey(h)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
func TestParseNode(t *testing.T) {
|
||||
for _, test := range parseNodeTests {
|
||||
n, err := ParseNode(test.rawurl)
|
||||
n, err := ParseV4(test.rawurl)
|
||||
if test.wantError != "" {
|
||||
if err == nil {
|
||||
t.Errorf("test %q:\n got nil error, expected %#q", test.rawurl, test.wantError)
|
||||
|
@ -163,7 +145,7 @@ func TestParseNode(t *testing.T) {
|
|||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(n, test.wantResult) {
|
||||
t.Errorf("test %q:\n result mismatch:\ngot: %#v, want: %#v", test.rawurl, n, test.wantResult)
|
||||
t.Errorf("test %q:\n result mismatch:\ngot: %#v\nwant: %#v", test.rawurl, n, test.wantResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -181,9 +163,9 @@ func TestNodeString(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestHexID(t *testing.T) {
|
||||
ref := NodeID{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 106, 217, 182, 31, 165, 174, 1, 67, 7, 235, 220, 150, 66, 83, 173, 205, 159, 44, 10, 57, 42, 161, 26, 188}
|
||||
id1 := MustHexID("0x000000000000000000000000000000000000000000000000000000000000000000000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc")
|
||||
id2 := MustHexID("000000000000000000000000000000000000000000000000000000000000000000000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc")
|
||||
ref := ID{0, 0, 0, 0, 0, 0, 0, 128, 106, 217, 182, 31, 165, 174, 1, 67, 7, 235, 220, 150, 66, 83, 173, 205, 159, 44, 10, 57, 42, 161, 26, 188}
|
||||
id1 := HexID("0x00000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc")
|
||||
id2 := HexID("00000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc")
|
||||
|
||||
if id1 != ref {
|
||||
t.Errorf("wrong id1\ngot %v\nwant %v", id1[:], ref[:])
|
||||
|
@ -193,17 +175,14 @@ func TestHexID(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNodeID_textEncoding(t *testing.T) {
|
||||
ref := NodeID{
|
||||
func TestID_textEncoding(t *testing.T) {
|
||||
ref := ID{
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20,
|
||||
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30,
|
||||
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40,
|
||||
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x50,
|
||||
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x60,
|
||||
0x61, 0x62, 0x63, 0x64,
|
||||
0x31, 0x32,
|
||||
}
|
||||
hex := "01020304050607080910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364"
|
||||
hex := "0102030405060708091011121314151617181920212223242526272829303132"
|
||||
|
||||
text, err := ref.MarshalText()
|
||||
if err != nil {
|
||||
|
@ -213,7 +192,7 @@ func TestNodeID_textEncoding(t *testing.T) {
|
|||
t.Fatalf("text encoding did not match\nexpected: %s\ngot: %s", hex, text)
|
||||
}
|
||||
|
||||
id := new(NodeID)
|
||||
id := new(ID)
|
||||
if err := id.UnmarshalText(text); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -222,114 +201,43 @@ func TestNodeID_textEncoding(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNodeID_recover(t *testing.T) {
|
||||
prv := newkey()
|
||||
hash := make([]byte, 32)
|
||||
sig, err := crypto.Sign(hash, prv)
|
||||
if err != nil {
|
||||
t.Fatalf("signing error: %v", err)
|
||||
}
|
||||
|
||||
pub := PubkeyID(&prv.PublicKey)
|
||||
recpub, err := recoverNodeID(hash, sig)
|
||||
if err != nil {
|
||||
t.Fatalf("recovery error: %v", err)
|
||||
}
|
||||
if pub != recpub {
|
||||
t.Errorf("recovered wrong pubkey:\ngot: %v\nwant: %v", recpub, pub)
|
||||
}
|
||||
|
||||
ecdsa, err := pub.Pubkey()
|
||||
if err != nil {
|
||||
t.Errorf("Pubkey error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(ecdsa, &prv.PublicKey) {
|
||||
t.Errorf("Pubkey mismatch:\n got: %#v\n want: %#v", ecdsa, &prv.PublicKey)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeID_pubkeyBad(t *testing.T) {
|
||||
ecdsa, err := NodeID{}.Pubkey()
|
||||
if err == nil {
|
||||
t.Error("expected error for zero ID")
|
||||
}
|
||||
if ecdsa != nil {
|
||||
t.Error("expected nil result")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeID_distcmp(t *testing.T) {
|
||||
distcmpBig := func(target, a, b common.Hash) int {
|
||||
distcmpBig := func(target, a, b ID) int {
|
||||
tbig := new(big.Int).SetBytes(target[:])
|
||||
abig := new(big.Int).SetBytes(a[:])
|
||||
bbig := new(big.Int).SetBytes(b[:])
|
||||
return new(big.Int).Xor(tbig, abig).Cmp(new(big.Int).Xor(tbig, bbig))
|
||||
}
|
||||
if err := quick.CheckEqual(distcmp, distcmpBig, quickcfg()); err != nil {
|
||||
if err := quick.CheckEqual(DistCmp, distcmpBig, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// the random tests is likely to miss the case where they're equal.
|
||||
// The random tests is likely to miss the case where a and b are equal,
|
||||
// this test checks it explicitly.
|
||||
func TestNodeID_distcmpEqual(t *testing.T) {
|
||||
base := common.Hash{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
x := common.Hash{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
|
||||
if distcmp(base, x, x) != 0 {
|
||||
t.Errorf("distcmp(base, x, x) != 0")
|
||||
base := ID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
x := ID{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
|
||||
if DistCmp(base, x, x) != 0 {
|
||||
t.Errorf("DistCmp(base, x, x) != 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeID_logdist(t *testing.T) {
|
||||
logdistBig := func(a, b common.Hash) int {
|
||||
logdistBig := func(a, b ID) int {
|
||||
abig, bbig := new(big.Int).SetBytes(a[:]), new(big.Int).SetBytes(b[:])
|
||||
return new(big.Int).Xor(abig, bbig).BitLen()
|
||||
}
|
||||
if err := quick.CheckEqual(logdist, logdistBig, quickcfg()); err != nil {
|
||||
if err := quick.CheckEqual(LogDist, logdistBig, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// the random tests is likely to miss the case where they're equal.
|
||||
// The random tests is likely to miss the case where a and b are equal,
|
||||
// this test checks it explicitly.
|
||||
func TestNodeID_logdistEqual(t *testing.T) {
|
||||
x := common.Hash{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
if logdist(x, x) != 0 {
|
||||
t.Errorf("logdist(x, x) != 0")
|
||||
x := ID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
if LogDist(x, x) != 0 {
|
||||
t.Errorf("LogDist(x, x) != 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeID_hashAtDistance(t *testing.T) {
|
||||
// we don't use quick.Check here because its output isn't
|
||||
// very helpful when the test fails.
|
||||
cfg := quickcfg()
|
||||
for i := 0; i < cfg.MaxCount; i++ {
|
||||
a := gen(common.Hash{}, cfg.Rand).(common.Hash)
|
||||
dist := cfg.Rand.Intn(len(common.Hash{}) * 8)
|
||||
result := hashAtDistance(a, dist)
|
||||
actualdist := logdist(result, a)
|
||||
|
||||
if dist != actualdist {
|
||||
t.Log("a: ", a)
|
||||
t.Log("result:", result)
|
||||
t.Fatalf("#%d: distance of result is %d, want %d", i, actualdist, dist)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func quickcfg() *quick.Config {
|
||||
return &quick.Config{
|
||||
MaxCount: 5000,
|
||||
Rand: rand.New(rand.NewSource(time.Now().Unix())),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: The Generate method can be dropped when we require Go >= 1.5
|
||||
// because testing/quick learned to generate arrays in 1.5.
|
||||
|
||||
func (NodeID) Generate(rand *rand.Rand, size int) reflect.Value {
|
||||
var id NodeID
|
||||
m := rand.Intn(len(id))
|
||||
for i := len(id) - 1; i > m; i-- {
|
||||
id[i] = byte(rand.Uint32())
|
||||
}
|
||||
return reflect.ValueOf(id)
|
||||
}
|
170
p2p/enr/enr.go
170
p2p/enr/enr.go
|
@ -15,14 +15,20 @@
|
|||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package enr implements Ethereum Node Records as defined in EIP-778. A node record holds
|
||||
// arbitrary information about a node on the peer-to-peer network.
|
||||
//
|
||||
// Records contain named keys. To store and retrieve key/values in a record, use the Entry
|
||||
// arbitrary information about a node on the peer-to-peer network. Node information is
|
||||
// stored in key/value pairs. To store and retrieve key/values in a record, use the Entry
|
||||
// interface.
|
||||
//
|
||||
// Records must be signed before transmitting them to another node. Decoding a record verifies
|
||||
// its signature. When creating a record, set the entries you want, then call Sign to add the
|
||||
// signature. Modifying a record invalidates the signature.
|
||||
// Signature Handling
|
||||
//
|
||||
// Records must be signed before transmitting them to another node.
|
||||
//
|
||||
// Decoding a record doesn't check its signature. Code working with records from an
|
||||
// untrusted source must always verify two things: that the record uses an identity scheme
|
||||
// deemed secure, and that the signature is valid according to the declared scheme.
|
||||
//
|
||||
// When creating a record, set the entries you want and use a signing function provided by
|
||||
// the identity scheme to add the signature. Modifying a record invalidates the signature.
|
||||
//
|
||||
// Package enr supports the "secp256k1-keccak" identity scheme.
|
||||
package enr
|
||||
|
@ -40,8 +46,7 @@ import (
|
|||
const SizeLimit = 300 // maximum encoded size of a node record in bytes
|
||||
|
||||
var (
|
||||
errNoID = errors.New("unknown or unspecified identity scheme")
|
||||
errInvalidSig = errors.New("invalid signature")
|
||||
ErrInvalidSig = errors.New("invalid signature on node record")
|
||||
errNotSorted = errors.New("record key/value pairs are not sorted by key")
|
||||
errDuplicateKey = errors.New("record contains duplicate key")
|
||||
errIncompletePair = errors.New("record contains incomplete k/v pair")
|
||||
|
@ -50,6 +55,32 @@ var (
|
|||
errNotFound = errors.New("no such key in record")
|
||||
)
|
||||
|
||||
// An IdentityScheme is capable of verifying record signatures and
|
||||
// deriving node addresses.
|
||||
type IdentityScheme interface {
|
||||
Verify(r *Record, sig []byte) error
|
||||
NodeAddr(r *Record) []byte
|
||||
}
|
||||
|
||||
// SchemeMap is a registry of named identity schemes.
|
||||
type SchemeMap map[string]IdentityScheme
|
||||
|
||||
func (m SchemeMap) Verify(r *Record, sig []byte) error {
|
||||
s := m[r.IdentityScheme()]
|
||||
if s == nil {
|
||||
return ErrInvalidSig
|
||||
}
|
||||
return s.Verify(r, sig)
|
||||
}
|
||||
|
||||
func (m SchemeMap) NodeAddr(r *Record) []byte {
|
||||
s := m[r.IdentityScheme()]
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
return s.NodeAddr(r)
|
||||
}
|
||||
|
||||
// Record represents a node record. The zero value is an empty record.
|
||||
type Record struct {
|
||||
seq uint64 // sequence number
|
||||
|
@ -64,11 +95,6 @@ type pair struct {
|
|||
v rlp.RawValue
|
||||
}
|
||||
|
||||
// Signed reports whether the record has a valid signature.
|
||||
func (r *Record) Signed() bool {
|
||||
return r.signature != nil
|
||||
}
|
||||
|
||||
// Seq returns the sequence number.
|
||||
func (r *Record) Seq() uint64 {
|
||||
return r.seq
|
||||
|
@ -140,7 +166,7 @@ func (r *Record) invalidate() {
|
|||
// EncodeRLP implements rlp.Encoder. Encoding fails if
|
||||
// the record is unsigned.
|
||||
func (r Record) EncodeRLP(w io.Writer) error {
|
||||
if !r.Signed() {
|
||||
if r.signature == nil {
|
||||
return errEncodeUnsigned
|
||||
}
|
||||
_, err := w.Write(r.raw)
|
||||
|
@ -149,25 +175,34 @@ func (r Record) EncodeRLP(w io.Writer) error {
|
|||
|
||||
// DecodeRLP implements rlp.Decoder. Decoding verifies the signature.
|
||||
func (r *Record) DecodeRLP(s *rlp.Stream) error {
|
||||
raw, err := s.Raw()
|
||||
dec, raw, err := decodeRecord(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*r = dec
|
||||
r.raw = raw
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeRecord(s *rlp.Stream) (dec Record, raw []byte, err error) {
|
||||
raw, err = s.Raw()
|
||||
if err != nil {
|
||||
return dec, raw, err
|
||||
}
|
||||
if len(raw) > SizeLimit {
|
||||
return errTooBig
|
||||
return dec, raw, errTooBig
|
||||
}
|
||||
|
||||
// Decode the RLP container.
|
||||
dec := Record{raw: raw}
|
||||
s = rlp.NewStream(bytes.NewReader(raw), 0)
|
||||
if _, err := s.List(); err != nil {
|
||||
return err
|
||||
return dec, raw, err
|
||||
}
|
||||
if err = s.Decode(&dec.signature); err != nil {
|
||||
return err
|
||||
return dec, raw, err
|
||||
}
|
||||
if err = s.Decode(&dec.seq); err != nil {
|
||||
return err
|
||||
return dec, raw, err
|
||||
}
|
||||
// The rest of the record contains sorted k/v pairs.
|
||||
var prevkey string
|
||||
|
@ -177,73 +212,68 @@ func (r *Record) DecodeRLP(s *rlp.Stream) error {
|
|||
if err == rlp.EOL {
|
||||
break
|
||||
}
|
||||
return err
|
||||
return dec, raw, err
|
||||
}
|
||||
if err := s.Decode(&kv.v); err != nil {
|
||||
if err == rlp.EOL {
|
||||
return errIncompletePair
|
||||
return dec, raw, errIncompletePair
|
||||
}
|
||||
return err
|
||||
return dec, raw, err
|
||||
}
|
||||
if i > 0 {
|
||||
if kv.k == prevkey {
|
||||
return errDuplicateKey
|
||||
return dec, raw, errDuplicateKey
|
||||
}
|
||||
if kv.k < prevkey {
|
||||
return errNotSorted
|
||||
return dec, raw, errNotSorted
|
||||
}
|
||||
}
|
||||
dec.pairs = append(dec.pairs, kv)
|
||||
prevkey = kv.k
|
||||
}
|
||||
if err := s.ListEnd(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, scheme := dec.idScheme()
|
||||
if scheme == nil {
|
||||
return errNoID
|
||||
}
|
||||
if err := scheme.Verify(&dec, dec.signature); err != nil {
|
||||
return err
|
||||
}
|
||||
*r = dec
|
||||
return nil
|
||||
return dec, raw, s.ListEnd()
|
||||
}
|
||||
|
||||
// NodeAddr returns the node address. The return value will be nil if the record is
|
||||
// unsigned or uses an unknown identity scheme.
|
||||
func (r *Record) NodeAddr() []byte {
|
||||
_, scheme := r.idScheme()
|
||||
if scheme == nil {
|
||||
return nil
|
||||
}
|
||||
return scheme.NodeAddr(r)
|
||||
// IdentityScheme returns the name of the identity scheme in the record.
|
||||
func (r *Record) IdentityScheme() string {
|
||||
var id ID
|
||||
r.Load(&id)
|
||||
return string(id)
|
||||
}
|
||||
|
||||
// VerifySignature checks whether the record is signed using the given identity scheme.
|
||||
func (r *Record) VerifySignature(s IdentityScheme) error {
|
||||
return s.Verify(r, r.signature)
|
||||
}
|
||||
|
||||
// SetSig sets the record signature. It returns an error if the encoded record is larger
|
||||
// than the size limit or if the signature is invalid according to the passed scheme.
|
||||
func (r *Record) SetSig(idscheme string, sig []byte) error {
|
||||
// Check that "id" is set and matches the given scheme. This panics because
|
||||
// inconsitencies here are always implementation bugs in the signing function calling
|
||||
// this method.
|
||||
id, s := r.idScheme()
|
||||
if s == nil {
|
||||
panic(errNoID)
|
||||
//
|
||||
// You can also use SetSig to remove the signature explicitly by passing a nil scheme
|
||||
// and signature.
|
||||
//
|
||||
// SetSig panics when either the scheme or the signature (but not both) are nil.
|
||||
func (r *Record) SetSig(s IdentityScheme, sig []byte) error {
|
||||
switch {
|
||||
// Prevent storing invalid data.
|
||||
case s == nil && sig != nil:
|
||||
panic("enr: invalid call to SetSig with non-nil signature but nil scheme")
|
||||
case s != nil && sig == nil:
|
||||
panic("enr: invalid call to SetSig with nil signature but non-nil scheme")
|
||||
// Verify if we have a scheme.
|
||||
case s != nil:
|
||||
if err := s.Verify(r, sig); err != nil {
|
||||
return err
|
||||
}
|
||||
raw, err := r.encode(sig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.signature, r.raw = sig, raw
|
||||
// Reset otherwise.
|
||||
default:
|
||||
r.signature, r.raw = nil, nil
|
||||
}
|
||||
if id != idscheme {
|
||||
panic(fmt.Errorf("identity scheme mismatch in Sign: record has %s, want %s", id, idscheme))
|
||||
}
|
||||
|
||||
// Verify against the scheme.
|
||||
if err := s.Verify(r, sig); err != nil {
|
||||
return err
|
||||
}
|
||||
raw, err := r.encode(sig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.signature, r.raw = sig, raw
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -268,11 +298,3 @@ func (r *Record) encode(sig []byte) (raw []byte, err error) {
|
|||
}
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (r *Record) idScheme() (string, IdentityScheme) {
|
||||
var id ID
|
||||
if err := r.Load(&id); err != nil {
|
||||
return "", nil
|
||||
}
|
||||
return string(id), FindIdentityScheme(string(id))
|
||||
}
|
||||
|
|
|
@ -18,23 +18,17 @@ package enr
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
privkey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
pubkey = &privkey.PublicKey
|
||||
)
|
||||
|
||||
var rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
func randomString(strlen int) string {
|
||||
|
@ -87,18 +81,6 @@ func TestGetSetUDP(t *testing.T) {
|
|||
assert.Equal(t, port, port2)
|
||||
}
|
||||
|
||||
// TestGetSetSecp256k1 tests encoding/decoding and setting/getting of the Secp256k1 key.
|
||||
func TestGetSetSecp256k1(t *testing.T) {
|
||||
var r Record
|
||||
if err := SignV4(&r, privkey); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var pk Secp256k1
|
||||
require.NoError(t, r.Load(&pk))
|
||||
assert.EqualValues(t, pubkey, &pk)
|
||||
}
|
||||
|
||||
func TestLoadErrors(t *testing.T) {
|
||||
var r Record
|
||||
ip4 := IP{127, 0, 0, 1}
|
||||
|
@ -167,23 +149,20 @@ func TestSortedGetAndSet(t *testing.T) {
|
|||
func TestDirty(t *testing.T) {
|
||||
var r Record
|
||||
|
||||
if r.Signed() {
|
||||
t.Error("Signed returned true for zero record")
|
||||
}
|
||||
if _, err := rlp.EncodeToBytes(r); err != errEncodeUnsigned {
|
||||
t.Errorf("expected errEncodeUnsigned, got %#v", err)
|
||||
}
|
||||
|
||||
require.NoError(t, SignV4(&r, privkey))
|
||||
if !r.Signed() {
|
||||
t.Error("Signed return false for signed record")
|
||||
require.NoError(t, signTest([]byte{5}, &r))
|
||||
if len(r.signature) == 0 {
|
||||
t.Error("record is not signed")
|
||||
}
|
||||
_, err := rlp.EncodeToBytes(r)
|
||||
assert.NoError(t, err)
|
||||
|
||||
r.SetSeq(3)
|
||||
if r.Signed() {
|
||||
t.Error("Signed returned true for modified record")
|
||||
if len(r.signature) != 0 {
|
||||
t.Error("signature still set after modification")
|
||||
}
|
||||
if _, err := rlp.EncodeToBytes(r); err != errEncodeUnsigned {
|
||||
t.Errorf("expected errEncodeUnsigned, got %#v", err)
|
||||
|
@ -210,7 +189,7 @@ func TestSignEncodeAndDecode(t *testing.T) {
|
|||
var r Record
|
||||
r.Set(UDP(30303))
|
||||
r.Set(IP{127, 0, 0, 1})
|
||||
require.NoError(t, SignV4(&r, privkey))
|
||||
require.NoError(t, signTest([]byte{5}, &r))
|
||||
|
||||
blob, err := rlp.EncodeToBytes(r)
|
||||
require.NoError(t, err)
|
||||
|
@ -224,48 +203,6 @@ func TestSignEncodeAndDecode(t *testing.T) {
|
|||
assert.Equal(t, blob, blob2)
|
||||
}
|
||||
|
||||
func TestNodeAddr(t *testing.T) {
|
||||
var r Record
|
||||
if addr := r.NodeAddr(); addr != nil {
|
||||
t.Errorf("wrong address on empty record: got %v, want %v", addr, nil)
|
||||
}
|
||||
|
||||
require.NoError(t, SignV4(&r, privkey))
|
||||
expected := "a448f24c6d18e575453db13171562b71999873db5b286df957af199ec94617f7"
|
||||
assert.Equal(t, expected, hex.EncodeToString(r.NodeAddr()))
|
||||
}
|
||||
|
||||
var pyRecord, _ = hex.DecodeString("f884b8407098ad865b00a582051940cb9cf36836572411a47278783077011599ed5cd16b76f2635f4e234738f30813a89eb9137e3e3df5266e3a1f11df72ecf1145ccb9c01826964827634826970847f00000189736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31388375647082765f")
|
||||
|
||||
// TestPythonInterop checks that we can decode and verify a record produced by the Python
|
||||
// implementation.
|
||||
func TestPythonInterop(t *testing.T) {
|
||||
var r Record
|
||||
if err := rlp.DecodeBytes(pyRecord, &r); err != nil {
|
||||
t.Fatalf("can't decode: %v", err)
|
||||
}
|
||||
|
||||
var (
|
||||
wantAddr, _ = hex.DecodeString("a448f24c6d18e575453db13171562b71999873db5b286df957af199ec94617f7")
|
||||
wantSeq = uint64(1)
|
||||
wantIP = IP{127, 0, 0, 1}
|
||||
wantUDP = UDP(30303)
|
||||
)
|
||||
if r.Seq() != wantSeq {
|
||||
t.Errorf("wrong seq: got %d, want %d", r.Seq(), wantSeq)
|
||||
}
|
||||
if addr := r.NodeAddr(); !bytes.Equal(addr, wantAddr) {
|
||||
t.Errorf("wrong addr: got %x, want %x", addr, wantAddr)
|
||||
}
|
||||
want := map[Entry]interface{}{new(IP): &wantIP, new(UDP): &wantUDP}
|
||||
for k, v := range want {
|
||||
desc := fmt.Sprintf("loading key %q", k.ENRKey())
|
||||
if assert.NoError(t, r.Load(k), desc) {
|
||||
assert.Equal(t, k, v, desc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestRecordTooBig tests that records bigger than SizeLimit bytes cannot be signed.
|
||||
func TestRecordTooBig(t *testing.T) {
|
||||
var r Record
|
||||
|
@ -273,13 +210,13 @@ func TestRecordTooBig(t *testing.T) {
|
|||
|
||||
// set a big value for random key, expect error
|
||||
r.Set(WithEntry(key, randomString(SizeLimit)))
|
||||
if err := SignV4(&r, privkey); err != errTooBig {
|
||||
if err := signTest([]byte{5}, &r); err != errTooBig {
|
||||
t.Fatalf("expected to get errTooBig, got %#v", err)
|
||||
}
|
||||
|
||||
// set an acceptable value for random key, expect no error
|
||||
r.Set(WithEntry(key, randomString(100)))
|
||||
require.NoError(t, SignV4(&r, privkey))
|
||||
require.NoError(t, signTest([]byte{5}, &r))
|
||||
}
|
||||
|
||||
// TestSignEncodeAndDecodeRandom tests encoding/decoding of records containing random key/value pairs.
|
||||
|
@ -295,7 +232,7 @@ func TestSignEncodeAndDecodeRandom(t *testing.T) {
|
|||
r.Set(WithEntry(key, &value))
|
||||
}
|
||||
|
||||
require.NoError(t, SignV4(&r, privkey))
|
||||
require.NoError(t, signTest([]byte{5}, &r))
|
||||
_, err := rlp.EncodeToBytes(r)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -308,11 +245,40 @@ func TestSignEncodeAndDecodeRandom(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecode(b *testing.B) {
|
||||
var r Record
|
||||
for i := 0; i < b.N; i++ {
|
||||
rlp.DecodeBytes(pyRecord, &r)
|
||||
}
|
||||
b.StopTimer()
|
||||
r.NodeAddr()
|
||||
type testSig struct{}
|
||||
|
||||
type testID []byte
|
||||
|
||||
func (id testID) ENRKey() string { return "testid" }
|
||||
|
||||
func signTest(id []byte, r *Record) error {
|
||||
r.Set(ID("test"))
|
||||
r.Set(testID(id))
|
||||
return r.SetSig(testSig{}, makeTestSig(id, r.Seq()))
|
||||
}
|
||||
|
||||
func makeTestSig(id []byte, seq uint64) []byte {
|
||||
sig := make([]byte, 8, len(id)+8)
|
||||
binary.BigEndian.PutUint64(sig[:8], seq)
|
||||
sig = append(sig, id...)
|
||||
return sig
|
||||
}
|
||||
|
||||
func (testSig) Verify(r *Record, sig []byte) error {
|
||||
var id []byte
|
||||
if err := r.Load((*testID)(&id)); err != nil {
|
||||
return err
|
||||
}
|
||||
if !bytes.Equal(sig, makeTestSig(id, r.Seq())) {
|
||||
return ErrInvalidSig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (testSig) NodeAddr(r *Record) []byte {
|
||||
var id []byte
|
||||
if err := r.Load((*testID)(&id)); err != nil {
|
||||
return nil
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
|
|
@ -17,12 +17,10 @@
|
|||
package enr
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
|
@ -98,30 +96,6 @@ func (v *IP) DecodeRLP(s *rlp.Stream) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Secp256k1 is the "secp256k1" key, which holds a public key.
|
||||
type Secp256k1 ecdsa.PublicKey
|
||||
|
||||
func (v Secp256k1) ENRKey() string { return "secp256k1" }
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
func (v Secp256k1) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, crypto.CompressPubkey((*ecdsa.PublicKey)(&v)))
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder.
|
||||
func (v *Secp256k1) DecodeRLP(s *rlp.Stream) error {
|
||||
buf, err := s.Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pk, err := crypto.DecompressPubkey(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = (Secp256k1)(*pk)
|
||||
return nil
|
||||
}
|
||||
|
||||
// KeyError is an error related to a key.
|
||||
type KeyError struct {
|
||||
Key string
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package enr
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Checks that failure to sign leaves the record unmodified.
|
||||
func TestSignError(t *testing.T) {
|
||||
invalidKey := &ecdsa.PrivateKey{D: new(big.Int), PublicKey: *pubkey}
|
||||
|
||||
var r Record
|
||||
if err := SignV4(&r, invalidKey); err == nil {
|
||||
t.Fatal("expected error from SignV4")
|
||||
}
|
||||
if len(r.pairs) > 0 {
|
||||
t.Fatal("expected empty record, have", r.pairs)
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
|
@ -253,13 +253,13 @@ type msgEventer struct {
|
|||
MsgReadWriter
|
||||
|
||||
feed *event.Feed
|
||||
peerID discover.NodeID
|
||||
peerID enode.ID
|
||||
Protocol string
|
||||
}
|
||||
|
||||
// newMsgEventer returns a msgEventer which sends message events to the given
|
||||
// feed
|
||||
func newMsgEventer(rw MsgReadWriter, feed *event.Feed, peerID discover.NodeID, proto string) *msgEventer {
|
||||
func newMsgEventer(rw MsgReadWriter, feed *event.Feed, peerID enode.ID, proto string) *msgEventer {
|
||||
return &msgEventer{
|
||||
MsgReadWriter: rw,
|
||||
feed: feed,
|
||||
|
|
36
p2p/peer.go
36
p2p/peer.go
|
@ -28,7 +28,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common/mclock"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
|
@ -60,7 +61,7 @@ type protoHandshake struct {
|
|||
Name string
|
||||
Caps []Cap
|
||||
ListenPort uint64
|
||||
ID discover.NodeID
|
||||
ID []byte // secp256k1 public key
|
||||
|
||||
// Ignore additional fields (for forward compatibility).
|
||||
Rest []rlp.RawValue `rlp:"tail"`
|
||||
|
@ -90,12 +91,12 @@ const (
|
|||
// PeerEvent is an event emitted when peers are either added or dropped from
|
||||
// a p2p.Server or when a message is sent or received on a peer connection
|
||||
type PeerEvent struct {
|
||||
Type PeerEventType `json:"type"`
|
||||
Peer discover.NodeID `json:"peer"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Protocol string `json:"protocol,omitempty"`
|
||||
MsgCode *uint64 `json:"msg_code,omitempty"`
|
||||
MsgSize *uint32 `json:"msg_size,omitempty"`
|
||||
Type PeerEventType `json:"type"`
|
||||
Peer enode.ID `json:"peer"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Protocol string `json:"protocol,omitempty"`
|
||||
MsgCode *uint64 `json:"msg_code,omitempty"`
|
||||
MsgSize *uint32 `json:"msg_size,omitempty"`
|
||||
}
|
||||
|
||||
// Peer represents a connected remote node.
|
||||
|
@ -115,17 +116,23 @@ type Peer struct {
|
|||
}
|
||||
|
||||
// NewPeer returns a peer for testing purposes.
|
||||
func NewPeer(id discover.NodeID, name string, caps []Cap) *Peer {
|
||||
func NewPeer(id enode.ID, name string, caps []Cap) *Peer {
|
||||
pipe, _ := net.Pipe()
|
||||
conn := &conn{fd: pipe, transport: nil, id: id, caps: caps, name: name}
|
||||
node := enode.SignNull(new(enr.Record), id)
|
||||
conn := &conn{fd: pipe, transport: nil, node: node, caps: caps, name: name}
|
||||
peer := newPeer(conn, nil)
|
||||
close(peer.closed) // ensures Disconnect doesn't block
|
||||
return peer
|
||||
}
|
||||
|
||||
// ID returns the node's public key.
|
||||
func (p *Peer) ID() discover.NodeID {
|
||||
return p.rw.id
|
||||
func (p *Peer) ID() enode.ID {
|
||||
return p.rw.node.ID()
|
||||
}
|
||||
|
||||
// Node returns the peer's node descriptor.
|
||||
func (p *Peer) Node() *enode.Node {
|
||||
return p.rw.node
|
||||
}
|
||||
|
||||
// Name returns the node name that the remote node advertised.
|
||||
|
@ -160,7 +167,8 @@ func (p *Peer) Disconnect(reason DiscReason) {
|
|||
|
||||
// String implements fmt.Stringer.
|
||||
func (p *Peer) String() string {
|
||||
return fmt.Sprintf("Peer %x %v", p.rw.id[:8], p.RemoteAddr())
|
||||
id := p.ID()
|
||||
return fmt.Sprintf("Peer %x %v", id[:8], p.RemoteAddr())
|
||||
}
|
||||
|
||||
// Inbound returns true if the peer is an inbound connection
|
||||
|
@ -177,7 +185,7 @@ func newPeer(conn *conn, protocols []Protocol) *Peer {
|
|||
disc: make(chan DiscReason),
|
||||
protoErr: make(chan error, len(protomap)+1), // protocols + pingLoop
|
||||
closed: make(chan struct{}),
|
||||
log: log.New("id", conn.id, "conn", conn.flags),
|
||||
log: log.New("id", conn.node.ID(), "conn", conn.flags),
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
|
|
@ -45,8 +45,8 @@ var discard = Protocol{
|
|||
|
||||
func testPeer(protos []Protocol) (func(), *conn, *Peer, <-chan error) {
|
||||
fd1, fd2 := net.Pipe()
|
||||
c1 := &conn{fd: fd1, transport: newTestTransport(randomID(), fd1)}
|
||||
c2 := &conn{fd: fd2, transport: newTestTransport(randomID(), fd2)}
|
||||
c1 := &conn{fd: fd1, node: newNode(randomID(), nil), transport: newTestTransport(&newkey().PublicKey, fd1)}
|
||||
c2 := &conn{fd: fd2, node: newNode(randomID(), nil), transport: newTestTransport(&newkey().PublicKey, fd2)}
|
||||
for _, p := range protos {
|
||||
c1.caps = append(c1.caps, p.cap())
|
||||
c2.caps = append(c2.caps, p.cap())
|
||||
|
|
|
@ -19,7 +19,7 @@ package p2p
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
)
|
||||
|
||||
// Protocol represents a P2P subprotocol implementation.
|
||||
|
@ -51,7 +51,7 @@ type Protocol struct {
|
|||
// PeerInfo is an optional helper method to retrieve protocol specific metadata
|
||||
// about a certain peer in the network. If an info retrieval function is set,
|
||||
// but returns nil, it is assumed that the protocol handshake is still running.
|
||||
PeerInfo func(id discover.NodeID) interface{}
|
||||
PeerInfo func(id enode.ID) interface{}
|
||||
}
|
||||
|
||||
func (p Protocol) cap() Cap {
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
|
||||
)
|
||||
|
@ -36,7 +36,7 @@ type hs0 struct {
|
|||
|
||||
// message to kill/drop the peer with nodeID
|
||||
type kill struct {
|
||||
C discover.NodeID
|
||||
C enode.ID
|
||||
}
|
||||
|
||||
// message to drop connection
|
||||
|
@ -144,7 +144,7 @@ func protocolTester(t *testing.T, pp *p2ptest.TestPeerPool) *p2ptest.ProtocolTes
|
|||
return p2ptest.NewProtocolTester(t, conf.ID, 2, newProtocol(pp))
|
||||
}
|
||||
|
||||
func protoHandshakeExchange(id discover.NodeID, proto *protoHandshake) []p2ptest.Exchange {
|
||||
func protoHandshakeExchange(id enode.ID, proto *protoHandshake) []p2ptest.Exchange {
|
||||
|
||||
return []p2ptest.Exchange{
|
||||
{
|
||||
|
@ -172,13 +172,13 @@ func runProtoHandshake(t *testing.T, proto *protoHandshake, errs ...error) {
|
|||
pp := p2ptest.NewTestPeerPool()
|
||||
s := protocolTester(t, pp)
|
||||
// TODO: make this more than one handshake
|
||||
id := s.IDs[0]
|
||||
if err := s.TestExchanges(protoHandshakeExchange(id, proto)...); err != nil {
|
||||
node := s.Nodes[0]
|
||||
if err := s.TestExchanges(protoHandshakeExchange(node.ID(), proto)...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var disconnects []*p2ptest.Disconnect
|
||||
for i, err := range errs {
|
||||
disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.IDs[i], Error: err})
|
||||
disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.Nodes[i].ID(), Error: err})
|
||||
}
|
||||
if err := s.TestDisconnected(disconnects...); err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -197,7 +197,7 @@ func TestProtoHandshakeSuccess(t *testing.T) {
|
|||
runProtoHandshake(t, &protoHandshake{42, "420"})
|
||||
}
|
||||
|
||||
func moduleHandshakeExchange(id discover.NodeID, resp uint) []p2ptest.Exchange {
|
||||
func moduleHandshakeExchange(id enode.ID, resp uint) []p2ptest.Exchange {
|
||||
|
||||
return []p2ptest.Exchange{
|
||||
{
|
||||
|
@ -224,16 +224,16 @@ func moduleHandshakeExchange(id discover.NodeID, resp uint) []p2ptest.Exchange {
|
|||
func runModuleHandshake(t *testing.T, resp uint, errs ...error) {
|
||||
pp := p2ptest.NewTestPeerPool()
|
||||
s := protocolTester(t, pp)
|
||||
id := s.IDs[0]
|
||||
if err := s.TestExchanges(protoHandshakeExchange(id, &protoHandshake{42, "420"})...); err != nil {
|
||||
node := s.Nodes[0]
|
||||
if err := s.TestExchanges(protoHandshakeExchange(node.ID(), &protoHandshake{42, "420"})...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := s.TestExchanges(moduleHandshakeExchange(id, resp)...); err != nil {
|
||||
if err := s.TestExchanges(moduleHandshakeExchange(node.ID(), resp)...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var disconnects []*p2ptest.Disconnect
|
||||
for i, err := range errs {
|
||||
disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.IDs[i], Error: err})
|
||||
disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.Nodes[i].ID(), Error: err})
|
||||
}
|
||||
if err := s.TestDisconnected(disconnects...); err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -249,7 +249,7 @@ func TestModuleHandshakeSuccess(t *testing.T) {
|
|||
}
|
||||
|
||||
// testing complex interactions over multiple peers, relaying, dropping
|
||||
func testMultiPeerSetup(a, b discover.NodeID) []p2ptest.Exchange {
|
||||
func testMultiPeerSetup(a, b enode.ID) []p2ptest.Exchange {
|
||||
|
||||
return []p2ptest.Exchange{
|
||||
{
|
||||
|
@ -305,7 +305,7 @@ func runMultiplePeers(t *testing.T, peer int, errs ...error) {
|
|||
pp := p2ptest.NewTestPeerPool()
|
||||
s := protocolTester(t, pp)
|
||||
|
||||
if err := s.TestExchanges(testMultiPeerSetup(s.IDs[0], s.IDs[1])...); err != nil {
|
||||
if err := s.TestExchanges(testMultiPeerSetup(s.Nodes[0].ID(), s.Nodes[1].ID())...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// after some exchanges of messages, we can test state changes
|
||||
|
@ -318,15 +318,15 @@ WAIT:
|
|||
for {
|
||||
select {
|
||||
case <-tick.C:
|
||||
if pp.Has(s.IDs[0]) {
|
||||
if pp.Has(s.Nodes[0].ID()) {
|
||||
break WAIT
|
||||
}
|
||||
case <-timeout.C:
|
||||
t.Fatal("timeout")
|
||||
}
|
||||
}
|
||||
if !pp.Has(s.IDs[1]) {
|
||||
t.Fatalf("missing peer test-1: %v (%v)", pp, s.IDs)
|
||||
if !pp.Has(s.Nodes[1].ID()) {
|
||||
t.Fatalf("missing peer test-1: %v (%v)", pp, s.Nodes)
|
||||
}
|
||||
|
||||
// peer 0 sends kill request for peer with index <peer>
|
||||
|
@ -334,8 +334,8 @@ WAIT:
|
|||
Triggers: []p2ptest.Trigger{
|
||||
{
|
||||
Code: 2,
|
||||
Msg: &kill{s.IDs[peer]},
|
||||
Peer: s.IDs[0],
|
||||
Msg: &kill{s.Nodes[peer].ID()},
|
||||
Peer: s.Nodes[0].ID(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -350,7 +350,7 @@ WAIT:
|
|||
{
|
||||
Code: 3,
|
||||
Msg: &drop{},
|
||||
Peer: s.IDs[(peer+1)%2],
|
||||
Peer: s.Nodes[(peer+1)%2].ID(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -362,14 +362,14 @@ WAIT:
|
|||
// check the actual discconnect errors on the individual peers
|
||||
var disconnects []*p2ptest.Disconnect
|
||||
for i, err := range errs {
|
||||
disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.IDs[i], Error: err})
|
||||
disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.Nodes[i].ID(), Error: err})
|
||||
}
|
||||
if err := s.TestDisconnected(disconnects...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// test if disconnected peers have been removed from peerPool
|
||||
if pp.Has(s.IDs[peer]) {
|
||||
t.Fatalf("peer test-%v not dropped: %v (%v)", peer, pp, s.IDs)
|
||||
if pp.Has(s.Nodes[peer].ID()) {
|
||||
t.Fatalf("peer test-%v not dropped: %v (%v)", peer, pp, s.Nodes)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
55
p2p/rlpx.go
55
p2p/rlpx.go
|
@ -35,11 +35,11 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/bitutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/ecies"
|
||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/golang/snappy"
|
||||
)
|
||||
|
@ -165,7 +165,7 @@ func readProtocolHandshake(rw MsgReader, our *protoHandshake) (*protoHandshake,
|
|||
if err := msg.Decode(&hs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if (hs.ID == discover.NodeID{}) {
|
||||
if len(hs.ID) != 64 || !bitutil.TestBytes(hs.ID) {
|
||||
return nil, DiscInvalidIdentity
|
||||
}
|
||||
return &hs, nil
|
||||
|
@ -175,7 +175,7 @@ func readProtocolHandshake(rw MsgReader, our *protoHandshake) (*protoHandshake,
|
|||
// messages. the protocol handshake is the first authenticated message
|
||||
// and also verifies whether the encryption handshake 'worked' and the
|
||||
// remote side actually provided the right public key.
|
||||
func (t *rlpx) doEncHandshake(prv *ecdsa.PrivateKey, dial *discover.Node) (discover.NodeID, error) {
|
||||
func (t *rlpx) doEncHandshake(prv *ecdsa.PrivateKey, dial *ecdsa.PublicKey) (*ecdsa.PublicKey, error) {
|
||||
var (
|
||||
sec secrets
|
||||
err error
|
||||
|
@ -183,23 +183,21 @@ func (t *rlpx) doEncHandshake(prv *ecdsa.PrivateKey, dial *discover.Node) (disco
|
|||
if dial == nil {
|
||||
sec, err = receiverEncHandshake(t.fd, prv)
|
||||
} else {
|
||||
sec, err = initiatorEncHandshake(t.fd, prv, dial.ID)
|
||||
sec, err = initiatorEncHandshake(t.fd, prv, dial)
|
||||
}
|
||||
if err != nil {
|
||||
return discover.NodeID{}, err
|
||||
return nil, err
|
||||
}
|
||||
t.wmu.Lock()
|
||||
t.rw = newRLPXFrameRW(t.fd, sec)
|
||||
t.wmu.Unlock()
|
||||
return sec.RemoteID, nil
|
||||
return sec.Remote.ExportECDSA(), nil
|
||||
}
|
||||
|
||||
// encHandshake contains the state of the encryption handshake.
|
||||
type encHandshake struct {
|
||||
initiator bool
|
||||
remoteID discover.NodeID
|
||||
|
||||
remotePub *ecies.PublicKey // remote-pubk
|
||||
initiator bool
|
||||
remote *ecies.PublicKey // remote-pubk
|
||||
initNonce, respNonce []byte // nonce
|
||||
randomPrivKey *ecies.PrivateKey // ecdhe-random
|
||||
remoteRandomPub *ecies.PublicKey // ecdhe-random-pubk
|
||||
|
@ -208,7 +206,7 @@ type encHandshake struct {
|
|||
// secrets represents the connection secrets
|
||||
// which are negotiated during the encryption handshake.
|
||||
type secrets struct {
|
||||
RemoteID discover.NodeID
|
||||
Remote *ecies.PublicKey
|
||||
AES, MAC []byte
|
||||
EgressMAC, IngressMAC hash.Hash
|
||||
Token []byte
|
||||
|
@ -249,9 +247,9 @@ func (h *encHandshake) secrets(auth, authResp []byte) (secrets, error) {
|
|||
sharedSecret := crypto.Keccak256(ecdheSecret, crypto.Keccak256(h.respNonce, h.initNonce))
|
||||
aesSecret := crypto.Keccak256(ecdheSecret, sharedSecret)
|
||||
s := secrets{
|
||||
RemoteID: h.remoteID,
|
||||
AES: aesSecret,
|
||||
MAC: crypto.Keccak256(ecdheSecret, aesSecret),
|
||||
Remote: h.remote,
|
||||
AES: aesSecret,
|
||||
MAC: crypto.Keccak256(ecdheSecret, aesSecret),
|
||||
}
|
||||
|
||||
// setup sha3 instances for the MACs
|
||||
|
@ -273,15 +271,15 @@ func (h *encHandshake) secrets(auth, authResp []byte) (secrets, error) {
|
|||
// staticSharedSecret returns the static shared secret, the result
|
||||
// of key agreement between the local and remote static node key.
|
||||
func (h *encHandshake) staticSharedSecret(prv *ecdsa.PrivateKey) ([]byte, error) {
|
||||
return ecies.ImportECDSA(prv).GenerateShared(h.remotePub, sskLen, sskLen)
|
||||
return ecies.ImportECDSA(prv).GenerateShared(h.remote, sskLen, sskLen)
|
||||
}
|
||||
|
||||
// initiatorEncHandshake negotiates a session token on conn.
|
||||
// it should be called on the dialing side of the connection.
|
||||
//
|
||||
// prv is the local client's private key.
|
||||
func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remoteID discover.NodeID) (s secrets, err error) {
|
||||
h := &encHandshake{initiator: true, remoteID: remoteID}
|
||||
func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remote *ecdsa.PublicKey) (s secrets, err error) {
|
||||
h := &encHandshake{initiator: true, remote: ecies.ImportECDSAPublic(remote)}
|
||||
authMsg, err := h.makeAuthMsg(prv)
|
||||
if err != nil {
|
||||
return s, err
|
||||
|
@ -307,14 +305,10 @@ func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remoteID d
|
|||
|
||||
// makeAuthMsg creates the initiator handshake message.
|
||||
func (h *encHandshake) makeAuthMsg(prv *ecdsa.PrivateKey) (*authMsgV4, error) {
|
||||
rpub, err := h.remoteID.Pubkey()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("bad remoteID: %v", err)
|
||||
}
|
||||
h.remotePub = ecies.ImportECDSAPublic(rpub)
|
||||
// Generate random initiator nonce.
|
||||
h.initNonce = make([]byte, shaLen)
|
||||
if _, err := rand.Read(h.initNonce); err != nil {
|
||||
_, err := rand.Read(h.initNonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Generate random keypair to for ECDH.
|
||||
|
@ -384,13 +378,12 @@ func receiverEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey) (s secrets,
|
|||
|
||||
func (h *encHandshake) handleAuthMsg(msg *authMsgV4, prv *ecdsa.PrivateKey) error {
|
||||
// Import the remote identity.
|
||||
h.initNonce = msg.Nonce[:]
|
||||
h.remoteID = msg.InitiatorPubkey
|
||||
rpub, err := h.remoteID.Pubkey()
|
||||
rpub, err := importPublicKey(msg.InitiatorPubkey[:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("bad remoteID: %#v", err)
|
||||
return err
|
||||
}
|
||||
h.remotePub = ecies.ImportECDSAPublic(rpub)
|
||||
h.initNonce = msg.Nonce[:]
|
||||
h.remote = rpub
|
||||
|
||||
// Generate random keypair for ECDH.
|
||||
// If a private key is already set, use it instead of generating one (for testing).
|
||||
|
@ -436,7 +429,7 @@ func (msg *authMsgV4) sealPlain(h *encHandshake) ([]byte, error) {
|
|||
n += copy(buf[n:], msg.InitiatorPubkey[:])
|
||||
n += copy(buf[n:], msg.Nonce[:])
|
||||
buf[n] = 0 // token-flag
|
||||
return ecies.Encrypt(rand.Reader, h.remotePub, buf, nil, nil)
|
||||
return ecies.Encrypt(rand.Reader, h.remote, buf, nil, nil)
|
||||
}
|
||||
|
||||
func (msg *authMsgV4) decodePlain(input []byte) {
|
||||
|
@ -452,7 +445,7 @@ func (msg *authRespV4) sealPlain(hs *encHandshake) ([]byte, error) {
|
|||
buf := make([]byte, authRespLen)
|
||||
n := copy(buf, msg.RandomPubkey[:])
|
||||
copy(buf[n:], msg.Nonce[:])
|
||||
return ecies.Encrypt(rand.Reader, hs.remotePub, buf, nil, nil)
|
||||
return ecies.Encrypt(rand.Reader, hs.remote, buf, nil, nil)
|
||||
}
|
||||
|
||||
func (msg *authRespV4) decodePlain(input []byte) {
|
||||
|
@ -475,7 +468,7 @@ func sealEIP8(msg interface{}, h *encHandshake) ([]byte, error) {
|
|||
prefix := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(prefix, uint16(buf.Len()+eciesOverhead))
|
||||
|
||||
enc, err := ecies.Encrypt(rand.Reader, h.remotePub, buf.Bytes(), nil, prefix)
|
||||
enc, err := ecies.Encrypt(rand.Reader, h.remote, buf.Bytes(), nil, prefix)
|
||||
return append(prefix, enc...), err
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ package p2p
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -34,7 +35,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/ecies"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/pipes"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
@ -80,9 +80,9 @@ func TestEncHandshake(t *testing.T) {
|
|||
|
||||
func testEncHandshake(token []byte) error {
|
||||
type result struct {
|
||||
side string
|
||||
id discover.NodeID
|
||||
err error
|
||||
side string
|
||||
pubkey *ecdsa.PublicKey
|
||||
err error
|
||||
}
|
||||
var (
|
||||
prv0, _ = crypto.GenerateKey()
|
||||
|
@ -97,14 +97,12 @@ func testEncHandshake(token []byte) error {
|
|||
defer func() { output <- r }()
|
||||
defer fd0.Close()
|
||||
|
||||
dest := &discover.Node{ID: discover.PubkeyID(&prv1.PublicKey)}
|
||||
r.id, r.err = c0.doEncHandshake(prv0, dest)
|
||||
r.pubkey, r.err = c0.doEncHandshake(prv0, &prv1.PublicKey)
|
||||
if r.err != nil {
|
||||
return
|
||||
}
|
||||
id1 := discover.PubkeyID(&prv1.PublicKey)
|
||||
if r.id != id1 {
|
||||
r.err = fmt.Errorf("remote ID mismatch: got %v, want: %v", r.id, id1)
|
||||
if !reflect.DeepEqual(r.pubkey, &prv1.PublicKey) {
|
||||
r.err = fmt.Errorf("remote pubkey mismatch: got %v, want: %v", r.pubkey, &prv1.PublicKey)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
|
@ -112,13 +110,12 @@ func testEncHandshake(token []byte) error {
|
|||
defer func() { output <- r }()
|
||||
defer fd1.Close()
|
||||
|
||||
r.id, r.err = c1.doEncHandshake(prv1, nil)
|
||||
r.pubkey, r.err = c1.doEncHandshake(prv1, nil)
|
||||
if r.err != nil {
|
||||
return
|
||||
}
|
||||
id0 := discover.PubkeyID(&prv0.PublicKey)
|
||||
if r.id != id0 {
|
||||
r.err = fmt.Errorf("remote ID mismatch: got %v, want: %v", r.id, id0)
|
||||
if !reflect.DeepEqual(r.pubkey, &prv0.PublicKey) {
|
||||
r.err = fmt.Errorf("remote ID mismatch: got %v, want: %v", r.pubkey, &prv0.PublicKey)
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -150,12 +147,12 @@ func testEncHandshake(token []byte) error {
|
|||
func TestProtocolHandshake(t *testing.T) {
|
||||
var (
|
||||
prv0, _ = crypto.GenerateKey()
|
||||
node0 = &discover.Node{ID: discover.PubkeyID(&prv0.PublicKey), IP: net.IP{1, 2, 3, 4}, TCP: 33}
|
||||
hs0 = &protoHandshake{Version: 3, ID: node0.ID, Caps: []Cap{{"a", 0}, {"b", 2}}}
|
||||
pub0 = crypto.FromECDSAPub(&prv0.PublicKey)[1:]
|
||||
hs0 = &protoHandshake{Version: 3, ID: pub0, Caps: []Cap{{"a", 0}, {"b", 2}}}
|
||||
|
||||
prv1, _ = crypto.GenerateKey()
|
||||
node1 = &discover.Node{ID: discover.PubkeyID(&prv1.PublicKey), IP: net.IP{5, 6, 7, 8}, TCP: 44}
|
||||
hs1 = &protoHandshake{Version: 3, ID: node1.ID, Caps: []Cap{{"c", 1}, {"d", 3}}}
|
||||
pub1 = crypto.FromECDSAPub(&prv1.PublicKey)[1:]
|
||||
hs1 = &protoHandshake{Version: 3, ID: pub1, Caps: []Cap{{"c", 1}, {"d", 3}}}
|
||||
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
|
@ -170,13 +167,13 @@ func TestProtocolHandshake(t *testing.T) {
|
|||
defer wg.Done()
|
||||
defer fd0.Close()
|
||||
rlpx := newRLPX(fd0)
|
||||
remid, err := rlpx.doEncHandshake(prv0, node1)
|
||||
rpubkey, err := rlpx.doEncHandshake(prv0, &prv1.PublicKey)
|
||||
if err != nil {
|
||||
t.Errorf("dial side enc handshake failed: %v", err)
|
||||
return
|
||||
}
|
||||
if remid != node1.ID {
|
||||
t.Errorf("dial side remote id mismatch: got %v, want %v", remid, node1.ID)
|
||||
if !reflect.DeepEqual(rpubkey, &prv1.PublicKey) {
|
||||
t.Errorf("dial side remote pubkey mismatch: got %v, want %v", rpubkey, &prv1.PublicKey)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -196,13 +193,13 @@ func TestProtocolHandshake(t *testing.T) {
|
|||
defer wg.Done()
|
||||
defer fd1.Close()
|
||||
rlpx := newRLPX(fd1)
|
||||
remid, err := rlpx.doEncHandshake(prv1, nil)
|
||||
rpubkey, err := rlpx.doEncHandshake(prv1, nil)
|
||||
if err != nil {
|
||||
t.Errorf("listen side enc handshake failed: %v", err)
|
||||
return
|
||||
}
|
||||
if remid != node0.ID {
|
||||
t.Errorf("listen side remote id mismatch: got %v, want %v", remid, node0.ID)
|
||||
if !reflect.DeepEqual(rpubkey, &prv0.PublicKey) {
|
||||
t.Errorf("listen side remote pubkey mismatch: got %v, want %v", rpubkey, &prv0.PublicKey)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
197
p2p/server.go
197
p2p/server.go
|
@ -18,6 +18,7 @@
|
|||
package p2p
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -28,10 +29,12 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/mclock"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
)
|
||||
|
@ -87,7 +90,7 @@ type Config struct {
|
|||
|
||||
// BootstrapNodes are used to establish connectivity
|
||||
// with the rest of the network.
|
||||
BootstrapNodes []*discover.Node
|
||||
BootstrapNodes []*enode.Node
|
||||
|
||||
// BootstrapNodesV5 are used to establish connectivity
|
||||
// with the rest of the network using the V5 discovery
|
||||
|
@ -96,11 +99,11 @@ type Config struct {
|
|||
|
||||
// Static nodes are used as pre-configured connections which are always
|
||||
// maintained and re-connected on disconnects.
|
||||
StaticNodes []*discover.Node
|
||||
StaticNodes []*enode.Node
|
||||
|
||||
// Trusted nodes are used as pre-configured connections which are always
|
||||
// allowed to connect, even above the peer limit.
|
||||
TrustedNodes []*discover.Node
|
||||
TrustedNodes []*enode.Node
|
||||
|
||||
// Connectivity can be restricted to certain IP networks.
|
||||
// If this option is set to a non-nil value, only hosts which match one of the
|
||||
|
@ -168,10 +171,10 @@ type Server struct {
|
|||
peerOpDone chan struct{}
|
||||
|
||||
quit chan struct{}
|
||||
addstatic chan *discover.Node
|
||||
removestatic chan *discover.Node
|
||||
addtrusted chan *discover.Node
|
||||
removetrusted chan *discover.Node
|
||||
addstatic chan *enode.Node
|
||||
removestatic chan *enode.Node
|
||||
addtrusted chan *enode.Node
|
||||
removetrusted chan *enode.Node
|
||||
posthandshake chan *conn
|
||||
addpeer chan *conn
|
||||
delpeer chan peerDrop
|
||||
|
@ -180,7 +183,7 @@ type Server struct {
|
|||
log log.Logger
|
||||
}
|
||||
|
||||
type peerOpFunc func(map[discover.NodeID]*Peer)
|
||||
type peerOpFunc func(map[enode.ID]*Peer)
|
||||
|
||||
type peerDrop struct {
|
||||
*Peer
|
||||
|
@ -202,16 +205,16 @@ const (
|
|||
type conn struct {
|
||||
fd net.Conn
|
||||
transport
|
||||
node *enode.Node
|
||||
flags connFlag
|
||||
cont chan error // The run loop uses cont to signal errors to SetupConn.
|
||||
id discover.NodeID // valid after the encryption handshake
|
||||
caps []Cap // valid after the protocol handshake
|
||||
name string // valid after the protocol handshake
|
||||
cont chan error // The run loop uses cont to signal errors to SetupConn.
|
||||
caps []Cap // valid after the protocol handshake
|
||||
name string // valid after the protocol handshake
|
||||
}
|
||||
|
||||
type transport interface {
|
||||
// The two handshakes.
|
||||
doEncHandshake(prv *ecdsa.PrivateKey, dialDest *discover.Node) (discover.NodeID, error)
|
||||
doEncHandshake(prv *ecdsa.PrivateKey, dialDest *ecdsa.PublicKey) (*ecdsa.PublicKey, error)
|
||||
doProtoHandshake(our *protoHandshake) (*protoHandshake, error)
|
||||
// The MsgReadWriter can only be used after the encryption
|
||||
// handshake has completed. The code uses conn.id to track this
|
||||
|
@ -225,8 +228,8 @@ type transport interface {
|
|||
|
||||
func (c *conn) String() string {
|
||||
s := c.flags.String()
|
||||
if (c.id != discover.NodeID{}) {
|
||||
s += " " + c.id.String()
|
||||
if (c.node.ID() != enode.ID{}) {
|
||||
s += " " + c.node.ID().String()
|
||||
}
|
||||
s += " " + c.fd.RemoteAddr().String()
|
||||
return s
|
||||
|
@ -279,7 +282,7 @@ func (srv *Server) Peers() []*Peer {
|
|||
// Note: We'd love to put this function into a variable but
|
||||
// that seems to cause a weird compiler error in some
|
||||
// environments.
|
||||
case srv.peerOp <- func(peers map[discover.NodeID]*Peer) {
|
||||
case srv.peerOp <- func(peers map[enode.ID]*Peer) {
|
||||
for _, p := range peers {
|
||||
ps = append(ps, p)
|
||||
}
|
||||
|
@ -294,7 +297,7 @@ func (srv *Server) Peers() []*Peer {
|
|||
func (srv *Server) PeerCount() int {
|
||||
var count int
|
||||
select {
|
||||
case srv.peerOp <- func(ps map[discover.NodeID]*Peer) { count = len(ps) }:
|
||||
case srv.peerOp <- func(ps map[enode.ID]*Peer) { count = len(ps) }:
|
||||
<-srv.peerOpDone
|
||||
case <-srv.quit:
|
||||
}
|
||||
|
@ -304,7 +307,7 @@ func (srv *Server) PeerCount() int {
|
|||
// AddPeer connects to the given node and maintains the connection until the
|
||||
// server is shut down. If the connection fails for any reason, the server will
|
||||
// attempt to reconnect the peer.
|
||||
func (srv *Server) AddPeer(node *discover.Node) {
|
||||
func (srv *Server) AddPeer(node *enode.Node) {
|
||||
select {
|
||||
case srv.addstatic <- node:
|
||||
case <-srv.quit:
|
||||
|
@ -312,7 +315,7 @@ func (srv *Server) AddPeer(node *discover.Node) {
|
|||
}
|
||||
|
||||
// RemovePeer disconnects from the given node
|
||||
func (srv *Server) RemovePeer(node *discover.Node) {
|
||||
func (srv *Server) RemovePeer(node *enode.Node) {
|
||||
select {
|
||||
case srv.removestatic <- node:
|
||||
case <-srv.quit:
|
||||
|
@ -321,7 +324,7 @@ func (srv *Server) RemovePeer(node *discover.Node) {
|
|||
|
||||
// AddTrustedPeer adds the given node to a reserved whitelist which allows the
|
||||
// node to always connect, even if the slot are full.
|
||||
func (srv *Server) AddTrustedPeer(node *discover.Node) {
|
||||
func (srv *Server) AddTrustedPeer(node *enode.Node) {
|
||||
select {
|
||||
case srv.addtrusted <- node:
|
||||
case <-srv.quit:
|
||||
|
@ -329,7 +332,7 @@ func (srv *Server) AddTrustedPeer(node *discover.Node) {
|
|||
}
|
||||
|
||||
// RemoveTrustedPeer removes the given node from the trusted peer set.
|
||||
func (srv *Server) RemoveTrustedPeer(node *discover.Node) {
|
||||
func (srv *Server) RemoveTrustedPeer(node *enode.Node) {
|
||||
select {
|
||||
case srv.removetrusted <- node:
|
||||
case <-srv.quit:
|
||||
|
@ -342,36 +345,47 @@ func (srv *Server) SubscribeEvents(ch chan *PeerEvent) event.Subscription {
|
|||
}
|
||||
|
||||
// Self returns the local node's endpoint information.
|
||||
func (srv *Server) Self() *discover.Node {
|
||||
func (srv *Server) Self() *enode.Node {
|
||||
srv.lock.Lock()
|
||||
defer srv.lock.Unlock()
|
||||
running, listener, ntab := srv.running, srv.listener, srv.ntab
|
||||
srv.lock.Unlock()
|
||||
|
||||
if !srv.running {
|
||||
return &discover.Node{IP: net.ParseIP("0.0.0.0")}
|
||||
if !running {
|
||||
return enode.NewV4(&srv.PrivateKey.PublicKey, net.ParseIP("0.0.0.0"), 0, 0)
|
||||
}
|
||||
return srv.makeSelf(srv.listener, srv.ntab)
|
||||
return srv.makeSelf(listener, ntab)
|
||||
}
|
||||
|
||||
func (srv *Server) makeSelf(listener net.Listener, ntab discoverTable) *discover.Node {
|
||||
// If the server's not running, return an empty node.
|
||||
func (srv *Server) makeSelf(listener net.Listener, ntab discoverTable) *enode.Node {
|
||||
// If the node is running but discovery is off, manually assemble the node infos.
|
||||
if ntab == nil {
|
||||
// Inbound connections disabled, use zero address.
|
||||
if listener == nil {
|
||||
return &discover.Node{IP: net.ParseIP("0.0.0.0"), ID: discover.PubkeyID(&srv.PrivateKey.PublicKey)}
|
||||
}
|
||||
// Otherwise inject the listener address too
|
||||
addr := listener.Addr().(*net.TCPAddr)
|
||||
return &discover.Node{
|
||||
ID: discover.PubkeyID(&srv.PrivateKey.PublicKey),
|
||||
IP: addr.IP,
|
||||
TCP: uint16(addr.Port),
|
||||
}
|
||||
addr := srv.tcpAddr(listener)
|
||||
return enode.NewV4(&srv.PrivateKey.PublicKey, addr.IP, addr.Port, 0)
|
||||
}
|
||||
// Otherwise return the discovery node.
|
||||
return ntab.Self()
|
||||
}
|
||||
|
||||
func (srv *Server) tcpAddr(listener net.Listener) net.TCPAddr {
|
||||
addr := net.TCPAddr{IP: net.IP{0, 0, 0, 0}}
|
||||
if listener == nil {
|
||||
return addr // Inbound connections disabled, use zero address.
|
||||
}
|
||||
// Otherwise inject the listener address too.
|
||||
if a, ok := listener.Addr().(*net.TCPAddr); ok {
|
||||
addr = *a
|
||||
}
|
||||
if srv.NAT != nil {
|
||||
if ip, err := srv.NAT.ExternalIP(); err == nil {
|
||||
addr.IP = ip
|
||||
}
|
||||
}
|
||||
if addr.IP.IsUnspecified() {
|
||||
addr.IP = net.IP{127, 0, 0, 1}
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
// Stop terminates the server and all active peer connections.
|
||||
// It blocks until all active connections have been closed.
|
||||
func (srv *Server) Stop() {
|
||||
|
@ -445,10 +459,10 @@ func (srv *Server) Start() (err error) {
|
|||
srv.addpeer = make(chan *conn)
|
||||
srv.delpeer = make(chan peerDrop)
|
||||
srv.posthandshake = make(chan *conn)
|
||||
srv.addstatic = make(chan *discover.Node)
|
||||
srv.removestatic = make(chan *discover.Node)
|
||||
srv.addtrusted = make(chan *discover.Node)
|
||||
srv.removetrusted = make(chan *discover.Node)
|
||||
srv.addstatic = make(chan *enode.Node)
|
||||
srv.removestatic = make(chan *enode.Node)
|
||||
srv.addtrusted = make(chan *enode.Node)
|
||||
srv.removetrusted = make(chan *enode.Node)
|
||||
srv.peerOp = make(chan peerOpFunc)
|
||||
srv.peerOpDone = make(chan struct{})
|
||||
|
||||
|
@ -525,7 +539,8 @@ func (srv *Server) Start() (err error) {
|
|||
dialer := newDialState(srv.StaticNodes, srv.BootstrapNodes, srv.ntab, dynPeers, srv.NetRestrict)
|
||||
|
||||
// handshake
|
||||
srv.ourHandshake = &protoHandshake{Version: baseProtocolVersion, Name: srv.Name, ID: discover.PubkeyID(&srv.PrivateKey.PublicKey)}
|
||||
pubkey := crypto.FromECDSAPub(&srv.PrivateKey.PublicKey)
|
||||
srv.ourHandshake = &protoHandshake{Version: baseProtocolVersion, Name: srv.Name, ID: pubkey[1:]}
|
||||
for _, p := range srv.Protocols {
|
||||
srv.ourHandshake.Caps = append(srv.ourHandshake.Caps, p.cap())
|
||||
}
|
||||
|
@ -541,7 +556,6 @@ func (srv *Server) Start() (err error) {
|
|||
|
||||
srv.loopWG.Add(1)
|
||||
go srv.run(dialer)
|
||||
srv.running = true
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -568,18 +582,18 @@ func (srv *Server) startListening() error {
|
|||
}
|
||||
|
||||
type dialer interface {
|
||||
newTasks(running int, peers map[discover.NodeID]*Peer, now time.Time) []task
|
||||
newTasks(running int, peers map[enode.ID]*Peer, now time.Time) []task
|
||||
taskDone(task, time.Time)
|
||||
addStatic(*discover.Node)
|
||||
removeStatic(*discover.Node)
|
||||
addStatic(*enode.Node)
|
||||
removeStatic(*enode.Node)
|
||||
}
|
||||
|
||||
func (srv *Server) run(dialstate dialer) {
|
||||
defer srv.loopWG.Done()
|
||||
var (
|
||||
peers = make(map[discover.NodeID]*Peer)
|
||||
peers = make(map[enode.ID]*Peer)
|
||||
inboundCount = 0
|
||||
trusted = make(map[discover.NodeID]bool, len(srv.TrustedNodes))
|
||||
trusted = make(map[enode.ID]bool, len(srv.TrustedNodes))
|
||||
taskdone = make(chan task, maxActiveDialTasks)
|
||||
runningTasks []task
|
||||
queuedTasks []task // tasks that can't run yet
|
||||
|
@ -587,7 +601,7 @@ func (srv *Server) run(dialstate dialer) {
|
|||
// Put trusted nodes into a map to speed up checks.
|
||||
// Trusted peers are loaded on startup or added via AddTrustedPeer RPC.
|
||||
for _, n := range srv.TrustedNodes {
|
||||
trusted[n.ID] = true
|
||||
trusted[n.ID()] = true
|
||||
}
|
||||
|
||||
// removes t from runningTasks
|
||||
|
@ -640,27 +654,27 @@ running:
|
|||
// stop keeping the node connected.
|
||||
srv.log.Trace("Removing static node", "node", n)
|
||||
dialstate.removeStatic(n)
|
||||
if p, ok := peers[n.ID]; ok {
|
||||
if p, ok := peers[n.ID()]; ok {
|
||||
p.Disconnect(DiscRequested)
|
||||
}
|
||||
case n := <-srv.addtrusted:
|
||||
// This channel is used by AddTrustedPeer to add an enode
|
||||
// to the trusted node set.
|
||||
srv.log.Trace("Adding trusted node", "node", n)
|
||||
trusted[n.ID] = true
|
||||
trusted[n.ID()] = true
|
||||
// Mark any already-connected peer as trusted
|
||||
if p, ok := peers[n.ID]; ok {
|
||||
if p, ok := peers[n.ID()]; ok {
|
||||
p.rw.set(trustedConn, true)
|
||||
}
|
||||
case n := <-srv.removetrusted:
|
||||
// This channel is used by RemoveTrustedPeer to remove an enode
|
||||
// from the trusted node set.
|
||||
srv.log.Trace("Removing trusted node", "node", n)
|
||||
if _, ok := trusted[n.ID]; ok {
|
||||
delete(trusted, n.ID)
|
||||
if _, ok := trusted[n.ID()]; ok {
|
||||
delete(trusted, n.ID())
|
||||
}
|
||||
// Unmark any already-connected peer as trusted
|
||||
if p, ok := peers[n.ID]; ok {
|
||||
if p, ok := peers[n.ID()]; ok {
|
||||
p.rw.set(trustedConn, false)
|
||||
}
|
||||
case op := <-srv.peerOp:
|
||||
|
@ -677,7 +691,7 @@ running:
|
|||
case c := <-srv.posthandshake:
|
||||
// A connection has passed the encryption handshake so
|
||||
// the remote identity is known (but hasn't been verified yet).
|
||||
if trusted[c.id] {
|
||||
if trusted[c.node.ID()] {
|
||||
// Ensure that the trusted flag is set before checking against MaxPeers.
|
||||
c.flags |= trustedConn
|
||||
}
|
||||
|
@ -702,7 +716,7 @@ running:
|
|||
name := truncateName(c.name)
|
||||
srv.log.Debug("Adding p2p peer", "name", name, "addr", c.fd.RemoteAddr(), "peers", len(peers)+1)
|
||||
go srv.runPeer(p)
|
||||
peers[c.id] = p
|
||||
peers[c.node.ID()] = p
|
||||
if p.Inbound() {
|
||||
inboundCount++
|
||||
}
|
||||
|
@ -749,7 +763,7 @@ running:
|
|||
}
|
||||
}
|
||||
|
||||
func (srv *Server) protoHandshakeChecks(peers map[discover.NodeID]*Peer, inboundCount int, c *conn) error {
|
||||
func (srv *Server) protoHandshakeChecks(peers map[enode.ID]*Peer, inboundCount int, c *conn) error {
|
||||
// Drop connections with no matching protocols.
|
||||
if len(srv.Protocols) > 0 && countMatchingProtocols(srv.Protocols, c.caps) == 0 {
|
||||
return DiscUselessPeer
|
||||
|
@ -759,15 +773,15 @@ func (srv *Server) protoHandshakeChecks(peers map[discover.NodeID]*Peer, inbound
|
|||
return srv.encHandshakeChecks(peers, inboundCount, c)
|
||||
}
|
||||
|
||||
func (srv *Server) encHandshakeChecks(peers map[discover.NodeID]*Peer, inboundCount int, c *conn) error {
|
||||
func (srv *Server) encHandshakeChecks(peers map[enode.ID]*Peer, inboundCount int, c *conn) error {
|
||||
switch {
|
||||
case !c.is(trustedConn|staticDialedConn) && len(peers) >= srv.MaxPeers:
|
||||
return DiscTooManyPeers
|
||||
case !c.is(trustedConn) && c.is(inboundConn) && inboundCount >= srv.maxInboundConns():
|
||||
return DiscTooManyPeers
|
||||
case peers[c.id] != nil:
|
||||
case peers[c.node.ID()] != nil:
|
||||
return DiscAlreadyConnected
|
||||
case c.id == srv.Self().ID:
|
||||
case c.node.ID() == srv.Self().ID():
|
||||
return DiscSelf
|
||||
default:
|
||||
return nil
|
||||
|
@ -777,7 +791,6 @@ func (srv *Server) encHandshakeChecks(peers map[discover.NodeID]*Peer, inboundCo
|
|||
func (srv *Server) maxInboundConns() int {
|
||||
return srv.MaxPeers - srv.maxDialedConns()
|
||||
}
|
||||
|
||||
func (srv *Server) maxDialedConns() int {
|
||||
if srv.NoDiscovery || srv.NoDial {
|
||||
return 0
|
||||
|
@ -797,7 +810,7 @@ type tempError interface {
|
|||
// inbound connections.
|
||||
func (srv *Server) listenLoop() {
|
||||
defer srv.loopWG.Done()
|
||||
srv.log.Info("RLPx listener up", "self", srv.makeSelf(srv.listener, srv.ntab))
|
||||
srv.log.Info("RLPx listener up", "self", srv.Self())
|
||||
|
||||
tokens := defaultMaxPendingPeers
|
||||
if srv.MaxPendingPeers > 0 {
|
||||
|
@ -850,7 +863,7 @@ func (srv *Server) listenLoop() {
|
|||
// SetupConn runs the handshakes and attempts to add the connection
|
||||
// as a peer. It returns when the connection has been added as a peer
|
||||
// or the handshakes have failed.
|
||||
func (srv *Server) SetupConn(fd net.Conn, flags connFlag, dialDest *discover.Node) error {
|
||||
func (srv *Server) SetupConn(fd net.Conn, flags connFlag, dialDest *enode.Node) error {
|
||||
self := srv.Self()
|
||||
if self == nil {
|
||||
return errors.New("shutdown")
|
||||
|
@ -859,12 +872,12 @@ func (srv *Server) SetupConn(fd net.Conn, flags connFlag, dialDest *discover.Nod
|
|||
err := srv.setupConn(c, flags, dialDest)
|
||||
if err != nil {
|
||||
c.close(err)
|
||||
srv.log.Trace("Setting up connection failed", "id", c.id, "err", err)
|
||||
srv.log.Trace("Setting up connection failed", "addr", fd.RemoteAddr(), "err", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *discover.Node) error {
|
||||
func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *enode.Node) error {
|
||||
// Prevent leftover pending conns from entering the handshake.
|
||||
srv.lock.Lock()
|
||||
running := srv.running
|
||||
|
@ -872,18 +885,30 @@ func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *discover.Node) e
|
|||
if !running {
|
||||
return errServerStopped
|
||||
}
|
||||
// If dialing, figure out the remote public key.
|
||||
var dialPubkey *ecdsa.PublicKey
|
||||
if dialDest != nil {
|
||||
dialPubkey = new(ecdsa.PublicKey)
|
||||
if err := dialDest.Load((*enode.Secp256k1)(dialPubkey)); err != nil {
|
||||
return fmt.Errorf("dial destination doesn't have a secp256k1 public key")
|
||||
}
|
||||
}
|
||||
// Run the encryption handshake.
|
||||
var err error
|
||||
if c.id, err = c.doEncHandshake(srv.PrivateKey, dialDest); err != nil {
|
||||
remotePubkey, err := c.doEncHandshake(srv.PrivateKey, dialPubkey)
|
||||
if err != nil {
|
||||
srv.log.Trace("Failed RLPx handshake", "addr", c.fd.RemoteAddr(), "conn", c.flags, "err", err)
|
||||
return err
|
||||
}
|
||||
clog := srv.log.New("id", c.id, "addr", c.fd.RemoteAddr(), "conn", c.flags)
|
||||
// For dialed connections, check that the remote public key matches.
|
||||
if dialDest != nil && c.id != dialDest.ID {
|
||||
clog.Trace("Dialed identity mismatch", "want", c, dialDest.ID)
|
||||
return DiscUnexpectedIdentity
|
||||
if dialDest != nil {
|
||||
// For dialed connections, check that the remote public key matches.
|
||||
if dialPubkey.X.Cmp(remotePubkey.X) != 0 || dialPubkey.Y.Cmp(remotePubkey.Y) != 0 {
|
||||
return DiscUnexpectedIdentity
|
||||
}
|
||||
c.node = dialDest
|
||||
} else {
|
||||
c.node = nodeFromConn(remotePubkey, c.fd)
|
||||
}
|
||||
clog := srv.log.New("id", c.node.ID(), "addr", c.fd.RemoteAddr(), "conn", c.flags)
|
||||
err = srv.checkpoint(c, srv.posthandshake)
|
||||
if err != nil {
|
||||
clog.Trace("Rejected peer before protocol handshake", "err", err)
|
||||
|
@ -895,8 +920,8 @@ func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *discover.Node) e
|
|||
clog.Trace("Failed proto handshake", "err", err)
|
||||
return err
|
||||
}
|
||||
if phs.ID != c.id {
|
||||
clog.Trace("Wrong devp2p handshake identity", "err", phs.ID)
|
||||
if id := c.node.ID(); !bytes.Equal(crypto.Keccak256(phs.ID), id[:]) {
|
||||
clog.Trace("Wrong devp2p handshake identity", "phsid", fmt.Sprintf("%x", phs.ID))
|
||||
return DiscUnexpectedIdentity
|
||||
}
|
||||
c.caps, c.name = phs.Caps, phs.Name
|
||||
|
@ -911,6 +936,16 @@ func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *discover.Node) e
|
|||
return nil
|
||||
}
|
||||
|
||||
func nodeFromConn(pubkey *ecdsa.PublicKey, conn net.Conn) *enode.Node {
|
||||
var ip net.IP
|
||||
var port int
|
||||
if tcp, ok := conn.RemoteAddr().(*net.TCPAddr); ok {
|
||||
ip = tcp.IP
|
||||
port = tcp.Port
|
||||
}
|
||||
return enode.NewV4(pubkey, ip, port, port)
|
||||
}
|
||||
|
||||
func truncateName(s string) string {
|
||||
if len(s) > 20 {
|
||||
return s[:20] + "..."
|
||||
|
@ -985,13 +1020,13 @@ func (srv *Server) NodeInfo() *NodeInfo {
|
|||
info := &NodeInfo{
|
||||
Name: srv.Name,
|
||||
Enode: node.String(),
|
||||
ID: node.ID.String(),
|
||||
IP: node.IP.String(),
|
||||
ID: node.ID().String(),
|
||||
IP: node.IP().String(),
|
||||
ListenAddr: srv.ListenAddr,
|
||||
Protocols: make(map[string]interface{}),
|
||||
}
|
||||
info.Ports.Discovery = int(node.UDP)
|
||||
info.Ports.Listener = int(node.TCP)
|
||||
info.Ports.Discovery = node.UDP()
|
||||
info.Ports.Listener = node.TCP()
|
||||
|
||||
// Gather all the running protocol infos (only once per protocol type)
|
||||
for _, proto := range srv.Protocols {
|
||||
|
|
|
@ -28,21 +28,22 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlError, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
||||
}
|
||||
// func init() {
|
||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
||||
// }
|
||||
|
||||
type testTransport struct {
|
||||
id discover.NodeID
|
||||
rpub *ecdsa.PublicKey
|
||||
*rlpx
|
||||
|
||||
closeErr error
|
||||
}
|
||||
|
||||
func newTestTransport(id discover.NodeID, fd net.Conn) transport {
|
||||
func newTestTransport(rpub *ecdsa.PublicKey, fd net.Conn) transport {
|
||||
wrapped := newRLPX(fd).(*rlpx)
|
||||
wrapped.rw = newRLPXFrameRW(fd, secrets{
|
||||
MAC: zero16,
|
||||
|
@ -50,15 +51,16 @@ func newTestTransport(id discover.NodeID, fd net.Conn) transport {
|
|||
IngressMAC: sha3.NewKeccak256(),
|
||||
EgressMAC: sha3.NewKeccak256(),
|
||||
})
|
||||
return &testTransport{id: id, rlpx: wrapped}
|
||||
return &testTransport{rpub: rpub, rlpx: wrapped}
|
||||
}
|
||||
|
||||
func (c *testTransport) doEncHandshake(prv *ecdsa.PrivateKey, dialDest *discover.Node) (discover.NodeID, error) {
|
||||
return c.id, nil
|
||||
func (c *testTransport) doEncHandshake(prv *ecdsa.PrivateKey, dialDest *ecdsa.PublicKey) (*ecdsa.PublicKey, error) {
|
||||
return c.rpub, nil
|
||||
}
|
||||
|
||||
func (c *testTransport) doProtoHandshake(our *protoHandshake) (*protoHandshake, error) {
|
||||
return &protoHandshake{ID: c.id, Name: "test"}, nil
|
||||
pubkey := crypto.FromECDSAPub(c.rpub)[1:]
|
||||
return &protoHandshake{ID: pubkey, Name: "test"}, nil
|
||||
}
|
||||
|
||||
func (c *testTransport) close(err error) {
|
||||
|
@ -66,7 +68,7 @@ func (c *testTransport) close(err error) {
|
|||
c.closeErr = err
|
||||
}
|
||||
|
||||
func startTestServer(t *testing.T, id discover.NodeID, pf func(*Peer)) *Server {
|
||||
func startTestServer(t *testing.T, remoteKey *ecdsa.PublicKey, pf func(*Peer)) *Server {
|
||||
config := Config{
|
||||
Name: "test",
|
||||
MaxPeers: 10,
|
||||
|
@ -76,7 +78,7 @@ func startTestServer(t *testing.T, id discover.NodeID, pf func(*Peer)) *Server {
|
|||
server := &Server{
|
||||
Config: config,
|
||||
newPeerHook: pf,
|
||||
newTransport: func(fd net.Conn) transport { return newTestTransport(id, fd) },
|
||||
newTransport: func(fd net.Conn) transport { return newTestTransport(remoteKey, fd) },
|
||||
}
|
||||
if err := server.Start(); err != nil {
|
||||
t.Fatalf("Could not start server: %v", err)
|
||||
|
@ -87,14 +89,11 @@ func startTestServer(t *testing.T, id discover.NodeID, pf func(*Peer)) *Server {
|
|||
func TestServerListen(t *testing.T) {
|
||||
// start the test server
|
||||
connected := make(chan *Peer)
|
||||
remid := randomID()
|
||||
remid := &newkey().PublicKey
|
||||
srv := startTestServer(t, remid, func(p *Peer) {
|
||||
if p.ID() != remid {
|
||||
if p.ID() != enode.PubkeyToIDV4(remid) {
|
||||
t.Error("peer func called with wrong node id")
|
||||
}
|
||||
if p == nil {
|
||||
t.Error("peer func called with nil conn")
|
||||
}
|
||||
connected <- p
|
||||
})
|
||||
defer close(connected)
|
||||
|
@ -141,14 +140,14 @@ func TestServerDial(t *testing.T) {
|
|||
|
||||
// start the server
|
||||
connected := make(chan *Peer)
|
||||
remid := randomID()
|
||||
remid := &newkey().PublicKey
|
||||
srv := startTestServer(t, remid, func(p *Peer) { connected <- p })
|
||||
defer close(connected)
|
||||
defer srv.Stop()
|
||||
|
||||
// tell the server to connect
|
||||
tcpAddr := listener.Addr().(*net.TCPAddr)
|
||||
node := &discover.Node{ID: remid, IP: tcpAddr.IP, TCP: uint16(tcpAddr.Port)}
|
||||
node := enode.NewV4(remid, tcpAddr.IP, tcpAddr.Port, 0)
|
||||
srv.AddPeer(node)
|
||||
|
||||
select {
|
||||
|
@ -157,7 +156,7 @@ func TestServerDial(t *testing.T) {
|
|||
|
||||
select {
|
||||
case peer := <-connected:
|
||||
if peer.ID() != remid {
|
||||
if peer.ID() != enode.PubkeyToIDV4(remid) {
|
||||
t.Errorf("peer has wrong id")
|
||||
}
|
||||
if peer.Name() != "test" {
|
||||
|
@ -211,7 +210,7 @@ func TestServerTaskScheduling(t *testing.T) {
|
|||
quit, returned = make(chan struct{}), make(chan struct{})
|
||||
tc = 0
|
||||
tg = taskgen{
|
||||
newFunc: func(running int, peers map[discover.NodeID]*Peer) []task {
|
||||
newFunc: func(running int, peers map[enode.ID]*Peer) []task {
|
||||
tc++
|
||||
return []task{&testTask{index: tc - 1}}
|
||||
},
|
||||
|
@ -284,7 +283,7 @@ func TestServerManyTasks(t *testing.T) {
|
|||
defer srv.Stop()
|
||||
srv.loopWG.Add(1)
|
||||
go srv.run(taskgen{
|
||||
newFunc: func(running int, peers map[discover.NodeID]*Peer) []task {
|
||||
newFunc: func(running int, peers map[enode.ID]*Peer) []task {
|
||||
start, end = end, end+maxActiveDialTasks+10
|
||||
if end > len(alltasks) {
|
||||
end = len(alltasks)
|
||||
|
@ -319,19 +318,19 @@ func TestServerManyTasks(t *testing.T) {
|
|||
}
|
||||
|
||||
type taskgen struct {
|
||||
newFunc func(running int, peers map[discover.NodeID]*Peer) []task
|
||||
newFunc func(running int, peers map[enode.ID]*Peer) []task
|
||||
doneFunc func(task)
|
||||
}
|
||||
|
||||
func (tg taskgen) newTasks(running int, peers map[discover.NodeID]*Peer, now time.Time) []task {
|
||||
func (tg taskgen) newTasks(running int, peers map[enode.ID]*Peer, now time.Time) []task {
|
||||
return tg.newFunc(running, peers)
|
||||
}
|
||||
func (tg taskgen) taskDone(t task, now time.Time) {
|
||||
tg.doneFunc(t)
|
||||
}
|
||||
func (tg taskgen) addStatic(*discover.Node) {
|
||||
func (tg taskgen) addStatic(*enode.Node) {
|
||||
}
|
||||
func (tg taskgen) removeStatic(*discover.Node) {
|
||||
func (tg taskgen) removeStatic(*enode.Node) {
|
||||
}
|
||||
|
||||
type testTask struct {
|
||||
|
@ -347,13 +346,14 @@ func (t *testTask) Do(srv *Server) {
|
|||
// just after the encryption handshake when the server is
|
||||
// at capacity. Trusted connections should still be accepted.
|
||||
func TestServerAtCap(t *testing.T) {
|
||||
trustedID := randomID()
|
||||
trustedNode := newkey()
|
||||
trustedID := enode.PubkeyToIDV4(&trustedNode.PublicKey)
|
||||
srv := &Server{
|
||||
Config: Config{
|
||||
PrivateKey: newkey(),
|
||||
MaxPeers: 10,
|
||||
NoDial: true,
|
||||
TrustedNodes: []*discover.Node{{ID: trustedID}},
|
||||
TrustedNodes: []*enode.Node{newNode(trustedID, nil)},
|
||||
},
|
||||
}
|
||||
if err := srv.Start(); err != nil {
|
||||
|
@ -361,10 +361,11 @@ func TestServerAtCap(t *testing.T) {
|
|||
}
|
||||
defer srv.Stop()
|
||||
|
||||
newconn := func(id discover.NodeID) *conn {
|
||||
newconn := func(id enode.ID) *conn {
|
||||
fd, _ := net.Pipe()
|
||||
tx := newTestTransport(id, fd)
|
||||
return &conn{fd: fd, transport: tx, flags: inboundConn, id: id, cont: make(chan error)}
|
||||
tx := newTestTransport(&trustedNode.PublicKey, fd)
|
||||
node := enode.SignNull(new(enr.Record), id)
|
||||
return &conn{fd: fd, transport: tx, flags: inboundConn, node: node, cont: make(chan error)}
|
||||
}
|
||||
|
||||
// Inject a few connections to fill up the peer set.
|
||||
|
@ -390,14 +391,14 @@ func TestServerAtCap(t *testing.T) {
|
|||
}
|
||||
|
||||
// Remove from trusted set and try again
|
||||
srv.RemoveTrustedPeer(&discover.Node{ID: trustedID})
|
||||
srv.RemoveTrustedPeer(newNode(trustedID, nil))
|
||||
c = newconn(trustedID)
|
||||
if err := srv.checkpoint(c, srv.posthandshake); err != DiscTooManyPeers {
|
||||
t.Error("wrong error for insert:", err)
|
||||
}
|
||||
|
||||
// Add anotherID to trusted set and try again
|
||||
srv.AddTrustedPeer(&discover.Node{ID: anotherID})
|
||||
srv.AddTrustedPeer(newNode(anotherID, nil))
|
||||
c = newconn(anotherID)
|
||||
if err := srv.checkpoint(c, srv.posthandshake); err != nil {
|
||||
t.Error("unexpected error for trusted conn @posthandshake:", err)
|
||||
|
@ -409,20 +410,17 @@ func TestServerAtCap(t *testing.T) {
|
|||
|
||||
func TestServerPeerLimits(t *testing.T) {
|
||||
srvkey := newkey()
|
||||
clientkey := newkey()
|
||||
clientnode := enode.NewV4(&clientkey.PublicKey, nil, 0, 0)
|
||||
|
||||
clientid := randomID()
|
||||
clientnode := &discover.Node{ID: clientid}
|
||||
|
||||
var tp *setupTransport = &setupTransport{
|
||||
id: clientid,
|
||||
phs: &protoHandshake{
|
||||
ID: clientid,
|
||||
var tp = &setupTransport{
|
||||
pubkey: &clientkey.PublicKey,
|
||||
phs: protoHandshake{
|
||||
ID: crypto.FromECDSAPub(&clientkey.PublicKey)[1:],
|
||||
// Force "DiscUselessPeer" due to unmatching caps
|
||||
// Caps: []Cap{discard.cap()},
|
||||
},
|
||||
}
|
||||
var flags connFlag = dynDialedConn
|
||||
var dialDest *discover.Node = &discover.Node{ID: clientid}
|
||||
|
||||
srv := &Server{
|
||||
Config: Config{
|
||||
|
@ -440,6 +438,8 @@ func TestServerPeerLimits(t *testing.T) {
|
|||
defer srv.Stop()
|
||||
|
||||
// Check that server is full (MaxPeers=0)
|
||||
flags := dynDialedConn
|
||||
dialDest := clientnode
|
||||
conn, _ := net.Pipe()
|
||||
srv.SetupConn(conn, flags, dialDest)
|
||||
if tp.closeErr != DiscTooManyPeers {
|
||||
|
@ -473,59 +473,61 @@ func TestServerPeerLimits(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestServerSetupConn(t *testing.T) {
|
||||
id := randomID()
|
||||
srvkey := newkey()
|
||||
srvid := discover.PubkeyID(&srvkey.PublicKey)
|
||||
var (
|
||||
clientkey, srvkey = newkey(), newkey()
|
||||
clientpub = &clientkey.PublicKey
|
||||
srvpub = &srvkey.PublicKey
|
||||
)
|
||||
tests := []struct {
|
||||
dontstart bool
|
||||
tt *setupTransport
|
||||
flags connFlag
|
||||
dialDest *discover.Node
|
||||
dialDest *enode.Node
|
||||
|
||||
wantCloseErr error
|
||||
wantCalls string
|
||||
}{
|
||||
{
|
||||
dontstart: true,
|
||||
tt: &setupTransport{id: id},
|
||||
tt: &setupTransport{pubkey: clientpub},
|
||||
wantCalls: "close,",
|
||||
wantCloseErr: errServerStopped,
|
||||
},
|
||||
{
|
||||
tt: &setupTransport{id: id, encHandshakeErr: errors.New("read error")},
|
||||
tt: &setupTransport{pubkey: clientpub, encHandshakeErr: errors.New("read error")},
|
||||
flags: inboundConn,
|
||||
wantCalls: "doEncHandshake,close,",
|
||||
wantCloseErr: errors.New("read error"),
|
||||
},
|
||||
{
|
||||
tt: &setupTransport{id: id},
|
||||
dialDest: &discover.Node{ID: randomID()},
|
||||
tt: &setupTransport{pubkey: clientpub},
|
||||
dialDest: enode.NewV4(&newkey().PublicKey, nil, 0, 0),
|
||||
flags: dynDialedConn,
|
||||
wantCalls: "doEncHandshake,close,",
|
||||
wantCloseErr: DiscUnexpectedIdentity,
|
||||
},
|
||||
{
|
||||
tt: &setupTransport{id: id, phs: &protoHandshake{ID: randomID()}},
|
||||
dialDest: &discover.Node{ID: id},
|
||||
tt: &setupTransport{pubkey: clientpub, phs: protoHandshake{ID: randomID().Bytes()}},
|
||||
dialDest: enode.NewV4(clientpub, nil, 0, 0),
|
||||
flags: dynDialedConn,
|
||||
wantCalls: "doEncHandshake,doProtoHandshake,close,",
|
||||
wantCloseErr: DiscUnexpectedIdentity,
|
||||
},
|
||||
{
|
||||
tt: &setupTransport{id: id, protoHandshakeErr: errors.New("foo")},
|
||||
dialDest: &discover.Node{ID: id},
|
||||
tt: &setupTransport{pubkey: clientpub, protoHandshakeErr: errors.New("foo")},
|
||||
dialDest: enode.NewV4(clientpub, nil, 0, 0),
|
||||
flags: dynDialedConn,
|
||||
wantCalls: "doEncHandshake,doProtoHandshake,close,",
|
||||
wantCloseErr: errors.New("foo"),
|
||||
},
|
||||
{
|
||||
tt: &setupTransport{id: srvid, phs: &protoHandshake{ID: srvid}},
|
||||
tt: &setupTransport{pubkey: srvpub, phs: protoHandshake{ID: crypto.FromECDSAPub(srvpub)[1:]}},
|
||||
flags: inboundConn,
|
||||
wantCalls: "doEncHandshake,close,",
|
||||
wantCloseErr: DiscSelf,
|
||||
},
|
||||
{
|
||||
tt: &setupTransport{id: id, phs: &protoHandshake{ID: id}},
|
||||
tt: &setupTransport{pubkey: clientpub, phs: protoHandshake{ID: crypto.FromECDSAPub(clientpub)[1:]}},
|
||||
flags: inboundConn,
|
||||
wantCalls: "doEncHandshake,doProtoHandshake,close,",
|
||||
wantCloseErr: DiscUselessPeer,
|
||||
|
@ -560,26 +562,26 @@ func TestServerSetupConn(t *testing.T) {
|
|||
}
|
||||
|
||||
type setupTransport struct {
|
||||
id discover.NodeID
|
||||
encHandshakeErr error
|
||||
|
||||
phs *protoHandshake
|
||||
pubkey *ecdsa.PublicKey
|
||||
encHandshakeErr error
|
||||
phs protoHandshake
|
||||
protoHandshakeErr error
|
||||
|
||||
calls string
|
||||
closeErr error
|
||||
}
|
||||
|
||||
func (c *setupTransport) doEncHandshake(prv *ecdsa.PrivateKey, dialDest *discover.Node) (discover.NodeID, error) {
|
||||
func (c *setupTransport) doEncHandshake(prv *ecdsa.PrivateKey, dialDest *ecdsa.PublicKey) (*ecdsa.PublicKey, error) {
|
||||
c.calls += "doEncHandshake,"
|
||||
return c.id, c.encHandshakeErr
|
||||
return c.pubkey, c.encHandshakeErr
|
||||
}
|
||||
|
||||
func (c *setupTransport) doProtoHandshake(our *protoHandshake) (*protoHandshake, error) {
|
||||
c.calls += "doProtoHandshake,"
|
||||
if c.protoHandshakeErr != nil {
|
||||
return nil, c.protoHandshakeErr
|
||||
}
|
||||
return c.phs, nil
|
||||
return &c.phs, nil
|
||||
}
|
||||
func (c *setupTransport) close(err error) {
|
||||
c.calls += "close,"
|
||||
|
@ -602,7 +604,7 @@ func newkey() *ecdsa.PrivateKey {
|
|||
return key
|
||||
}
|
||||
|
||||
func randomID() (id discover.NodeID) {
|
||||
func randomID() (id enode.ID) {
|
||||
for i := range id {
|
||||
id[i] = byte(rand.Intn(255))
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import (
|
|||
|
||||
"github.com/docker/docker/pkg/reexec"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -64,7 +64,7 @@ func NewDockerAdapter() (*DockerAdapter, error) {
|
|||
|
||||
return &DockerAdapter{
|
||||
ExecAdapter{
|
||||
nodes: make(map[discover.NodeID]*ExecNode),
|
||||
nodes: make(map[enode.ID]*ExecNode),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
@ -54,7 +54,7 @@ type ExecAdapter struct {
|
|||
// simulation node are created.
|
||||
BaseDir string
|
||||
|
||||
nodes map[discover.NodeID]*ExecNode
|
||||
nodes map[enode.ID]*ExecNode
|
||||
}
|
||||
|
||||
// NewExecAdapter returns an ExecAdapter which stores node data in
|
||||
|
@ -62,7 +62,7 @@ type ExecAdapter struct {
|
|||
func NewExecAdapter(baseDir string) *ExecAdapter {
|
||||
return &ExecAdapter{
|
||||
BaseDir: baseDir,
|
||||
nodes: make(map[discover.NodeID]*ExecNode),
|
||||
nodes: make(map[enode.ID]*ExecNode),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,7 @@ func (e *ExecAdapter) NewNode(config *NodeConfig) (Node, error) {
|
|||
// ExecNode starts a simulation node by exec'ing the current binary and
|
||||
// running the configured services
|
||||
type ExecNode struct {
|
||||
ID discover.NodeID
|
||||
ID enode.ID
|
||||
Dir string
|
||||
Config *execNodeConfig
|
||||
Cmd *exec.Cmd
|
||||
|
@ -492,7 +492,7 @@ type wsRPCDialer struct {
|
|||
|
||||
// DialRPC implements the RPCDialer interface by creating a WebSocket RPC
|
||||
// client of the given node
|
||||
func (w *wsRPCDialer) DialRPC(id discover.NodeID) (*rpc.Client, error) {
|
||||
func (w *wsRPCDialer) DialRPC(id enode.ID) (*rpc.Client, error) {
|
||||
addr, ok := w.addrs[id.String()]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown node: %s", id)
|
||||
|
|
|
@ -27,7 +27,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/pipes"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
@ -37,7 +37,7 @@ import (
|
|||
type SimAdapter struct {
|
||||
pipe func() (net.Conn, net.Conn, error)
|
||||
mtx sync.RWMutex
|
||||
nodes map[discover.NodeID]*SimNode
|
||||
nodes map[enode.ID]*SimNode
|
||||
services map[string]ServiceFunc
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ type SimAdapter struct {
|
|||
func NewSimAdapter(services map[string]ServiceFunc) *SimAdapter {
|
||||
return &SimAdapter{
|
||||
pipe: pipes.NetPipe,
|
||||
nodes: make(map[discover.NodeID]*SimNode),
|
||||
nodes: make(map[enode.ID]*SimNode),
|
||||
services: services,
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ func NewSimAdapter(services map[string]ServiceFunc) *SimAdapter {
|
|||
func NewTCPAdapter(services map[string]ServiceFunc) *SimAdapter {
|
||||
return &SimAdapter{
|
||||
pipe: pipes.TCPPipe,
|
||||
nodes: make(map[discover.NodeID]*SimNode),
|
||||
nodes: make(map[enode.ID]*SimNode),
|
||||
services: services,
|
||||
}
|
||||
}
|
||||
|
@ -115,14 +115,14 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
|
|||
|
||||
// Dial implements the p2p.NodeDialer interface by connecting to the node using
|
||||
// an in-memory net.Pipe
|
||||
func (s *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) {
|
||||
node, ok := s.GetNode(dest.ID)
|
||||
func (s *SimAdapter) Dial(dest *enode.Node) (conn net.Conn, err error) {
|
||||
node, ok := s.GetNode(dest.ID())
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown node: %s", dest.ID)
|
||||
return nil, fmt.Errorf("unknown node: %s", dest.ID())
|
||||
}
|
||||
srv := node.Server()
|
||||
if srv == nil {
|
||||
return nil, fmt.Errorf("node not running: %s", dest.ID)
|
||||
return nil, fmt.Errorf("node not running: %s", dest.ID())
|
||||
}
|
||||
// SimAdapter.pipe is net.Pipe (NewSimAdapter)
|
||||
pipe1, pipe2, err := s.pipe()
|
||||
|
@ -138,7 +138,7 @@ func (s *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) {
|
|||
|
||||
// DialRPC implements the RPCDialer interface by creating an in-memory RPC
|
||||
// client of the given node
|
||||
func (s *SimAdapter) DialRPC(id discover.NodeID) (*rpc.Client, error) {
|
||||
func (s *SimAdapter) DialRPC(id enode.ID) (*rpc.Client, error) {
|
||||
node, ok := s.GetNode(id)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown node: %s", id)
|
||||
|
@ -151,7 +151,7 @@ func (s *SimAdapter) DialRPC(id discover.NodeID) (*rpc.Client, error) {
|
|||
}
|
||||
|
||||
// GetNode returns the node with the given ID if it exists
|
||||
func (s *SimAdapter) GetNode(id discover.NodeID) (*SimNode, bool) {
|
||||
func (s *SimAdapter) GetNode(id enode.ID) (*SimNode, bool) {
|
||||
s.mtx.RLock()
|
||||
defer s.mtx.RUnlock()
|
||||
node, ok := s.nodes[id]
|
||||
|
@ -163,7 +163,7 @@ func (s *SimAdapter) GetNode(id discover.NodeID) (*SimNode, bool) {
|
|||
// pipe
|
||||
type SimNode struct {
|
||||
lock sync.RWMutex
|
||||
ID discover.NodeID
|
||||
ID enode.ID
|
||||
config *NodeConfig
|
||||
adapter *SimAdapter
|
||||
node *node.Node
|
||||
|
@ -177,9 +177,9 @@ func (sn *SimNode) Addr() []byte {
|
|||
return []byte(sn.Node().String())
|
||||
}
|
||||
|
||||
// Node returns a discover.Node representing the SimNode
|
||||
func (sn *SimNode) Node() *discover.Node {
|
||||
return discover.NewNode(sn.ID, net.IP{127, 0, 0, 1}, 30303, 30303)
|
||||
// Node returns a node descriptor representing the SimNode
|
||||
func (sn *SimNode) Node() *enode.Node {
|
||||
return sn.config.Node()
|
||||
}
|
||||
|
||||
// Client returns an rpc.Client which can be used to communicate with the
|
||||
|
|
|
@ -29,7 +29,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
|
@ -78,7 +78,7 @@ type NodeAdapter interface {
|
|||
type NodeConfig struct {
|
||||
// ID is the node's ID which is used to identify the node in the
|
||||
// simulation network
|
||||
ID discover.NodeID
|
||||
ID enode.ID
|
||||
|
||||
// PrivateKey is the node's private key which is used by the devp2p
|
||||
// stack to encrypt communications
|
||||
|
@ -97,7 +97,7 @@ type NodeConfig struct {
|
|||
Services []string
|
||||
|
||||
// function to sanction or prevent suggesting a peer
|
||||
Reachable func(id discover.NodeID) bool
|
||||
Reachable func(id enode.ID) bool
|
||||
|
||||
Port uint16
|
||||
}
|
||||
|
@ -138,11 +138,9 @@ func (n *NodeConfig) UnmarshalJSON(data []byte) error {
|
|||
}
|
||||
|
||||
if confJSON.ID != "" {
|
||||
nodeID, err := discover.HexID(confJSON.ID)
|
||||
if err != nil {
|
||||
if err := n.ID.UnmarshalText([]byte(confJSON.ID)); err != nil {
|
||||
return err
|
||||
}
|
||||
n.ID = nodeID
|
||||
}
|
||||
|
||||
if confJSON.PrivateKey != "" {
|
||||
|
@ -165,6 +163,11 @@ func (n *NodeConfig) UnmarshalJSON(data []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Node returns the node descriptor represented by the config.
|
||||
func (n *NodeConfig) Node() *enode.Node {
|
||||
return enode.NewV4(&n.PrivateKey.PublicKey, net.IP{127, 0, 0, 1}, int(n.Port), int(n.Port))
|
||||
}
|
||||
|
||||
// RandomNodeConfig returns node configuration with a randomly generated ID and
|
||||
// PrivateKey
|
||||
func RandomNodeConfig() *NodeConfig {
|
||||
|
@ -173,7 +176,7 @@ func RandomNodeConfig() *NodeConfig {
|
|||
panic("unable to generate key")
|
||||
}
|
||||
|
||||
id := discover.PubkeyID(&key.PublicKey)
|
||||
id := enode.PubkeyToIDV4(&key.PublicKey)
|
||||
port, err := assignTCPPort()
|
||||
if err != nil {
|
||||
panic("unable to assign tcp port")
|
||||
|
@ -218,7 +221,7 @@ type ServiceContext struct {
|
|||
// other nodes in the network (for example a simulated Swarm node which needs
|
||||
// to connect to a Geth node to resolve ENS names)
|
||||
type RPCDialer interface {
|
||||
DialRPC(id discover.NodeID) (*rpc.Client, error)
|
||||
DialRPC(id enode.ID) (*rpc.Client, error)
|
||||
}
|
||||
|
||||
// Services is a collection of services which can be run in a simulation
|
||||
|
|
|
@ -28,7 +28,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
@ -96,12 +96,12 @@ func main() {
|
|||
// sends a ping to all its connected peers every 10s and receives a pong in
|
||||
// return
|
||||
type pingPongService struct {
|
||||
id discover.NodeID
|
||||
id enode.ID
|
||||
log log.Logger
|
||||
received int64
|
||||
}
|
||||
|
||||
func newPingPongService(id discover.NodeID) *pingPongService {
|
||||
func newPingPongService(id enode.ID) *pingPongService {
|
||||
return &pingPongService{
|
||||
id: id,
|
||||
log: log.New("node.id", id),
|
||||
|
|
|
@ -31,7 +31,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
@ -709,8 +709,9 @@ func (s *Server) wrapHandler(handler http.HandlerFunc) httprouter.Handle {
|
|||
ctx := context.Background()
|
||||
|
||||
if id := params.ByName("nodeid"); id != "" {
|
||||
var nodeID enode.ID
|
||||
var node *Node
|
||||
if nodeID, err := discover.HexID(id); err == nil {
|
||||
if nodeID.UnmarshalText([]byte(id)) == nil {
|
||||
node = s.network.GetNode(nodeID)
|
||||
} else {
|
||||
node = s.network.GetNodeByName(id)
|
||||
|
@ -723,8 +724,9 @@ func (s *Server) wrapHandler(handler http.HandlerFunc) httprouter.Handle {
|
|||
}
|
||||
|
||||
if id := params.ByName("peerid"); id != "" {
|
||||
var peerID enode.ID
|
||||
var peer *Node
|
||||
if peerID, err := discover.HexID(id); err == nil {
|
||||
if peerID.UnmarshalText([]byte(id)) == nil {
|
||||
peer = s.network.GetNode(peerID)
|
||||
} else {
|
||||
peer = s.network.GetNodeByName(id)
|
||||
|
|
|
@ -30,7 +30,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
@ -38,12 +38,12 @@ import (
|
|||
// testService implements the node.Service interface and provides protocols
|
||||
// and APIs which are useful for testing nodes in a simulation network
|
||||
type testService struct {
|
||||
id discover.NodeID
|
||||
id enode.ID
|
||||
|
||||
// peerCount is incremented once a peer handshake has been performed
|
||||
peerCount int64
|
||||
|
||||
peers map[discover.NodeID]*testPeer
|
||||
peers map[enode.ID]*testPeer
|
||||
peersMtx sync.Mutex
|
||||
|
||||
// state stores []byte which is used to test creating and loading
|
||||
|
@ -54,7 +54,7 @@ type testService struct {
|
|||
func newTestService(ctx *adapters.ServiceContext) (node.Service, error) {
|
||||
svc := &testService{
|
||||
id: ctx.Config.ID,
|
||||
peers: make(map[discover.NodeID]*testPeer),
|
||||
peers: make(map[enode.ID]*testPeer),
|
||||
}
|
||||
svc.state.Store(ctx.Snapshot)
|
||||
return svc, nil
|
||||
|
@ -65,7 +65,7 @@ type testPeer struct {
|
|||
dumReady chan struct{}
|
||||
}
|
||||
|
||||
func (t *testService) peer(id discover.NodeID) *testPeer {
|
||||
func (t *testService) peer(id enode.ID) *testPeer {
|
||||
t.peersMtx.Lock()
|
||||
defer t.peersMtx.Unlock()
|
||||
if peer, ok := t.peers[id]; ok {
|
||||
|
@ -410,7 +410,7 @@ func (t *expectEvents) nodeEvent(id string, up bool) *Event {
|
|||
Type: EventTypeNode,
|
||||
Node: &Node{
|
||||
Config: &adapters.NodeConfig{
|
||||
ID: discover.MustHexID(id),
|
||||
ID: enode.HexID(id),
|
||||
},
|
||||
Up: up,
|
||||
},
|
||||
|
@ -421,8 +421,8 @@ func (t *expectEvents) connEvent(one, other string, up bool) *Event {
|
|||
return &Event{
|
||||
Type: EventTypeConn,
|
||||
Conn: &Conn{
|
||||
One: discover.MustHexID(one),
|
||||
Other: discover.MustHexID(other),
|
||||
One: enode.HexID(one),
|
||||
Other: enode.HexID(other),
|
||||
Up: up,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
)
|
||||
|
||||
|
@ -154,7 +154,7 @@ func probabilistic(net *Network, quit chan struct{}, nodeCount int) {
|
|||
wg.Done()
|
||||
continue
|
||||
}
|
||||
go func(id discover.NodeID) {
|
||||
go func(id enode.ID) {
|
||||
time.Sleep(randWait)
|
||||
err := net.Start(id)
|
||||
if err != nil {
|
||||
|
@ -169,8 +169,8 @@ func probabilistic(net *Network, quit chan struct{}, nodeCount int) {
|
|||
}
|
||||
|
||||
//connect nodeCount number of nodes in a ring
|
||||
func connectNodesInRing(net *Network, nodeCount int) ([]discover.NodeID, error) {
|
||||
ids := make([]discover.NodeID, nodeCount)
|
||||
func connectNodesInRing(net *Network, nodeCount int) ([]enode.ID, error) {
|
||||
ids := make([]enode.ID, nodeCount)
|
||||
for i := 0; i < nodeCount; i++ {
|
||||
conf := adapters.RandomNodeConfig()
|
||||
node, err := net.NewNodeWithConfig(conf)
|
||||
|
|
|
@ -27,7 +27,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
)
|
||||
|
||||
func TestMocker(t *testing.T) {
|
||||
|
@ -82,7 +82,7 @@ func TestMocker(t *testing.T) {
|
|||
defer sub.Unsubscribe()
|
||||
//wait until all nodes are started and connected
|
||||
//store every node up event in a map (value is irrelevant, mimic Set datatype)
|
||||
nodemap := make(map[discover.NodeID]bool)
|
||||
nodemap := make(map[enode.ID]bool)
|
||||
wg.Add(1)
|
||||
nodesComplete := false
|
||||
connCount := 0
|
||||
|
|
|
@ -27,7 +27,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
)
|
||||
|
||||
|
@ -51,7 +51,7 @@ type Network struct {
|
|||
NetworkConfig
|
||||
|
||||
Nodes []*Node `json:"nodes"`
|
||||
nodeMap map[discover.NodeID]int
|
||||
nodeMap map[enode.ID]int
|
||||
|
||||
Conns []*Conn `json:"conns"`
|
||||
connMap map[string]int
|
||||
|
@ -67,7 +67,7 @@ func NewNetwork(nodeAdapter adapters.NodeAdapter, conf *NetworkConfig) *Network
|
|||
return &Network{
|
||||
NetworkConfig: *conf,
|
||||
nodeAdapter: nodeAdapter,
|
||||
nodeMap: make(map[discover.NodeID]int),
|
||||
nodeMap: make(map[enode.ID]int),
|
||||
connMap: make(map[string]int),
|
||||
quitc: make(chan struct{}),
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ func (net *Network) NewNodeWithConfig(conf *adapters.NodeConfig) (*Node, error)
|
|||
defer net.lock.Unlock()
|
||||
|
||||
if conf.Reachable == nil {
|
||||
conf.Reachable = func(otherID discover.NodeID) bool {
|
||||
conf.Reachable = func(otherID enode.ID) bool {
|
||||
_, err := net.InitConn(conf.ID, otherID)
|
||||
if err != nil && bytes.Compare(conf.ID.Bytes(), otherID.Bytes()) < 0 {
|
||||
return false
|
||||
|
@ -158,13 +158,13 @@ func (net *Network) StopAll() error {
|
|||
}
|
||||
|
||||
// Start starts the node with the given ID
|
||||
func (net *Network) Start(id discover.NodeID) error {
|
||||
func (net *Network) Start(id enode.ID) error {
|
||||
return net.startWithSnapshots(id, nil)
|
||||
}
|
||||
|
||||
// startWithSnapshots starts the node with the given ID using the give
|
||||
// snapshots
|
||||
func (net *Network) startWithSnapshots(id discover.NodeID, snapshots map[string][]byte) error {
|
||||
func (net *Network) startWithSnapshots(id enode.ID, snapshots map[string][]byte) error {
|
||||
net.lock.Lock()
|
||||
defer net.lock.Unlock()
|
||||
node := net.getNode(id)
|
||||
|
@ -200,7 +200,7 @@ func (net *Network) startWithSnapshots(id discover.NodeID, snapshots map[string]
|
|||
|
||||
// watchPeerEvents reads peer events from the given channel and emits
|
||||
// corresponding network events
|
||||
func (net *Network) watchPeerEvents(id discover.NodeID, events chan *p2p.PeerEvent, sub event.Subscription) {
|
||||
func (net *Network) watchPeerEvents(id enode.ID, events chan *p2p.PeerEvent, sub event.Subscription) {
|
||||
defer func() {
|
||||
sub.Unsubscribe()
|
||||
|
||||
|
@ -248,7 +248,7 @@ func (net *Network) watchPeerEvents(id discover.NodeID, events chan *p2p.PeerEve
|
|||
}
|
||||
|
||||
// Stop stops the node with the given ID
|
||||
func (net *Network) Stop(id discover.NodeID) error {
|
||||
func (net *Network) Stop(id enode.ID) error {
|
||||
net.lock.Lock()
|
||||
defer net.lock.Unlock()
|
||||
node := net.getNode(id)
|
||||
|
@ -270,7 +270,7 @@ func (net *Network) Stop(id discover.NodeID) error {
|
|||
|
||||
// Connect connects two nodes together by calling the "admin_addPeer" RPC
|
||||
// method on the "one" node so that it connects to the "other" node
|
||||
func (net *Network) Connect(oneID, otherID discover.NodeID) error {
|
||||
func (net *Network) Connect(oneID, otherID enode.ID) error {
|
||||
log.Debug(fmt.Sprintf("connecting %s to %s", oneID, otherID))
|
||||
conn, err := net.InitConn(oneID, otherID)
|
||||
if err != nil {
|
||||
|
@ -286,7 +286,7 @@ func (net *Network) Connect(oneID, otherID discover.NodeID) error {
|
|||
|
||||
// Disconnect disconnects two nodes by calling the "admin_removePeer" RPC
|
||||
// method on the "one" node so that it disconnects from the "other" node
|
||||
func (net *Network) Disconnect(oneID, otherID discover.NodeID) error {
|
||||
func (net *Network) Disconnect(oneID, otherID enode.ID) error {
|
||||
conn := net.GetConn(oneID, otherID)
|
||||
if conn == nil {
|
||||
return fmt.Errorf("connection between %v and %v does not exist", oneID, otherID)
|
||||
|
@ -303,7 +303,7 @@ func (net *Network) Disconnect(oneID, otherID discover.NodeID) error {
|
|||
}
|
||||
|
||||
// DidConnect tracks the fact that the "one" node connected to the "other" node
|
||||
func (net *Network) DidConnect(one, other discover.NodeID) error {
|
||||
func (net *Network) DidConnect(one, other enode.ID) error {
|
||||
net.lock.Lock()
|
||||
defer net.lock.Unlock()
|
||||
conn, err := net.getOrCreateConn(one, other)
|
||||
|
@ -320,7 +320,7 @@ func (net *Network) DidConnect(one, other discover.NodeID) error {
|
|||
|
||||
// DidDisconnect tracks the fact that the "one" node disconnected from the
|
||||
// "other" node
|
||||
func (net *Network) DidDisconnect(one, other discover.NodeID) error {
|
||||
func (net *Network) DidDisconnect(one, other enode.ID) error {
|
||||
net.lock.Lock()
|
||||
defer net.lock.Unlock()
|
||||
conn := net.getConn(one, other)
|
||||
|
@ -337,7 +337,7 @@ func (net *Network) DidDisconnect(one, other discover.NodeID) error {
|
|||
}
|
||||
|
||||
// DidSend tracks the fact that "sender" sent a message to "receiver"
|
||||
func (net *Network) DidSend(sender, receiver discover.NodeID, proto string, code uint64) error {
|
||||
func (net *Network) DidSend(sender, receiver enode.ID, proto string, code uint64) error {
|
||||
msg := &Msg{
|
||||
One: sender,
|
||||
Other: receiver,
|
||||
|
@ -350,7 +350,7 @@ func (net *Network) DidSend(sender, receiver discover.NodeID, proto string, code
|
|||
}
|
||||
|
||||
// DidReceive tracks the fact that "receiver" received a message from "sender"
|
||||
func (net *Network) DidReceive(sender, receiver discover.NodeID, proto string, code uint64) error {
|
||||
func (net *Network) DidReceive(sender, receiver enode.ID, proto string, code uint64) error {
|
||||
msg := &Msg{
|
||||
One: sender,
|
||||
Other: receiver,
|
||||
|
@ -364,7 +364,7 @@ func (net *Network) DidReceive(sender, receiver discover.NodeID, proto string, c
|
|||
|
||||
// GetNode gets the node with the given ID, returning nil if the node does not
|
||||
// exist
|
||||
func (net *Network) GetNode(id discover.NodeID) *Node {
|
||||
func (net *Network) GetNode(id enode.ID) *Node {
|
||||
net.lock.Lock()
|
||||
defer net.lock.Unlock()
|
||||
return net.getNode(id)
|
||||
|
@ -387,7 +387,7 @@ func (net *Network) GetNodes() (nodes []*Node) {
|
|||
return nodes
|
||||
}
|
||||
|
||||
func (net *Network) getNode(id discover.NodeID) *Node {
|
||||
func (net *Network) getNode(id enode.ID) *Node {
|
||||
i, found := net.nodeMap[id]
|
||||
if !found {
|
||||
return nil
|
||||
|
@ -406,7 +406,7 @@ func (net *Network) getNodeByName(name string) *Node {
|
|||
|
||||
// GetConn returns the connection which exists between "one" and "other"
|
||||
// regardless of which node initiated the connection
|
||||
func (net *Network) GetConn(oneID, otherID discover.NodeID) *Conn {
|
||||
func (net *Network) GetConn(oneID, otherID enode.ID) *Conn {
|
||||
net.lock.Lock()
|
||||
defer net.lock.Unlock()
|
||||
return net.getConn(oneID, otherID)
|
||||
|
@ -414,13 +414,13 @@ func (net *Network) GetConn(oneID, otherID discover.NodeID) *Conn {
|
|||
|
||||
// GetOrCreateConn is like GetConn but creates the connection if it doesn't
|
||||
// already exist
|
||||
func (net *Network) GetOrCreateConn(oneID, otherID discover.NodeID) (*Conn, error) {
|
||||
func (net *Network) GetOrCreateConn(oneID, otherID enode.ID) (*Conn, error) {
|
||||
net.lock.Lock()
|
||||
defer net.lock.Unlock()
|
||||
return net.getOrCreateConn(oneID, otherID)
|
||||
}
|
||||
|
||||
func (net *Network) getOrCreateConn(oneID, otherID discover.NodeID) (*Conn, error) {
|
||||
func (net *Network) getOrCreateConn(oneID, otherID enode.ID) (*Conn, error) {
|
||||
if conn := net.getConn(oneID, otherID); conn != nil {
|
||||
return conn, nil
|
||||
}
|
||||
|
@ -445,7 +445,7 @@ func (net *Network) getOrCreateConn(oneID, otherID discover.NodeID) (*Conn, erro
|
|||
return conn, nil
|
||||
}
|
||||
|
||||
func (net *Network) getConn(oneID, otherID discover.NodeID) *Conn {
|
||||
func (net *Network) getConn(oneID, otherID enode.ID) *Conn {
|
||||
label := ConnLabel(oneID, otherID)
|
||||
i, found := net.connMap[label]
|
||||
if !found {
|
||||
|
@ -462,7 +462,7 @@ func (net *Network) getConn(oneID, otherID discover.NodeID) *Conn {
|
|||
// it also checks whether there has been recent attempt to connect the peers
|
||||
// this is cheating as the simulation is used as an oracle and know about
|
||||
// remote peers attempt to connect to a node which will then not initiate the connection
|
||||
func (net *Network) InitConn(oneID, otherID discover.NodeID) (*Conn, error) {
|
||||
func (net *Network) InitConn(oneID, otherID enode.ID) (*Conn, error) {
|
||||
net.lock.Lock()
|
||||
defer net.lock.Unlock()
|
||||
if oneID == otherID {
|
||||
|
@ -508,7 +508,7 @@ func (net *Network) Reset() {
|
|||
|
||||
//re-initialize the maps
|
||||
net.connMap = make(map[string]int)
|
||||
net.nodeMap = make(map[discover.NodeID]int)
|
||||
net.nodeMap = make(map[enode.ID]int)
|
||||
|
||||
net.Nodes = nil
|
||||
net.Conns = nil
|
||||
|
@ -527,7 +527,7 @@ type Node struct {
|
|||
}
|
||||
|
||||
// ID returns the ID of the node
|
||||
func (n *Node) ID() discover.NodeID {
|
||||
func (n *Node) ID() enode.ID {
|
||||
return n.Config.ID
|
||||
}
|
||||
|
||||
|
@ -564,10 +564,10 @@ func (n *Node) MarshalJSON() ([]byte, error) {
|
|||
// Conn represents a connection between two nodes in the network
|
||||
type Conn struct {
|
||||
// One is the node which initiated the connection
|
||||
One discover.NodeID `json:"one"`
|
||||
One enode.ID `json:"one"`
|
||||
|
||||
// Other is the node which the connection was made to
|
||||
Other discover.NodeID `json:"other"`
|
||||
Other enode.ID `json:"other"`
|
||||
|
||||
// Up tracks whether or not the connection is active
|
||||
Up bool `json:"up"`
|
||||
|
@ -596,11 +596,11 @@ func (c *Conn) String() string {
|
|||
|
||||
// Msg represents a p2p message sent between two nodes in the network
|
||||
type Msg struct {
|
||||
One discover.NodeID `json:"one"`
|
||||
Other discover.NodeID `json:"other"`
|
||||
Protocol string `json:"protocol"`
|
||||
Code uint64 `json:"code"`
|
||||
Received bool `json:"received"`
|
||||
One enode.ID `json:"one"`
|
||||
Other enode.ID `json:"other"`
|
||||
Protocol string `json:"protocol"`
|
||||
Code uint64 `json:"code"`
|
||||
Received bool `json:"received"`
|
||||
}
|
||||
|
||||
// String returns a log-friendly string
|
||||
|
@ -611,8 +611,8 @@ func (m *Msg) String() string {
|
|||
// ConnLabel generates a deterministic string which represents a connection
|
||||
// between two nodes, used to compare if two connections are between the same
|
||||
// nodes
|
||||
func ConnLabel(source, target discover.NodeID) string {
|
||||
var first, second discover.NodeID
|
||||
func ConnLabel(source, target enode.ID) string {
|
||||
var first, second enode.ID
|
||||
if bytes.Compare(source.Bytes(), target.Bytes()) > 0 {
|
||||
first = target
|
||||
second = source
|
||||
|
|
|
@ -22,7 +22,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
)
|
||||
|
||||
|
@ -39,7 +39,7 @@ func TestNetworkSimulation(t *testing.T) {
|
|||
})
|
||||
defer network.Shutdown()
|
||||
nodeCount := 20
|
||||
ids := make([]discover.NodeID, nodeCount)
|
||||
ids := make([]enode.ID, nodeCount)
|
||||
for i := 0; i < nodeCount; i++ {
|
||||
conf := adapters.RandomNodeConfig()
|
||||
node, err := network.NewNodeWithConfig(conf)
|
||||
|
@ -64,7 +64,7 @@ func TestNetworkSimulation(t *testing.T) {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
check := func(ctx context.Context, id discover.NodeID) (bool, error) {
|
||||
check := func(ctx context.Context, id enode.ID) (bool, error) {
|
||||
// check we haven't run out of time
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
@ -102,7 +102,7 @@ func TestNetworkSimulation(t *testing.T) {
|
|||
defer cancel()
|
||||
|
||||
// trigger a check every 100ms
|
||||
trigger := make(chan discover.NodeID)
|
||||
trigger := make(chan enode.ID)
|
||||
go triggerChecks(ctx, ids, trigger, 100*time.Millisecond)
|
||||
|
||||
result := NewSimulation(network).Run(ctx, &Step{
|
||||
|
@ -140,7 +140,7 @@ func TestNetworkSimulation(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func triggerChecks(ctx context.Context, ids []discover.NodeID, trigger chan discover.NodeID, interval time.Duration) {
|
||||
func triggerChecks(ctx context.Context, ids []enode.ID, trigger chan enode.ID, interval time.Duration) {
|
||||
tick := time.NewTicker(interval)
|
||||
defer tick.Stop()
|
||||
for {
|
||||
|
|
|
@ -20,7 +20,7 @@ import (
|
|||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
)
|
||||
|
||||
// Simulation provides a framework for running actions in a simulated network
|
||||
|
@ -55,7 +55,7 @@ func (s *Simulation) Run(ctx context.Context, step *Step) (result *StepResult) {
|
|||
}
|
||||
|
||||
// wait for all node expectations to either pass, error or timeout
|
||||
nodes := make(map[discover.NodeID]struct{}, len(step.Expect.Nodes))
|
||||
nodes := make(map[enode.ID]struct{}, len(step.Expect.Nodes))
|
||||
for _, id := range step.Expect.Nodes {
|
||||
nodes[id] = struct{}{}
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ type Step struct {
|
|||
|
||||
// Trigger is a channel which receives node ids and triggers an
|
||||
// expectation check for that node
|
||||
Trigger chan discover.NodeID
|
||||
Trigger chan enode.ID
|
||||
|
||||
// Expect is the expectation to wait for when performing this step
|
||||
Expect *Expectation
|
||||
|
@ -127,15 +127,15 @@ type Step struct {
|
|||
|
||||
type Expectation struct {
|
||||
// Nodes is a list of nodes to check
|
||||
Nodes []discover.NodeID
|
||||
Nodes []enode.ID
|
||||
|
||||
// Check checks whether a given node meets the expectation
|
||||
Check func(context.Context, discover.NodeID) (bool, error)
|
||||
Check func(context.Context, enode.ID) (bool, error)
|
||||
}
|
||||
|
||||
func newStepResult() *StepResult {
|
||||
return &StepResult{
|
||||
Passes: make(map[discover.NodeID]time.Time),
|
||||
Passes: make(map[enode.ID]time.Time),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,7 +150,7 @@ type StepResult struct {
|
|||
FinishedAt time.Time
|
||||
|
||||
// Passes are the timestamps of the successful node expectations
|
||||
Passes map[discover.NodeID]time.Time
|
||||
Passes map[enode.ID]time.Time
|
||||
|
||||
// NetworkEvents are the network events which occurred during the step
|
||||
NetworkEvents []*Event
|
||||
|
|
|
@ -21,22 +21,22 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
)
|
||||
|
||||
type TestPeer interface {
|
||||
ID() discover.NodeID
|
||||
ID() enode.ID
|
||||
Drop(error)
|
||||
}
|
||||
|
||||
// TestPeerPool is an example peerPool to demonstrate registration of peer connections
|
||||
type TestPeerPool struct {
|
||||
lock sync.Mutex
|
||||
peers map[discover.NodeID]TestPeer
|
||||
peers map[enode.ID]TestPeer
|
||||
}
|
||||
|
||||
func NewTestPeerPool() *TestPeerPool {
|
||||
return &TestPeerPool{peers: make(map[discover.NodeID]TestPeer)}
|
||||
return &TestPeerPool{peers: make(map[enode.ID]TestPeer)}
|
||||
}
|
||||
|
||||
func (p *TestPeerPool) Add(peer TestPeer) {
|
||||
|
@ -53,14 +53,14 @@ func (p *TestPeerPool) Remove(peer TestPeer) {
|
|||
delete(p.peers, peer.ID())
|
||||
}
|
||||
|
||||
func (p *TestPeerPool) Has(id discover.NodeID) bool {
|
||||
func (p *TestPeerPool) Has(id enode.ID) bool {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
_, ok := p.peers[id]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (p *TestPeerPool) Get(id discover.NodeID) TestPeer {
|
||||
func (p *TestPeerPool) Get(id enode.ID) TestPeer {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
return p.peers[id]
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
)
|
||||
|
||||
|
@ -35,7 +35,7 @@ var errTimedOut = errors.New("timed out")
|
|||
// receive (expect) messages
|
||||
type ProtocolSession struct {
|
||||
Server *p2p.Server
|
||||
IDs []discover.NodeID
|
||||
Nodes []*enode.Node
|
||||
adapter *adapters.SimAdapter
|
||||
events chan *p2p.PeerEvent
|
||||
}
|
||||
|
@ -56,32 +56,32 @@ type Exchange struct {
|
|||
// Trigger is part of the exchange, incoming message for the pivot node
|
||||
// sent by a peer
|
||||
type Trigger struct {
|
||||
Msg interface{} // type of message to be sent
|
||||
Code uint64 // code of message is given
|
||||
Peer discover.NodeID // the peer to send the message to
|
||||
Timeout time.Duration // timeout duration for the sending
|
||||
Msg interface{} // type of message to be sent
|
||||
Code uint64 // code of message is given
|
||||
Peer enode.ID // the peer to send the message to
|
||||
Timeout time.Duration // timeout duration for the sending
|
||||
}
|
||||
|
||||
// Expect is part of an exchange, outgoing message from the pivot node
|
||||
// received by a peer
|
||||
type Expect struct {
|
||||
Msg interface{} // type of message to expect
|
||||
Code uint64 // code of message is now given
|
||||
Peer discover.NodeID // the peer that expects the message
|
||||
Timeout time.Duration // timeout duration for receiving
|
||||
Msg interface{} // type of message to expect
|
||||
Code uint64 // code of message is now given
|
||||
Peer enode.ID // the peer that expects the message
|
||||
Timeout time.Duration // timeout duration for receiving
|
||||
}
|
||||
|
||||
// Disconnect represents a disconnect event, used and checked by TestDisconnected
|
||||
type Disconnect struct {
|
||||
Peer discover.NodeID // discconnected peer
|
||||
Error error // disconnect reason
|
||||
Peer enode.ID // discconnected peer
|
||||
Error error // disconnect reason
|
||||
}
|
||||
|
||||
// trigger sends messages from peers
|
||||
func (s *ProtocolSession) trigger(trig Trigger) error {
|
||||
simNode, ok := s.adapter.GetNode(trig.Peer)
|
||||
if !ok {
|
||||
return fmt.Errorf("trigger: peer %v does not exist (1- %v)", trig.Peer, len(s.IDs))
|
||||
return fmt.Errorf("trigger: peer %v does not exist (1- %v)", trig.Peer, len(s.Nodes))
|
||||
}
|
||||
mockNode, ok := simNode.Services()[0].(*mockNode)
|
||||
if !ok {
|
||||
|
@ -111,7 +111,7 @@ func (s *ProtocolSession) trigger(trig Trigger) error {
|
|||
// expect checks an expectation of a message sent out by the pivot node
|
||||
func (s *ProtocolSession) expect(exps []Expect) error {
|
||||
// construct a map of expectations for each node
|
||||
peerExpects := make(map[discover.NodeID][]Expect)
|
||||
peerExpects := make(map[enode.ID][]Expect)
|
||||
for _, exp := range exps {
|
||||
if exp.Msg == nil {
|
||||
return errors.New("no message to expect")
|
||||
|
@ -120,11 +120,11 @@ func (s *ProtocolSession) expect(exps []Expect) error {
|
|||
}
|
||||
|
||||
// construct a map of mockNodes for each node
|
||||
mockNodes := make(map[discover.NodeID]*mockNode)
|
||||
mockNodes := make(map[enode.ID]*mockNode)
|
||||
for nodeID := range peerExpects {
|
||||
simNode, ok := s.adapter.GetNode(nodeID)
|
||||
if !ok {
|
||||
return fmt.Errorf("trigger: peer %v does not exist (1- %v)", nodeID, len(s.IDs))
|
||||
return fmt.Errorf("trigger: peer %v does not exist (1- %v)", nodeID, len(s.Nodes))
|
||||
}
|
||||
mockNode, ok := simNode.Services()[0].(*mockNode)
|
||||
if !ok {
|
||||
|
@ -253,7 +253,7 @@ func (s *ProtocolSession) testExchange(e Exchange) error {
|
|||
// TestDisconnected tests the disconnections given as arguments
|
||||
// the disconnect structs describe what disconnect error is expected on which peer
|
||||
func (s *ProtocolSession) TestDisconnected(disconnects ...*Disconnect) error {
|
||||
expects := make(map[discover.NodeID]error)
|
||||
expects := make(map[enode.ID]error)
|
||||
for _, disconnect := range disconnects {
|
||||
expects[disconnect.Peer] = disconnect.Error
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
|
@ -52,7 +52,7 @@ type ProtocolTester struct {
|
|||
// NewProtocolTester constructs a new ProtocolTester
|
||||
// it takes as argument the pivot node id, the number of dummy peers and the
|
||||
// protocol run function called on a peer connection by the p2p server
|
||||
func NewProtocolTester(t *testing.T, id discover.NodeID, n int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester {
|
||||
func NewProtocolTester(t *testing.T, id enode.ID, n int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester {
|
||||
services := adapters.Services{
|
||||
"test": func(ctx *adapters.ServiceContext) (node.Service, error) {
|
||||
return &testNode{run}, nil
|
||||
|
@ -76,17 +76,17 @@ func NewProtocolTester(t *testing.T, id discover.NodeID, n int, run func(*p2p.Pe
|
|||
|
||||
node := net.GetNode(id).Node.(*adapters.SimNode)
|
||||
peers := make([]*adapters.NodeConfig, n)
|
||||
peerIDs := make([]discover.NodeID, n)
|
||||
nodes := make([]*enode.Node, n)
|
||||
for i := 0; i < n; i++ {
|
||||
peers[i] = adapters.RandomNodeConfig()
|
||||
peers[i].Services = []string{"mock"}
|
||||
peerIDs[i] = peers[i].ID
|
||||
nodes[i] = peers[i].Node()
|
||||
}
|
||||
events := make(chan *p2p.PeerEvent, 1000)
|
||||
node.SubscribeEvents(events)
|
||||
ps := &ProtocolSession{
|
||||
Server: node.Server(),
|
||||
IDs: peerIDs,
|
||||
Nodes: nodes,
|
||||
adapter: adapter,
|
||||
events: events,
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ func (t *ProtocolTester) Stop() error {
|
|||
|
||||
// Connect brings up the remote peer node and connects it using the
|
||||
// p2p/simulations network connection with the in memory network adapter
|
||||
func (t *ProtocolTester) Connect(selfID discover.NodeID, peers ...*adapters.NodeConfig) {
|
||||
func (t *ProtocolTester) Connect(selfID enode.ID, peers ...*adapters.NodeConfig) {
|
||||
for _, peer := range peers {
|
||||
log.Trace(fmt.Sprintf("start node %v", peer.ID))
|
||||
if _, err := t.network.NewNodeWithConfig(peer); err != nil {
|
||||
|
|
|
@ -27,7 +27,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/contracts/ens"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/swarm/log"
|
||||
"github.com/ethereum/go-ethereum/swarm/network"
|
||||
"github.com/ethereum/go-ethereum/swarm/pss"
|
||||
|
@ -117,7 +117,7 @@ func (c *Config) Init(prvKey *ecdsa.PrivateKey) {
|
|||
|
||||
c.PublicKey = pubkeyhex
|
||||
c.BzzKey = keyhex
|
||||
c.NodeID = discover.PubkeyID(&prvKey.PublicKey).String()
|
||||
c.NodeID = enode.PubkeyToIDV4(&prvKey.PublicKey).String()
|
||||
|
||||
if c.SwapEnabled {
|
||||
c.Swap.Init(c.Contract, prvKey)
|
||||
|
|
|
@ -87,7 +87,7 @@ func NotifyPeer(p *BzzAddr, k *Kademlia) {
|
|||
// unless already notified during the connection session
|
||||
func (d *Peer) NotifyPeer(a *BzzAddr, po uint8) {
|
||||
// immediately return
|
||||
if (po < d.getDepth() && pot.ProxCmp(d.localAddr, d, a) != 1) || d.seen(a) {
|
||||
if (po < d.getDepth() && pot.ProxCmp(d.kad.BaseAddr(), d, a) != 1) || d.seen(a) {
|
||||
return
|
||||
}
|
||||
resp := &peersMsg{
|
||||
|
@ -161,7 +161,7 @@ func (d *Peer) handleSubPeersMsg(msg *subPeersMsg) error {
|
|||
d.setDepth(msg.Depth)
|
||||
var peers []*BzzAddr
|
||||
d.kad.EachConn(d.Over(), 255, func(p *Peer, po int, isproxbin bool) bool {
|
||||
if pob, _ := pof(d, d.localAddr, 0); pob > po {
|
||||
if pob, _ := pof(d, d.kad.BaseAddr(), 0); pob > po {
|
||||
return false
|
||||
}
|
||||
if !d.seen(p.BzzAddr) {
|
||||
|
|
|
@ -31,8 +31,8 @@ func TestDiscovery(t *testing.T) {
|
|||
params := NewHiveParams()
|
||||
s, pp := newHiveTester(t, params, 1, nil)
|
||||
|
||||
id := s.IDs[0]
|
||||
raddr := NewAddrFromNodeID(id)
|
||||
node := s.Nodes[0]
|
||||
raddr := NewAddr(node)
|
||||
pp.Register(raddr)
|
||||
|
||||
// start the hive and wait for the connection
|
||||
|
@ -46,7 +46,7 @@ func TestDiscovery(t *testing.T) {
|
|||
{
|
||||
Code: 1,
|
||||
Msg: &subPeersMsg{Depth: 0},
|
||||
Peer: id,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -22,7 +22,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||
)
|
||||
|
||||
|
@ -32,7 +32,7 @@ var searchTimeout = 1 * time.Second
|
|||
// Also used in stream delivery.
|
||||
var RequestTimeout = 10 * time.Second
|
||||
|
||||
type RequestFunc func(context.Context, *Request) (*discover.NodeID, chan struct{}, error)
|
||||
type RequestFunc func(context.Context, *Request) (*enode.ID, chan struct{}, error)
|
||||
|
||||
// Fetcher is created when a chunk is not found locally. It starts a request handler loop once and
|
||||
// keeps it alive until all active requests are completed. This can happen:
|
||||
|
@ -41,18 +41,18 @@ type RequestFunc func(context.Context, *Request) (*discover.NodeID, chan struct{
|
|||
// Fetcher self destroys itself after it is completed.
|
||||
// TODO: cancel all forward requests after termination
|
||||
type Fetcher struct {
|
||||
protoRequestFunc RequestFunc // request function fetcher calls to issue retrieve request for a chunk
|
||||
addr storage.Address // the address of the chunk to be fetched
|
||||
offerC chan *discover.NodeID // channel of sources (peer node id strings)
|
||||
protoRequestFunc RequestFunc // request function fetcher calls to issue retrieve request for a chunk
|
||||
addr storage.Address // the address of the chunk to be fetched
|
||||
offerC chan *enode.ID // channel of sources (peer node id strings)
|
||||
requestC chan struct{}
|
||||
skipCheck bool
|
||||
}
|
||||
|
||||
type Request struct {
|
||||
Addr storage.Address // chunk address
|
||||
Source *discover.NodeID // nodeID of peer to request from (can be nil)
|
||||
SkipCheck bool // whether to offer the chunk first or deliver directly
|
||||
peersToSkip *sync.Map // peers not to request chunk from (only makes sense if source is nil)
|
||||
Addr storage.Address // chunk address
|
||||
Source *enode.ID // nodeID of peer to request from (can be nil)
|
||||
SkipCheck bool // whether to offer the chunk first or deliver directly
|
||||
peersToSkip *sync.Map // peers not to request chunk from (only makes sense if source is nil)
|
||||
}
|
||||
|
||||
// NewRequest returns a new instance of Request based on chunk address skip check and
|
||||
|
@ -112,14 +112,14 @@ func NewFetcher(addr storage.Address, rf RequestFunc, skipCheck bool) *Fetcher {
|
|||
return &Fetcher{
|
||||
addr: addr,
|
||||
protoRequestFunc: rf,
|
||||
offerC: make(chan *discover.NodeID),
|
||||
offerC: make(chan *enode.ID),
|
||||
requestC: make(chan struct{}),
|
||||
skipCheck: skipCheck,
|
||||
}
|
||||
}
|
||||
|
||||
// Offer is called when an upstream peer offers the chunk via syncing as part of `OfferedHashesMsg` and the node does not have the chunk locally.
|
||||
func (f *Fetcher) Offer(ctx context.Context, source *discover.NodeID) {
|
||||
func (f *Fetcher) Offer(ctx context.Context, source *enode.ID) {
|
||||
// First we need to have this select to make sure that we return if context is done
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
@ -156,13 +156,13 @@ func (f *Fetcher) Request(ctx context.Context) {
|
|||
// it keeps the Fetcher alive within the lifecycle of the passed context
|
||||
func (f *Fetcher) run(ctx context.Context, peers *sync.Map) {
|
||||
var (
|
||||
doRequest bool // determines if retrieval is initiated in the current iteration
|
||||
wait *time.Timer // timer for search timeout
|
||||
waitC <-chan time.Time // timer channel
|
||||
sources []*discover.NodeID // known sources, ie. peers that offered the chunk
|
||||
requested bool // true if the chunk was actually requested
|
||||
doRequest bool // determines if retrieval is initiated in the current iteration
|
||||
wait *time.Timer // timer for search timeout
|
||||
waitC <-chan time.Time // timer channel
|
||||
sources []*enode.ID // known sources, ie. peers that offered the chunk
|
||||
requested bool // true if the chunk was actually requested
|
||||
)
|
||||
gone := make(chan *discover.NodeID) // channel to signal that a peer we requested from disconnected
|
||||
gone := make(chan *enode.ID) // channel to signal that a peer we requested from disconnected
|
||||
|
||||
// loop that keeps the fetching process alive
|
||||
// after every request a timer is set. If this goes off we request again from another peer
|
||||
|
@ -251,9 +251,9 @@ func (f *Fetcher) run(ctx context.Context, peers *sync.Map) {
|
|||
// * the peer's address is added to the set of peers to skip
|
||||
// * the peer's address is removed from prospective sources, and
|
||||
// * a go routine is started that reports on the gone channel if the peer is disconnected (or terminated their streamer)
|
||||
func (f *Fetcher) doRequest(ctx context.Context, gone chan *discover.NodeID, peersToSkip *sync.Map, sources []*discover.NodeID) ([]*discover.NodeID, error) {
|
||||
func (f *Fetcher) doRequest(ctx context.Context, gone chan *enode.ID, peersToSkip *sync.Map, sources []*enode.ID) ([]*enode.ID, error) {
|
||||
var i int
|
||||
var sourceID *discover.NodeID
|
||||
var sourceID *enode.ID
|
||||
var quit chan struct{}
|
||||
|
||||
req := &Request{
|
||||
|
|
|
@ -22,11 +22,11 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
)
|
||||
|
||||
var requestedPeerID = discover.MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439")
|
||||
var sourcePeerID = discover.MustHexID("2dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439")
|
||||
var requestedPeerID = enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8")
|
||||
var sourcePeerID = enode.HexID("99d8594b52298567d2ca3f4c441a5ba0140ee9245e26460d01102a52773c73b9")
|
||||
|
||||
// mockRequester pushes every request to the requestC channel when its doRequest function is called
|
||||
type mockRequester struct {
|
||||
|
@ -45,7 +45,7 @@ func newMockRequester(waitTimes ...time.Duration) *mockRequester {
|
|||
}
|
||||
}
|
||||
|
||||
func (m *mockRequester) doRequest(ctx context.Context, request *Request) (*discover.NodeID, chan struct{}, error) {
|
||||
func (m *mockRequester) doRequest(ctx context.Context, request *Request) (*enode.ID, chan struct{}, error) {
|
||||
waitTime := time.Duration(0)
|
||||
if m.ctr < len(m.waitTimes) {
|
||||
waitTime = m.waitTimes[m.ctr]
|
||||
|
@ -389,9 +389,9 @@ func TestFetcherRequestQuitRetriesRequest(t *testing.T) {
|
|||
// and not skip unknown one.
|
||||
func TestRequestSkipPeer(t *testing.T) {
|
||||
addr := make([]byte, 32)
|
||||
peers := []discover.NodeID{
|
||||
discover.MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
discover.MustHexID("2dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
|
||||
peers := []enode.ID{
|
||||
enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8"),
|
||||
enode.HexID("99d8594b52298567d2ca3f4c441a5ba0140ee9245e26460d01102a52773c73b9"),
|
||||
}
|
||||
|
||||
peersToSkip := new(sync.Map)
|
||||
|
@ -411,7 +411,7 @@ func TestRequestSkipPeer(t *testing.T) {
|
|||
// after RequestTimeout has passed.
|
||||
func TestRequestSkipPeerExpired(t *testing.T) {
|
||||
addr := make([]byte, 32)
|
||||
peer := discover.MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439")
|
||||
peer := enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8")
|
||||
|
||||
// set RequestTimeout to a low value and reset it after the test
|
||||
defer func(t time.Duration) { RequestTimeout = t }(RequestTimeout)
|
||||
|
@ -437,7 +437,7 @@ func TestRequestSkipPeerExpired(t *testing.T) {
|
|||
// by value to peersToSkip map is not time.Duration.
|
||||
func TestRequestSkipPeerPermanent(t *testing.T) {
|
||||
addr := make([]byte, 32)
|
||||
peer := discover.MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439")
|
||||
peer := enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8")
|
||||
|
||||
// set RequestTimeout to a low value and reset it after the test
|
||||
defer func(t time.Duration) { RequestTimeout = t }(RequestTimeout)
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/swarm/log"
|
||||
"github.com/ethereum/go-ethereum/swarm/state"
|
||||
)
|
||||
|
@ -56,12 +56,13 @@ func NewHiveParams() *HiveParams {
|
|||
|
||||
// Hive manages network connections of the swarm node
|
||||
type Hive struct {
|
||||
*HiveParams // settings
|
||||
*Kademlia // the overlay connectiviy driver
|
||||
Store state.Store // storage interface to save peers across sessions
|
||||
addPeer func(*discover.Node) // server callback to connect to a peer
|
||||
*HiveParams // settings
|
||||
*Kademlia // the overlay connectiviy driver
|
||||
Store state.Store // storage interface to save peers across sessions
|
||||
addPeer func(*enode.Node) // server callback to connect to a peer
|
||||
// bookkeeping
|
||||
lock sync.Mutex
|
||||
peers map[enode.ID]*BzzPeer
|
||||
ticker *time.Ticker
|
||||
}
|
||||
|
||||
|
@ -74,6 +75,7 @@ func NewHive(params *HiveParams, kad *Kademlia, store state.Store) *Hive {
|
|||
HiveParams: params,
|
||||
Kademlia: kad,
|
||||
Store: store,
|
||||
peers: make(map[enode.ID]*BzzPeer),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,7 +139,7 @@ func (h *Hive) connect() {
|
|||
}
|
||||
|
||||
log.Trace(fmt.Sprintf("%08x hive connect() suggested %08x", h.BaseAddr()[:4], addr.Address()[:4]))
|
||||
under, err := discover.ParseNode(string(addr.Under()))
|
||||
under, err := enode.ParseV4(string(addr.Under()))
|
||||
if err != nil {
|
||||
log.Warn(fmt.Sprintf("%08x unable to connect to bee %08x: invalid node URL: %v", h.BaseAddr()[:4], addr.Address()[:4], err))
|
||||
continue
|
||||
|
@ -149,6 +151,9 @@ func (h *Hive) connect() {
|
|||
|
||||
// Run protocol run function
|
||||
func (h *Hive) Run(p *BzzPeer) error {
|
||||
h.trackPeer(p)
|
||||
defer h.untrackPeer(p)
|
||||
|
||||
dp := NewPeer(p, h.Kademlia)
|
||||
depth, changed := h.On(dp)
|
||||
// if we want discovery, advertise change of depth
|
||||
|
@ -166,6 +171,18 @@ func (h *Hive) Run(p *BzzPeer) error {
|
|||
return dp.Run(dp.HandleMsg)
|
||||
}
|
||||
|
||||
func (h *Hive) trackPeer(p *BzzPeer) {
|
||||
h.lock.Lock()
|
||||
h.peers[p.ID()] = p
|
||||
h.lock.Unlock()
|
||||
}
|
||||
|
||||
func (h *Hive) untrackPeer(p *BzzPeer) {
|
||||
h.lock.Lock()
|
||||
delete(h.peers, p.ID())
|
||||
h.lock.Unlock()
|
||||
}
|
||||
|
||||
// NodeInfo function is used by the p2p.server RPC interface to display
|
||||
// protocol specific node information
|
||||
func (h *Hive) NodeInfo() interface{} {
|
||||
|
@ -174,8 +191,15 @@ func (h *Hive) NodeInfo() interface{} {
|
|||
|
||||
// PeerInfo function is used by the p2p.server RPC interface to display
|
||||
// protocol specific information any connected peer referred to by their NodeID
|
||||
func (h *Hive) PeerInfo(id discover.NodeID) interface{} {
|
||||
addr := NewAddrFromNodeID(id)
|
||||
func (h *Hive) PeerInfo(id enode.ID) interface{} {
|
||||
h.lock.Lock()
|
||||
p := h.peers[id]
|
||||
h.lock.Unlock()
|
||||
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
addr := NewAddr(p.Node())
|
||||
return struct {
|
||||
OAddr hexutil.Bytes
|
||||
UAddr hexutil.Bytes
|
||||
|
|
|
@ -39,8 +39,8 @@ func TestRegisterAndConnect(t *testing.T) {
|
|||
params := NewHiveParams()
|
||||
s, pp := newHiveTester(t, params, 1, nil)
|
||||
|
||||
id := s.IDs[0]
|
||||
raddr := NewAddrFromNodeID(id)
|
||||
node := s.Nodes[0]
|
||||
raddr := NewAddr(node)
|
||||
pp.Register(raddr)
|
||||
|
||||
// start the hive and wait for the connection
|
||||
|
@ -51,7 +51,7 @@ func TestRegisterAndConnect(t *testing.T) {
|
|||
defer pp.Stop()
|
||||
// retrieve and broadcast
|
||||
err = s.TestDisconnected(&p2ptest.Disconnect{
|
||||
Peer: s.IDs[0],
|
||||
Peer: s.Nodes[0].ID(),
|
||||
Error: nil,
|
||||
})
|
||||
|
||||
|
@ -75,8 +75,8 @@ func TestHiveStatePersistance(t *testing.T) {
|
|||
s, pp := newHiveTester(t, params, 5, store)
|
||||
|
||||
peers := make(map[string]bool)
|
||||
for _, id := range s.IDs {
|
||||
raddr := NewAddrFromNodeID(id)
|
||||
for _, node := range s.Nodes {
|
||||
raddr := NewAddr(node)
|
||||
pp.Register(raddr)
|
||||
peers[raddr.String()] = true
|
||||
}
|
||||
|
@ -102,7 +102,10 @@ func TestHiveStatePersistance(t *testing.T) {
|
|||
i++
|
||||
return true
|
||||
})
|
||||
if len(peers) != 0 || i != 5 {
|
||||
t.Fatalf("invalid peers loaded")
|
||||
if i != 5 {
|
||||
t.Errorf("invalid number of entries: got %v, want %v", i, 5)
|
||||
}
|
||||
if len(peers) != 0 {
|
||||
t.Fatalf("%d peers left over: %v", len(peers), peers)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
@ -38,8 +38,8 @@ import (
|
|||
var (
|
||||
currentNetworkID int
|
||||
cnt int
|
||||
nodeMap map[int][]discover.NodeID
|
||||
kademlias map[discover.NodeID]*Kademlia
|
||||
nodeMap map[int][]enode.ID
|
||||
kademlias map[enode.ID]*Kademlia
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -70,7 +70,7 @@ func TestNetworkID(t *testing.T) {
|
|||
//arbitrarily set the number of nodes. It could be any number
|
||||
numNodes := 24
|
||||
//the nodeMap maps all nodes (slice value) with the same network ID (key)
|
||||
nodeMap = make(map[int][]discover.NodeID)
|
||||
nodeMap = make(map[int][]enode.ID)
|
||||
//set up the network and connect nodes
|
||||
net, err := setupNetwork(numNodes)
|
||||
if err != nil {
|
||||
|
@ -95,7 +95,7 @@ func TestNetworkID(t *testing.T) {
|
|||
kademlias[node].EachAddr(nil, 0, func(addr *BzzAddr, _ int, _ bool) bool {
|
||||
found := false
|
||||
for _, nd := range netIDGroup {
|
||||
p := ToOverlayAddr(nd.Bytes())
|
||||
p := nd.Bytes()
|
||||
if bytes.Equal(p, addr.Address()) {
|
||||
found = true
|
||||
}
|
||||
|
@ -183,12 +183,11 @@ func setupNetwork(numnodes int) (net *simulations.Network, err error) {
|
|||
}
|
||||
|
||||
func newServices() adapters.Services {
|
||||
kademlias = make(map[discover.NodeID]*Kademlia)
|
||||
kademlia := func(id discover.NodeID) *Kademlia {
|
||||
kademlias = make(map[enode.ID]*Kademlia)
|
||||
kademlia := func(id enode.ID) *Kademlia {
|
||||
if k, ok := kademlias[id]; ok {
|
||||
return k
|
||||
}
|
||||
addr := NewAddrFromNodeID(id)
|
||||
params := NewKadParams()
|
||||
params.MinProxBinSize = 2
|
||||
params.MaxBinSize = 3
|
||||
|
@ -196,19 +195,19 @@ func newServices() adapters.Services {
|
|||
params.MaxRetries = 1000
|
||||
params.RetryExponent = 2
|
||||
params.RetryInterval = 1000000
|
||||
kademlias[id] = NewKademlia(addr.Over(), params)
|
||||
kademlias[id] = NewKademlia(id[:], params)
|
||||
return kademlias[id]
|
||||
}
|
||||
return adapters.Services{
|
||||
"bzz": func(ctx *adapters.ServiceContext) (node.Service, error) {
|
||||
addr := NewAddrFromNodeID(ctx.Config.ID)
|
||||
addr := NewAddr(ctx.Config.Node())
|
||||
hp := NewHiveParams()
|
||||
hp.Discovery = false
|
||||
cnt++
|
||||
//assign the network ID
|
||||
currentNetworkID = cnt % NumberOfNets
|
||||
if ok := nodeMap[currentNetworkID]; ok == nil {
|
||||
nodeMap[currentNetworkID] = make([]discover.NodeID, 0)
|
||||
nodeMap[currentNetworkID] = make([]enode.ID, 0)
|
||||
}
|
||||
//add this node to the group sharing the same network ID
|
||||
nodeMap[currentNetworkID] = append(nodeMap[currentNetworkID], ctx.Config.ID)
|
||||
|
@ -224,7 +223,7 @@ func newServices() adapters.Services {
|
|||
}
|
||||
}
|
||||
|
||||
func watchSubscriptionEvents(ctx context.Context, id discover.NodeID, client *rpc.Client, errc chan error, quitC chan struct{}) {
|
||||
func watchSubscriptionEvents(ctx context.Context, id enode.ID, client *rpc.Client, errc chan error, quitC chan struct{}) {
|
||||
events := make(chan *p2p.PeerEvent)
|
||||
sub, err := client.Subscribe(context.Background(), "admin", events, "peerEvents")
|
||||
if err != nil {
|
||||
|
|
|
@ -26,7 +26,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/protocols"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/swarm/log"
|
||||
|
@ -78,7 +78,7 @@ type Bzz struct {
|
|||
LightNode bool
|
||||
localAddr *BzzAddr
|
||||
mtx sync.Mutex
|
||||
handshakes map[discover.NodeID]*HandshakeMsg
|
||||
handshakes map[enode.ID]*HandshakeMsg
|
||||
streamerSpec *protocols.Spec
|
||||
streamerRun func(*BzzPeer) error
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ func NewBzz(config *BzzConfig, kad *Kademlia, store state.Store, streamerSpec *p
|
|||
NetworkID: config.NetworkID,
|
||||
LightNode: config.LightNode,
|
||||
localAddr: &BzzAddr{config.OverlayAddr, config.UnderlayAddr},
|
||||
handshakes: make(map[discover.NodeID]*HandshakeMsg),
|
||||
handshakes: make(map[enode.ID]*HandshakeMsg),
|
||||
streamerRun: streamerRun,
|
||||
streamerSpec: streamerSpec,
|
||||
}
|
||||
|
@ -183,7 +183,6 @@ func (b *Bzz) RunProtocol(spec *protocols.Spec, run func(*BzzPeer) error) func(*
|
|||
// the handshake has succeeded so construct the BzzPeer and run the protocol
|
||||
peer := &BzzPeer{
|
||||
Peer: protocols.NewPeer(p, rw, spec),
|
||||
localAddr: b.localAddr,
|
||||
BzzAddr: handshake.peerAddr,
|
||||
lastActive: time.Now(),
|
||||
LightNode: handshake.LightNode,
|
||||
|
@ -218,14 +217,14 @@ func (b *Bzz) performHandshake(p *protocols.Peer, handshake *HandshakeMsg) error
|
|||
func (b *Bzz) runBzz(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||
handshake, _ := b.GetHandshake(p.ID())
|
||||
if !<-handshake.init {
|
||||
return fmt.Errorf("%08x: bzz already started on peer %08x", b.localAddr.Over()[:4], ToOverlayAddr(p.ID().Bytes())[:4])
|
||||
return fmt.Errorf("%08x: bzz already started on peer %08x", b.localAddr.Over()[:4], p.ID().Bytes()[:4])
|
||||
}
|
||||
close(handshake.init)
|
||||
defer b.removeHandshake(p.ID())
|
||||
peer := protocols.NewPeer(p, rw, BzzSpec)
|
||||
err := b.performHandshake(peer, handshake)
|
||||
if err != nil {
|
||||
log.Warn(fmt.Sprintf("%08x: handshake failed with remote peer %08x: %v", b.localAddr.Over()[:4], ToOverlayAddr(p.ID().Bytes())[:4], err))
|
||||
log.Warn(fmt.Sprintf("%08x: handshake failed with remote peer %08x: %v", b.localAddr.Over()[:4], p.ID().Bytes()[:4], err))
|
||||
|
||||
return err
|
||||
}
|
||||
|
@ -242,18 +241,13 @@ func (b *Bzz) runBzz(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
|||
// implements the Peer interface and all interfaces Peer implements: Addr, OverlayPeer
|
||||
type BzzPeer struct {
|
||||
*protocols.Peer // represents the connection for online peers
|
||||
localAddr *BzzAddr // local Peers address
|
||||
*BzzAddr // remote address -> implements Addr interface = protocols.Peer
|
||||
lastActive time.Time // time is updated whenever mutexes are releasing
|
||||
LightNode bool
|
||||
}
|
||||
|
||||
func NewBzzPeer(p *protocols.Peer, addr *BzzAddr) *BzzPeer {
|
||||
return &BzzPeer{
|
||||
Peer: p,
|
||||
localAddr: addr,
|
||||
BzzAddr: NewAddrFromNodeID(p.ID()),
|
||||
}
|
||||
func NewBzzPeer(p *protocols.Peer) *BzzPeer {
|
||||
return &BzzPeer{Peer: p, BzzAddr: NewAddr(p.Node())}
|
||||
}
|
||||
|
||||
// LastActive returns the time the peer was last active
|
||||
|
@ -261,6 +255,14 @@ func (p *BzzPeer) LastActive() time.Time {
|
|||
return p.lastActive
|
||||
}
|
||||
|
||||
// ID returns the peer's underlay node identifier.
|
||||
func (p *BzzPeer) ID() enode.ID {
|
||||
// This is here to resolve a method tie: both protocols.Peer and BzzAddr are embedded
|
||||
// into the struct and provide ID(). The protocols.Peer version is faster, ensure it
|
||||
// gets used.
|
||||
return p.Peer.ID()
|
||||
}
|
||||
|
||||
/*
|
||||
Handshake
|
||||
|
||||
|
@ -301,14 +303,14 @@ func (b *Bzz) checkHandshake(hs interface{}) error {
|
|||
|
||||
// removeHandshake removes handshake for peer with peerID
|
||||
// from the bzz handshake store
|
||||
func (b *Bzz) removeHandshake(peerID discover.NodeID) {
|
||||
func (b *Bzz) removeHandshake(peerID enode.ID) {
|
||||
b.mtx.Lock()
|
||||
defer b.mtx.Unlock()
|
||||
delete(b.handshakes, peerID)
|
||||
}
|
||||
|
||||
// GetHandshake returns the bzz handhake that the remote peer with peerID sent
|
||||
func (b *Bzz) GetHandshake(peerID discover.NodeID) (*HandshakeMsg, bool) {
|
||||
func (b *Bzz) GetHandshake(peerID enode.ID) (*HandshakeMsg, bool) {
|
||||
b.mtx.Lock()
|
||||
defer b.mtx.Unlock()
|
||||
handshake, found := b.handshakes[peerID]
|
||||
|
@ -336,24 +338,28 @@ type BzzAddr struct {
|
|||
UAddr []byte
|
||||
}
|
||||
|
||||
// Address implements OverlayPeer interface to be used in Overlay
|
||||
// Address implements OverlayPeer interface to be used in Overlay.
|
||||
func (a *BzzAddr) Address() []byte {
|
||||
return a.OAddr
|
||||
}
|
||||
|
||||
// Over returns the overlay address
|
||||
// Over returns the overlay address.
|
||||
func (a *BzzAddr) Over() []byte {
|
||||
return a.OAddr
|
||||
}
|
||||
|
||||
// Under returns the underlay address
|
||||
// Under returns the underlay address.
|
||||
func (a *BzzAddr) Under() []byte {
|
||||
return a.UAddr
|
||||
}
|
||||
|
||||
// ID returns the nodeID from the underlay enode address
|
||||
func (a *BzzAddr) ID() discover.NodeID {
|
||||
return discover.MustParseNode(string(a.UAddr)).ID
|
||||
// ID returns the node identifier in the underlay.
|
||||
func (a *BzzAddr) ID() enode.ID {
|
||||
n, err := enode.ParseV4(string(a.UAddr))
|
||||
if err != nil {
|
||||
return enode.ID{}
|
||||
}
|
||||
return n.ID()
|
||||
}
|
||||
|
||||
// Update updates the underlay address of a peer record
|
||||
|
@ -372,38 +378,11 @@ func RandomAddr() *BzzAddr {
|
|||
if err != nil {
|
||||
panic("unable to generate key")
|
||||
}
|
||||
pubkey := crypto.FromECDSAPub(&key.PublicKey)
|
||||
var id discover.NodeID
|
||||
copy(id[:], pubkey[1:])
|
||||
return NewAddrFromNodeID(id)
|
||||
node := enode.NewV4(&key.PublicKey, net.IP{127, 0, 0, 1}, 30303, 30303)
|
||||
return NewAddr(node)
|
||||
}
|
||||
|
||||
// NewNodeIDFromAddr transforms the underlay address to an adapters.NodeID
|
||||
func NewNodeIDFromAddr(addr *BzzAddr) discover.NodeID {
|
||||
log.Info(fmt.Sprintf("uaddr=%s", string(addr.UAddr)))
|
||||
node := discover.MustParseNode(string(addr.UAddr))
|
||||
return node.ID
|
||||
}
|
||||
|
||||
// NewAddrFromNodeID constucts a BzzAddr from a discover.NodeID
|
||||
// the overlay address is derived as the hash of the nodeID
|
||||
func NewAddrFromNodeID(id discover.NodeID) *BzzAddr {
|
||||
return &BzzAddr{
|
||||
OAddr: ToOverlayAddr(id.Bytes()),
|
||||
UAddr: []byte(discover.NewNode(id, net.IP{127, 0, 0, 1}, 30303, 30303).String()),
|
||||
}
|
||||
}
|
||||
|
||||
// NewAddrFromNodeIDAndPort constucts a BzzAddr from a discover.NodeID and port uint16
|
||||
// the overlay address is derived as the hash of the nodeID
|
||||
func NewAddrFromNodeIDAndPort(id discover.NodeID, host net.IP, port uint16) *BzzAddr {
|
||||
return &BzzAddr{
|
||||
OAddr: ToOverlayAddr(id.Bytes()),
|
||||
UAddr: []byte(discover.NewNode(id, host, port, port).String()),
|
||||
}
|
||||
}
|
||||
|
||||
// ToOverlayAddr creates an overlayaddress from a byte slice
|
||||
func ToOverlayAddr(id []byte) []byte {
|
||||
return crypto.Keccak256(id)
|
||||
// NewAddr constucts a BzzAddr from a node record.
|
||||
func NewAddr(node *enode.Node) *BzzAddr {
|
||||
return &BzzAddr{OAddr: node.ID().Bytes(), UAddr: []byte(node.String())}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/protocols"
|
||||
p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
|
||||
)
|
||||
|
@ -71,7 +71,7 @@ func (t *testStore) Save(key string, v []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func HandshakeMsgExchange(lhs, rhs *HandshakeMsg, id discover.NodeID) []p2ptest.Exchange {
|
||||
func HandshakeMsgExchange(lhs, rhs *HandshakeMsg, id enode.ID) []p2ptest.Exchange {
|
||||
|
||||
return []p2ptest.Exchange{
|
||||
{
|
||||
|
@ -108,17 +108,13 @@ func newBzzBaseTester(t *testing.T, n int, addr *BzzAddr, spec *protocols.Spec,
|
|||
}
|
||||
|
||||
protocol := func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||
return srv(&BzzPeer{
|
||||
Peer: protocols.NewPeer(p, rw, spec),
|
||||
localAddr: addr,
|
||||
BzzAddr: NewAddrFromNodeID(p.ID()),
|
||||
})
|
||||
return srv(&BzzPeer{Peer: protocols.NewPeer(p, rw, spec), BzzAddr: NewAddr(p.Node())})
|
||||
}
|
||||
|
||||
s := p2ptest.NewProtocolTester(t, NewNodeIDFromAddr(addr), n, protocol)
|
||||
s := p2ptest.NewProtocolTester(t, addr.ID(), n, protocol)
|
||||
|
||||
for _, id := range s.IDs {
|
||||
cs[id.String()] = make(chan bool)
|
||||
for _, node := range s.Nodes {
|
||||
cs[node.ID().String()] = make(chan bool)
|
||||
}
|
||||
|
||||
return &bzzTester{
|
||||
|
@ -150,7 +146,7 @@ func newBzz(addr *BzzAddr, lightNode bool) *Bzz {
|
|||
|
||||
func newBzzHandshakeTester(t *testing.T, n int, addr *BzzAddr, lightNode bool) *bzzTester {
|
||||
bzz := newBzz(addr, lightNode)
|
||||
pt := p2ptest.NewProtocolTester(t, NewNodeIDFromAddr(addr), n, bzz.runBzz)
|
||||
pt := p2ptest.NewProtocolTester(t, addr.ID(), n, bzz.runBzz)
|
||||
|
||||
return &bzzTester{
|
||||
addr: addr,
|
||||
|
@ -161,14 +157,14 @@ func newBzzHandshakeTester(t *testing.T, n int, addr *BzzAddr, lightNode bool) *
|
|||
|
||||
// should test handshakes in one exchange? parallelisation
|
||||
func (s *bzzTester) testHandshake(lhs, rhs *HandshakeMsg, disconnects ...*p2ptest.Disconnect) error {
|
||||
var peers []discover.NodeID
|
||||
id := NewNodeIDFromAddr(rhs.Addr)
|
||||
var peers []enode.ID
|
||||
id := rhs.Addr.ID()
|
||||
if len(disconnects) > 0 {
|
||||
for _, d := range disconnects {
|
||||
peers = append(peers, d.Peer)
|
||||
}
|
||||
} else {
|
||||
peers = []discover.NodeID{id}
|
||||
peers = []enode.ID{id}
|
||||
}
|
||||
|
||||
if err := s.TestExchanges(HandshakeMsgExchange(lhs, rhs, id)...); err != nil {
|
||||
|
@ -181,7 +177,7 @@ func (s *bzzTester) testHandshake(lhs, rhs *HandshakeMsg, disconnects ...*p2ptes
|
|||
|
||||
// If we don't expect disconnect, ensure peers remain connected
|
||||
err := s.TestDisconnected(&p2ptest.Disconnect{
|
||||
Peer: s.IDs[0],
|
||||
Peer: s.Nodes[0].ID(),
|
||||
Error: nil,
|
||||
})
|
||||
|
||||
|
@ -209,12 +205,12 @@ func TestBzzHandshakeNetworkIDMismatch(t *testing.T) {
|
|||
lightNode := false
|
||||
addr := RandomAddr()
|
||||
s := newBzzHandshakeTester(t, 1, addr, lightNode)
|
||||
id := s.IDs[0]
|
||||
node := s.Nodes[0]
|
||||
|
||||
err := s.testHandshake(
|
||||
correctBzzHandshake(addr, lightNode),
|
||||
&HandshakeMsg{Version: TestProtocolVersion, NetworkID: 321, Addr: NewAddrFromNodeID(id)},
|
||||
&p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("Handshake error: Message handler error: (msg code 0): network id mismatch 321 (!= 3)")},
|
||||
&HandshakeMsg{Version: TestProtocolVersion, NetworkID: 321, Addr: NewAddr(node)},
|
||||
&p2ptest.Disconnect{Peer: node.ID(), Error: fmt.Errorf("Handshake error: Message handler error: (msg code 0): network id mismatch 321 (!= 3)")},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
@ -226,12 +222,12 @@ func TestBzzHandshakeVersionMismatch(t *testing.T) {
|
|||
lightNode := false
|
||||
addr := RandomAddr()
|
||||
s := newBzzHandshakeTester(t, 1, addr, lightNode)
|
||||
id := s.IDs[0]
|
||||
node := s.Nodes[0]
|
||||
|
||||
err := s.testHandshake(
|
||||
correctBzzHandshake(addr, lightNode),
|
||||
&HandshakeMsg{Version: 0, NetworkID: TestProtocolNetworkID, Addr: NewAddrFromNodeID(id)},
|
||||
&p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("Handshake error: Message handler error: (msg code 0): version mismatch 0 (!= %d)", TestProtocolVersion)},
|
||||
&HandshakeMsg{Version: 0, NetworkID: TestProtocolNetworkID, Addr: NewAddr(node)},
|
||||
&p2ptest.Disconnect{Peer: node.ID(), Error: fmt.Errorf("Handshake error: Message handler error: (msg code 0): version mismatch 0 (!= %d)", TestProtocolVersion)},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
@ -243,11 +239,11 @@ func TestBzzHandshakeSuccess(t *testing.T) {
|
|||
lightNode := false
|
||||
addr := RandomAddr()
|
||||
s := newBzzHandshakeTester(t, 1, addr, lightNode)
|
||||
id := s.IDs[0]
|
||||
node := s.Nodes[0]
|
||||
|
||||
err := s.testHandshake(
|
||||
correctBzzHandshake(addr, lightNode),
|
||||
&HandshakeMsg{Version: TestProtocolVersion, NetworkID: TestProtocolNetworkID, Addr: NewAddrFromNodeID(id)},
|
||||
&HandshakeMsg{Version: TestProtocolVersion, NetworkID: TestProtocolNetworkID, Addr: NewAddr(node)},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
@ -268,8 +264,8 @@ func TestBzzHandshakeLightNode(t *testing.T) {
|
|||
t.Run(test.name, func(t *testing.T) {
|
||||
randomAddr := RandomAddr()
|
||||
pt := newBzzHandshakeTester(t, 1, randomAddr, false)
|
||||
id := pt.IDs[0]
|
||||
addr := NewAddrFromNodeID(id)
|
||||
node := pt.Nodes[0]
|
||||
addr := NewAddr(node)
|
||||
|
||||
err := pt.testHandshake(
|
||||
correctBzzHandshake(randomAddr, false),
|
||||
|
@ -280,8 +276,8 @@ func TestBzzHandshakeLightNode(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if pt.bzz.handshakes[id].LightNode != test.lightNode {
|
||||
t.Fatalf("peer LightNode flag is %v, should be %v", pt.bzz.handshakes[id].LightNode, test.lightNode)
|
||||
if pt.bzz.handshakes[node.ID()].LightNode != test.lightNode {
|
||||
t.Fatalf("peer LightNode flag is %v, should be %v", pt.bzz.handshakes[node.ID()].LightNode, test.lightNode)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -16,15 +16,13 @@
|
|||
|
||||
package simulation
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
)
|
||||
import "github.com/ethereum/go-ethereum/p2p/enode"
|
||||
|
||||
// BucketKey is the type that should be used for keys in simulation buckets.
|
||||
type BucketKey string
|
||||
|
||||
// NodeItem returns an item set in ServiceFunc function for a particualar node.
|
||||
func (s *Simulation) NodeItem(id discover.NodeID, key interface{}) (value interface{}, ok bool) {
|
||||
func (s *Simulation) NodeItem(id enode.ID, key interface{}) (value interface{}, ok bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
|
@ -36,7 +34,7 @@ func (s *Simulation) NodeItem(id discover.NodeID, key interface{}) (value interf
|
|||
|
||||
// SetNodeItem sets a new item associated with the node with provided NodeID.
|
||||
// Buckets should be used to avoid managing separate simulation global state.
|
||||
func (s *Simulation) SetNodeItem(id discover.NodeID, key interface{}, value interface{}) {
|
||||
func (s *Simulation) SetNodeItem(id enode.ID, key interface{}, value interface{}) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
|
@ -45,12 +43,12 @@ func (s *Simulation) SetNodeItem(id discover.NodeID, key interface{}, value inte
|
|||
|
||||
// NodesItems returns a map of items from all nodes that are all set under the
|
||||
// same BucketKey.
|
||||
func (s *Simulation) NodesItems(key interface{}) (values map[discover.NodeID]interface{}) {
|
||||
func (s *Simulation) NodesItems(key interface{}) (values map[enode.ID]interface{}) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
ids := s.NodeIDs()
|
||||
values = make(map[discover.NodeID]interface{}, len(ids))
|
||||
values = make(map[enode.ID]interface{}, len(ids))
|
||||
for _, id := range ids {
|
||||
if _, ok := s.buckets[id]; !ok {
|
||||
continue
|
||||
|
@ -63,12 +61,12 @@ func (s *Simulation) NodesItems(key interface{}) (values map[discover.NodeID]int
|
|||
}
|
||||
|
||||
// UpNodesItems returns a map of items with the same BucketKey from all nodes that are up.
|
||||
func (s *Simulation) UpNodesItems(key interface{}) (values map[discover.NodeID]interface{}) {
|
||||
func (s *Simulation) UpNodesItems(key interface{}) (values map[enode.ID]interface{}) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
ids := s.UpNodeIDs()
|
||||
values = make(map[discover.NodeID]interface{})
|
||||
values = make(map[enode.ID]interface{})
|
||||
for _, id := range ids {
|
||||
if _, ok := s.buckets[id]; !ok {
|
||||
continue
|
||||
|
|
|
@ -19,14 +19,14 @@ package simulation
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
)
|
||||
|
||||
// ConnectToPivotNode connects the node with provided NodeID
|
||||
// to the pivot node, already set by Simulation.SetPivotNode method.
|
||||
// It is useful when constructing a star network topology
|
||||
// when simulation adds and removes nodes dynamically.
|
||||
func (s *Simulation) ConnectToPivotNode(id discover.NodeID) (err error) {
|
||||
func (s *Simulation) ConnectToPivotNode(id enode.ID) (err error) {
|
||||
pid := s.PivotNodeID()
|
||||
if pid == nil {
|
||||
return ErrNoPivotNode
|
||||
|
@ -38,7 +38,7 @@ func (s *Simulation) ConnectToPivotNode(id discover.NodeID) (err error) {
|
|||
// to the last node that is up, and avoiding connection to self.
|
||||
// It is useful when constructing a chain network topology
|
||||
// when simulation adds and removes nodes dynamically.
|
||||
func (s *Simulation) ConnectToLastNode(id discover.NodeID) (err error) {
|
||||
func (s *Simulation) ConnectToLastNode(id enode.ID) (err error) {
|
||||
ids := s.UpNodeIDs()
|
||||
l := len(ids)
|
||||
if l < 2 {
|
||||
|
@ -53,7 +53,7 @@ func (s *Simulation) ConnectToLastNode(id discover.NodeID) (err error) {
|
|||
|
||||
// ConnectToRandomNode connects the node with provieded NodeID
|
||||
// to a random node that is up.
|
||||
func (s *Simulation) ConnectToRandomNode(id discover.NodeID) (err error) {
|
||||
func (s *Simulation) ConnectToRandomNode(id enode.ID) (err error) {
|
||||
n := s.RandomUpNode(id)
|
||||
if n == nil {
|
||||
return ErrNodeNotFound
|
||||
|
@ -64,7 +64,7 @@ func (s *Simulation) ConnectToRandomNode(id discover.NodeID) (err error) {
|
|||
// ConnectNodesFull connects all nodes one to another.
|
||||
// It provides a complete connectivity in the network
|
||||
// which should be rarely needed.
|
||||
func (s *Simulation) ConnectNodesFull(ids []discover.NodeID) (err error) {
|
||||
func (s *Simulation) ConnectNodesFull(ids []enode.ID) (err error) {
|
||||
if ids == nil {
|
||||
ids = s.UpNodeIDs()
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ func (s *Simulation) ConnectNodesFull(ids []discover.NodeID) (err error) {
|
|||
|
||||
// ConnectNodesChain connects all nodes in a chain topology.
|
||||
// If ids argument is nil, all nodes that are up will be connected.
|
||||
func (s *Simulation) ConnectNodesChain(ids []discover.NodeID) (err error) {
|
||||
func (s *Simulation) ConnectNodesChain(ids []enode.ID) (err error) {
|
||||
if ids == nil {
|
||||
ids = s.UpNodeIDs()
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ func (s *Simulation) ConnectNodesChain(ids []discover.NodeID) (err error) {
|
|||
|
||||
// ConnectNodesRing connects all nodes in a ring topology.
|
||||
// If ids argument is nil, all nodes that are up will be connected.
|
||||
func (s *Simulation) ConnectNodesRing(ids []discover.NodeID) (err error) {
|
||||
func (s *Simulation) ConnectNodesRing(ids []enode.ID) (err error) {
|
||||
if ids == nil {
|
||||
ids = s.UpNodeIDs()
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ func (s *Simulation) ConnectNodesRing(ids []discover.NodeID) (err error) {
|
|||
// ConnectNodesStar connects all nodes in a star topology
|
||||
// with the center at provided NodeID.
|
||||
// If ids argument is nil, all nodes that are up will be connected.
|
||||
func (s *Simulation) ConnectNodesStar(id discover.NodeID, ids []discover.NodeID) (err error) {
|
||||
func (s *Simulation) ConnectNodesStar(id enode.ID, ids []enode.ID) (err error) {
|
||||
if ids == nil {
|
||||
ids = s.UpNodeIDs()
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ func (s *Simulation) ConnectNodesStar(id discover.NodeID, ids []discover.NodeID)
|
|||
// ConnectNodesStarPivot connects all nodes in a star topology
|
||||
// with the center at already set pivot node.
|
||||
// If ids argument is nil, all nodes that are up will be connected.
|
||||
func (s *Simulation) ConnectNodesStarPivot(ids []discover.NodeID) (err error) {
|
||||
func (s *Simulation) ConnectNodesStarPivot(ids []enode.ID) (err error) {
|
||||
id := s.PivotNodeID()
|
||||
if id == nil {
|
||||
return ErrNoPivotNode
|
||||
|
@ -147,7 +147,7 @@ func (s *Simulation) ConnectNodesStarPivot(ids []discover.NodeID) (err error) {
|
|||
}
|
||||
|
||||
// connect connects two nodes but ignores already connected error.
|
||||
func (s *Simulation) connect(oneID, otherID discover.NodeID) error {
|
||||
func (s *Simulation) connect(oneID, otherID enode.ID) error {
|
||||
return ignoreAlreadyConnectedErr(s.Net.Connect(oneID, otherID))
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ package simulation
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
)
|
||||
|
||||
func TestConnectToPivotNode(t *testing.T) {
|
||||
|
@ -143,7 +143,7 @@ func TestConnectNodesFull(t *testing.T) {
|
|||
testFull(t, sim, ids)
|
||||
}
|
||||
|
||||
func testFull(t *testing.T, sim *Simulation, ids []discover.NodeID) {
|
||||
func testFull(t *testing.T, sim *Simulation, ids []enode.ID) {
|
||||
n := len(ids)
|
||||
var cc int
|
||||
for i := 0; i < n; i++ {
|
||||
|
@ -182,7 +182,7 @@ func TestConnectNodesChain(t *testing.T) {
|
|||
testChain(t, sim, ids)
|
||||
}
|
||||
|
||||
func testChain(t *testing.T, sim *Simulation, ids []discover.NodeID) {
|
||||
func testChain(t *testing.T, sim *Simulation, ids []enode.ID) {
|
||||
n := len(ids)
|
||||
for i := 0; i < n; i++ {
|
||||
for j := i + 1; j < n; j++ {
|
||||
|
@ -221,7 +221,7 @@ func TestConnectNodesRing(t *testing.T) {
|
|||
testRing(t, sim, ids)
|
||||
}
|
||||
|
||||
func testRing(t *testing.T, sim *Simulation, ids []discover.NodeID) {
|
||||
func testRing(t *testing.T, sim *Simulation, ids []enode.ID) {
|
||||
n := len(ids)
|
||||
for i := 0; i < n; i++ {
|
||||
for j := i + 1; j < n; j++ {
|
||||
|
@ -262,7 +262,7 @@ func TestConnectToNodesStar(t *testing.T) {
|
|||
testStar(t, sim, ids, centerIndex)
|
||||
}
|
||||
|
||||
func testStar(t *testing.T, sim *Simulation, ids []discover.NodeID, centerIndex int) {
|
||||
func testStar(t *testing.T, sim *Simulation, ids []enode.ID, centerIndex int) {
|
||||
n := len(ids)
|
||||
for i := 0; i < n; i++ {
|
||||
for j := i + 1; j < n; j++ {
|
||||
|
|
|
@ -20,15 +20,14 @@ import (
|
|||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
)
|
||||
|
||||
// PeerEvent is the type of the channel returned by Simulation.PeerEvents.
|
||||
type PeerEvent struct {
|
||||
// NodeID is the ID of node that the event is caught on.
|
||||
NodeID discover.NodeID
|
||||
NodeID enode.ID
|
||||
// Event is the event that is caught.
|
||||
Event *p2p.PeerEvent
|
||||
// Error is the error that may have happened during event watching.
|
||||
|
@ -69,7 +68,7 @@ func (f *PeerEventsFilter) MsgCode(c uint64) *PeerEventsFilter {
|
|||
// PeerEvents returns a channel of events that are captured by admin peerEvents
|
||||
// subscription nodes with provided NodeIDs. Additional filters can be set to ignore
|
||||
// events that are not relevant.
|
||||
func (s *Simulation) PeerEvents(ctx context.Context, ids []discover.NodeID, filters ...*PeerEventsFilter) <-chan PeerEvent {
|
||||
func (s *Simulation) PeerEvents(ctx context.Context, ids []enode.ID, filters ...*PeerEventsFilter) <-chan PeerEvent {
|
||||
eventC := make(chan PeerEvent)
|
||||
|
||||
// wait group to make sure all subscriptions to admin peerEvents are established
|
||||
|
@ -78,7 +77,7 @@ func (s *Simulation) PeerEvents(ctx context.Context, ids []discover.NodeID, filt
|
|||
for _, id := range ids {
|
||||
s.shutdownWG.Add(1)
|
||||
subsWG.Add(1)
|
||||
go func(id discover.NodeID) {
|
||||
go func(id enode.ID) {
|
||||
defer s.shutdownWG.Done()
|
||||
|
||||
client, err := s.Net.GetNode(id).Client()
|
||||
|
|
|
@ -36,7 +36,7 @@ import (
|
|||
func ExampleSimulation_WaitTillHealthy() {
|
||||
sim := simulation.New(map[string]simulation.ServiceFunc{
|
||||
"bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
|
||||
addr := network.NewAddrFromNodeID(ctx.Config.ID)
|
||||
addr := network.NewAddr(ctx.Config.Node())
|
||||
hp := network.NewHiveParams()
|
||||
hp.Discovery = false
|
||||
config := &network.BzzConfig{
|
||||
|
|
|
@ -21,10 +21,9 @@ import (
|
|||
"encoding/hex"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/swarm/network"
|
||||
)
|
||||
|
||||
|
@ -34,7 +33,7 @@ var BucketKeyKademlia BucketKey = "kademlia"
|
|||
|
||||
// WaitTillHealthy is blocking until the health of all kademlias is true.
|
||||
// If error is not nil, a map of kademlia that was found not healthy is returned.
|
||||
func (s *Simulation) WaitTillHealthy(ctx context.Context, kadMinProxSize int) (ill map[discover.NodeID]*network.Kademlia, err error) {
|
||||
func (s *Simulation) WaitTillHealthy(ctx context.Context, kadMinProxSize int) (ill map[enode.ID]*network.Kademlia, err error) {
|
||||
// Prepare PeerPot map for checking Kademlia health
|
||||
var ppmap map[string]*network.PeerPot
|
||||
kademlias := s.kademlias()
|
||||
|
@ -48,7 +47,7 @@ func (s *Simulation) WaitTillHealthy(ctx context.Context, kadMinProxSize int) (i
|
|||
ticker := time.NewTicker(200 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
|
||||
ill = make(map[discover.NodeID]*network.Kademlia)
|
||||
ill = make(map[enode.ID]*network.Kademlia)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
@ -82,9 +81,9 @@ func (s *Simulation) WaitTillHealthy(ctx context.Context, kadMinProxSize int) (i
|
|||
|
||||
// kademlias returns all Kademlia instances that are set
|
||||
// in simulation bucket.
|
||||
func (s *Simulation) kademlias() (ks map[discover.NodeID]*network.Kademlia) {
|
||||
func (s *Simulation) kademlias() (ks map[enode.ID]*network.Kademlia) {
|
||||
items := s.UpNodesItems(BucketKeyKademlia)
|
||||
ks = make(map[discover.NodeID]*network.Kademlia, len(items))
|
||||
ks = make(map[enode.ID]*network.Kademlia, len(items))
|
||||
for id, v := range items {
|
||||
k, ok := v.(*network.Kademlia)
|
||||
if !ok {
|
||||
|
|
|
@ -30,7 +30,7 @@ import (
|
|||
func TestWaitTillHealthy(t *testing.T) {
|
||||
sim := New(map[string]ServiceFunc{
|
||||
"bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
|
||||
addr := network.NewAddrFromNodeID(ctx.Config.ID)
|
||||
addr := network.NewAddr(ctx.Config.Node())
|
||||
hp := network.NewHiveParams()
|
||||
hp.Discovery = false
|
||||
config := &network.BzzConfig{
|
||||
|
|
|
@ -25,15 +25,15 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
)
|
||||
|
||||
// NodeIDs returns NodeIDs for all nodes in the network.
|
||||
func (s *Simulation) NodeIDs() (ids []discover.NodeID) {
|
||||
func (s *Simulation) NodeIDs() (ids []enode.ID) {
|
||||
nodes := s.Net.GetNodes()
|
||||
ids = make([]discover.NodeID, len(nodes))
|
||||
ids = make([]enode.ID, len(nodes))
|
||||
for i, node := range nodes {
|
||||
ids[i] = node.ID()
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ func (s *Simulation) NodeIDs() (ids []discover.NodeID) {
|
|||
}
|
||||
|
||||
// UpNodeIDs returns NodeIDs for nodes that are up in the network.
|
||||
func (s *Simulation) UpNodeIDs() (ids []discover.NodeID) {
|
||||
func (s *Simulation) UpNodeIDs() (ids []enode.ID) {
|
||||
nodes := s.Net.GetNodes()
|
||||
for _, node := range nodes {
|
||||
if node.Up {
|
||||
|
@ -52,7 +52,7 @@ func (s *Simulation) UpNodeIDs() (ids []discover.NodeID) {
|
|||
}
|
||||
|
||||
// DownNodeIDs returns NodeIDs for nodes that are stopped in the network.
|
||||
func (s *Simulation) DownNodeIDs() (ids []discover.NodeID) {
|
||||
func (s *Simulation) DownNodeIDs() (ids []enode.ID) {
|
||||
nodes := s.Net.GetNodes()
|
||||
for _, node := range nodes {
|
||||
if !node.Up {
|
||||
|
@ -88,7 +88,7 @@ func AddNodeWithService(serviceName string) AddNodeOption {
|
|||
// applies provided options to the config and adds the node to network.
|
||||
// By default all services will be started on a node. If one or more
|
||||
// AddNodeWithService option are provided, only specified services will be started.
|
||||
func (s *Simulation) AddNode(opts ...AddNodeOption) (id discover.NodeID, err error) {
|
||||
func (s *Simulation) AddNode(opts ...AddNodeOption) (id enode.ID, err error) {
|
||||
conf := adapters.RandomNodeConfig()
|
||||
for _, o := range opts {
|
||||
o(conf)
|
||||
|
@ -105,8 +105,8 @@ func (s *Simulation) AddNode(opts ...AddNodeOption) (id discover.NodeID, err err
|
|||
|
||||
// AddNodes creates new nodes with random configurations,
|
||||
// applies provided options to the config and adds nodes to network.
|
||||
func (s *Simulation) AddNodes(count int, opts ...AddNodeOption) (ids []discover.NodeID, err error) {
|
||||
ids = make([]discover.NodeID, 0, count)
|
||||
func (s *Simulation) AddNodes(count int, opts ...AddNodeOption) (ids []enode.ID, err error) {
|
||||
ids = make([]enode.ID, 0, count)
|
||||
for i := 0; i < count; i++ {
|
||||
id, err := s.AddNode(opts...)
|
||||
if err != nil {
|
||||
|
@ -119,7 +119,7 @@ func (s *Simulation) AddNodes(count int, opts ...AddNodeOption) (ids []discover.
|
|||
|
||||
// AddNodesAndConnectFull is a helpper method that combines
|
||||
// AddNodes and ConnectNodesFull. Only new nodes will be connected.
|
||||
func (s *Simulation) AddNodesAndConnectFull(count int, opts ...AddNodeOption) (ids []discover.NodeID, err error) {
|
||||
func (s *Simulation) AddNodesAndConnectFull(count int, opts ...AddNodeOption) (ids []enode.ID, err error) {
|
||||
if count < 2 {
|
||||
return nil, errors.New("count of nodes must be at least 2")
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ func (s *Simulation) AddNodesAndConnectFull(count int, opts ...AddNodeOption) (i
|
|||
// AddNodesAndConnectChain is a helpper method that combines
|
||||
// AddNodes and ConnectNodesChain. The chain will be continued from the last
|
||||
// added node, if there is one in simulation using ConnectToLastNode method.
|
||||
func (s *Simulation) AddNodesAndConnectChain(count int, opts ...AddNodeOption) (ids []discover.NodeID, err error) {
|
||||
func (s *Simulation) AddNodesAndConnectChain(count int, opts ...AddNodeOption) (ids []enode.ID, err error) {
|
||||
if count < 2 {
|
||||
return nil, errors.New("count of nodes must be at least 2")
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ func (s *Simulation) AddNodesAndConnectChain(count int, opts ...AddNodeOption) (
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ids = append([]discover.NodeID{id}, ids...)
|
||||
ids = append([]enode.ID{id}, ids...)
|
||||
err = s.ConnectNodesChain(ids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -163,7 +163,7 @@ func (s *Simulation) AddNodesAndConnectChain(count int, opts ...AddNodeOption) (
|
|||
|
||||
// AddNodesAndConnectRing is a helpper method that combines
|
||||
// AddNodes and ConnectNodesRing.
|
||||
func (s *Simulation) AddNodesAndConnectRing(count int, opts ...AddNodeOption) (ids []discover.NodeID, err error) {
|
||||
func (s *Simulation) AddNodesAndConnectRing(count int, opts ...AddNodeOption) (ids []enode.ID, err error) {
|
||||
if count < 2 {
|
||||
return nil, errors.New("count of nodes must be at least 2")
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ func (s *Simulation) AddNodesAndConnectRing(count int, opts ...AddNodeOption) (i
|
|||
|
||||
// AddNodesAndConnectStar is a helpper method that combines
|
||||
// AddNodes and ConnectNodesStar.
|
||||
func (s *Simulation) AddNodesAndConnectStar(count int, opts ...AddNodeOption) (ids []discover.NodeID, err error) {
|
||||
func (s *Simulation) AddNodesAndConnectStar(count int, opts ...AddNodeOption) (ids []enode.ID, err error) {
|
||||
if count < 2 {
|
||||
return nil, errors.New("count of nodes must be at least 2")
|
||||
}
|
||||
|
@ -246,7 +246,7 @@ func (s *Simulation) UploadSnapshot(snapshotFile string, opts ...AddNodeOption)
|
|||
// differently then other nodes in test. SetPivotNode and
|
||||
// PivotNodeID are just a convenient functions to set and
|
||||
// retrieve it.
|
||||
func (s *Simulation) SetPivotNode(id discover.NodeID) {
|
||||
func (s *Simulation) SetPivotNode(id enode.ID) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.pivotNodeID = &id
|
||||
|
@ -254,19 +254,19 @@ func (s *Simulation) SetPivotNode(id discover.NodeID) {
|
|||
|
||||
// PivotNodeID returns NodeID of the pivot node set by
|
||||
// Simulation.SetPivotNode method.
|
||||
func (s *Simulation) PivotNodeID() (id *discover.NodeID) {
|
||||
func (s *Simulation) PivotNodeID() (id *enode.ID) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.pivotNodeID
|
||||
}
|
||||
|
||||
// StartNode starts a node by NodeID.
|
||||
func (s *Simulation) StartNode(id discover.NodeID) (err error) {
|
||||
func (s *Simulation) StartNode(id enode.ID) (err error) {
|
||||
return s.Net.Start(id)
|
||||
}
|
||||
|
||||
// StartRandomNode starts a random node.
|
||||
func (s *Simulation) StartRandomNode() (id discover.NodeID, err error) {
|
||||
func (s *Simulation) StartRandomNode() (id enode.ID, err error) {
|
||||
n := s.randomDownNode()
|
||||
if n == nil {
|
||||
return id, ErrNodeNotFound
|
||||
|
@ -275,8 +275,8 @@ func (s *Simulation) StartRandomNode() (id discover.NodeID, err error) {
|
|||
}
|
||||
|
||||
// StartRandomNodes starts random nodes.
|
||||
func (s *Simulation) StartRandomNodes(count int) (ids []discover.NodeID, err error) {
|
||||
ids = make([]discover.NodeID, 0, count)
|
||||
func (s *Simulation) StartRandomNodes(count int) (ids []enode.ID, err error) {
|
||||
ids = make([]enode.ID, 0, count)
|
||||
downIDs := s.DownNodeIDs()
|
||||
for i := 0; i < count; i++ {
|
||||
n := s.randomNode(downIDs, ids...)
|
||||
|
@ -293,12 +293,12 @@ func (s *Simulation) StartRandomNodes(count int) (ids []discover.NodeID, err err
|
|||
}
|
||||
|
||||
// StopNode stops a node by NodeID.
|
||||
func (s *Simulation) StopNode(id discover.NodeID) (err error) {
|
||||
func (s *Simulation) StopNode(id enode.ID) (err error) {
|
||||
return s.Net.Stop(id)
|
||||
}
|
||||
|
||||
// StopRandomNode stops a random node.
|
||||
func (s *Simulation) StopRandomNode() (id discover.NodeID, err error) {
|
||||
func (s *Simulation) StopRandomNode() (id enode.ID, err error) {
|
||||
n := s.RandomUpNode()
|
||||
if n == nil {
|
||||
return id, ErrNodeNotFound
|
||||
|
@ -307,8 +307,8 @@ func (s *Simulation) StopRandomNode() (id discover.NodeID, err error) {
|
|||
}
|
||||
|
||||
// StopRandomNodes stops random nodes.
|
||||
func (s *Simulation) StopRandomNodes(count int) (ids []discover.NodeID, err error) {
|
||||
ids = make([]discover.NodeID, 0, count)
|
||||
func (s *Simulation) StopRandomNodes(count int) (ids []enode.ID, err error) {
|
||||
ids = make([]enode.ID, 0, count)
|
||||
upIDs := s.UpNodeIDs()
|
||||
for i := 0; i < count; i++ {
|
||||
n := s.randomNode(upIDs, ids...)
|
||||
|
@ -331,17 +331,17 @@ func init() {
|
|||
|
||||
// RandomUpNode returns a random SimNode that is up.
|
||||
// Arguments are NodeIDs for nodes that should not be returned.
|
||||
func (s *Simulation) RandomUpNode(exclude ...discover.NodeID) *adapters.SimNode {
|
||||
func (s *Simulation) RandomUpNode(exclude ...enode.ID) *adapters.SimNode {
|
||||
return s.randomNode(s.UpNodeIDs(), exclude...)
|
||||
}
|
||||
|
||||
// randomDownNode returns a random SimNode that is not up.
|
||||
func (s *Simulation) randomDownNode(exclude ...discover.NodeID) *adapters.SimNode {
|
||||
func (s *Simulation) randomDownNode(exclude ...enode.ID) *adapters.SimNode {
|
||||
return s.randomNode(s.DownNodeIDs(), exclude...)
|
||||
}
|
||||
|
||||
// randomNode returns a random SimNode from the slice of NodeIDs.
|
||||
func (s *Simulation) randomNode(ids []discover.NodeID, exclude ...discover.NodeID) *adapters.SimNode {
|
||||
func (s *Simulation) randomNode(ids []enode.ID, exclude ...enode.ID) *adapters.SimNode {
|
||||
for _, e := range exclude {
|
||||
var i int
|
||||
for _, id := range ids {
|
||||
|
|
|
@ -25,7 +25,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
"github.com/ethereum/go-ethereum/swarm/network"
|
||||
)
|
||||
|
@ -75,7 +75,7 @@ func TestUpDownNodeIDs(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func equalNodeIDs(one, other []discover.NodeID) bool {
|
||||
func equalNodeIDs(one, other []enode.ID) bool {
|
||||
if len(one) != len(other) {
|
||||
return false
|
||||
}
|
||||
|
@ -244,7 +244,7 @@ func TestUploadSnapshot(t *testing.T) {
|
|||
log.Debug("Creating simulation")
|
||||
s := New(map[string]ServiceFunc{
|
||||
"bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
|
||||
addr := network.NewAddrFromNodeID(ctx.Config.ID)
|
||||
addr := network.NewAddr(ctx.Config.Node())
|
||||
hp := network.NewHiveParams()
|
||||
hp.Discovery = false
|
||||
config := &network.BzzConfig{
|
||||
|
|
|
@ -18,13 +18,13 @@ package simulation
|
|||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
)
|
||||
|
||||
// Service returns a single Service by name on a particular node
|
||||
// with provided id.
|
||||
func (s *Simulation) Service(name string, id discover.NodeID) node.Service {
|
||||
func (s *Simulation) Service(name string, id enode.ID) node.Service {
|
||||
simNode, ok := s.Net.GetNode(id).Node.(*adapters.SimNode)
|
||||
if !ok {
|
||||
return nil
|
||||
|
@ -48,9 +48,9 @@ func (s *Simulation) RandomService(name string) node.Service {
|
|||
|
||||
// Services returns all services with a provided name
|
||||
// from nodes that are up.
|
||||
func (s *Simulation) Services(name string) (services map[discover.NodeID]node.Service) {
|
||||
func (s *Simulation) Services(name string) (services map[enode.ID]node.Service) {
|
||||
nodes := s.Net.GetNodes()
|
||||
services = make(map[discover.NodeID]node.Service)
|
||||
services = make(map[enode.ID]node.Service)
|
||||
for _, node := range nodes {
|
||||
if !node.Up {
|
||||
continue
|
||||
|
|
|
@ -25,7 +25,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
)
|
||||
|
@ -45,8 +45,8 @@ type Simulation struct {
|
|||
|
||||
serviceNames []string
|
||||
cleanupFuncs []func()
|
||||
buckets map[discover.NodeID]*sync.Map
|
||||
pivotNodeID *discover.NodeID
|
||||
buckets map[enode.ID]*sync.Map
|
||||
pivotNodeID *enode.ID
|
||||
shutdownWG sync.WaitGroup
|
||||
done chan struct{}
|
||||
mu sync.RWMutex
|
||||
|
@ -70,7 +70,7 @@ type ServiceFunc func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Se
|
|||
// simulations.Network initialized with provided services.
|
||||
func New(services map[string]ServiceFunc) (s *Simulation) {
|
||||
s = &Simulation{
|
||||
buckets: make(map[discover.NodeID]*sync.Map),
|
||||
buckets: make(map[enode.ID]*sync.Map),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
|
|
|
@ -31,11 +31,10 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
"github.com/ethereum/go-ethereum/swarm/network"
|
||||
|
@ -237,8 +236,8 @@ func discoverySimulation(nodes, conns int, adapter adapters.NodeAdapter) (*simul
|
|||
DefaultService: serviceName,
|
||||
})
|
||||
defer net.Shutdown()
|
||||
trigger := make(chan discover.NodeID)
|
||||
ids := make([]discover.NodeID, nodes)
|
||||
trigger := make(chan enode.ID)
|
||||
ids := make([]enode.ID, nodes)
|
||||
for i := 0; i < nodes; i++ {
|
||||
conf := adapters.RandomNodeConfig()
|
||||
node, err := net.NewNodeWithConfig(conf)
|
||||
|
@ -263,7 +262,7 @@ func discoverySimulation(nodes, conns int, adapter adapters.NodeAdapter) (*simul
|
|||
wg := sync.WaitGroup{}
|
||||
for i := range ids {
|
||||
// collect the overlay addresses, to
|
||||
addrs = append(addrs, network.ToOverlayAddr(ids[i].Bytes()))
|
||||
addrs = append(addrs, ids[i].Bytes())
|
||||
for j := 0; j < conns; j++ {
|
||||
var k int
|
||||
if j == 0 {
|
||||
|
@ -282,7 +281,7 @@ func discoverySimulation(nodes, conns int, adapter adapters.NodeAdapter) (*simul
|
|||
log.Debug(fmt.Sprintf("nodes: %v", len(addrs)))
|
||||
// construct the peer pot, so that kademlia health can be checked
|
||||
ppmap := network.NewPeerPotMap(testMinProxBinSize, addrs)
|
||||
check := func(ctx context.Context, id discover.NodeID) (bool, error) {
|
||||
check := func(ctx context.Context, id enode.ID) (bool, error) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return false, ctx.Err()
|
||||
|
@ -298,8 +297,7 @@ func discoverySimulation(nodes, conns int, adapter adapters.NodeAdapter) (*simul
|
|||
return false, fmt.Errorf("error getting node client: %s", err)
|
||||
}
|
||||
healthy := &network.Health{}
|
||||
addr := common.Bytes2Hex(network.ToOverlayAddr(id.Bytes()))
|
||||
if err := client.Call(&healthy, "hive_healthy", ppmap[addr]); err != nil {
|
||||
if err := client.Call(&healthy, "hive_healthy", ppmap[id.String()]); err != nil {
|
||||
return false, fmt.Errorf("error getting node health: %s", err)
|
||||
}
|
||||
log.Debug(fmt.Sprintf("node %4s healthy: got nearest neighbours: %v, know nearest neighbours: %v, saturated: %v\n%v", id, healthy.GotNN, healthy.KnowNN, healthy.Full, healthy.Hive))
|
||||
|
@ -351,8 +349,8 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
|
|||
DefaultService: serviceName,
|
||||
})
|
||||
defer net.Shutdown()
|
||||
trigger := make(chan discover.NodeID)
|
||||
ids := make([]discover.NodeID, nodes)
|
||||
trigger := make(chan enode.ID)
|
||||
ids := make([]enode.ID, nodes)
|
||||
var addrs [][]byte
|
||||
|
||||
for i := 0; i < nodes; i++ {
|
||||
|
@ -371,7 +369,7 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
|
|||
return nil, fmt.Errorf("error triggering checks for node %s: %s", node.ID().TerminalString(), err)
|
||||
}
|
||||
ids[i] = node.ID()
|
||||
a := network.ToOverlayAddr(ids[i].Bytes())
|
||||
a := ids[i].Bytes()
|
||||
|
||||
addrs = append(addrs, a)
|
||||
}
|
||||
|
@ -398,12 +396,12 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
|
|||
return fmt.Errorf("error getting node client: %s", err)
|
||||
}
|
||||
healthy := &network.Health{}
|
||||
addr := common.Bytes2Hex(network.ToOverlayAddr(id.Bytes()))
|
||||
addr := id.String()
|
||||
if err := client.Call(&healthy, "hive_healthy", ppmap[addr]); err != nil {
|
||||
return fmt.Errorf("error getting node health: %s", err)
|
||||
}
|
||||
|
||||
log.Info(fmt.Sprintf("NODE: %s, IS HEALTHY: %t", id.String(), healthy.GotNN && healthy.KnowNN && healthy.Full))
|
||||
log.Info(fmt.Sprintf("NODE: %s, IS HEALTHY: %t", addr, healthy.GotNN && healthy.KnowNN && healthy.Full))
|
||||
if !healthy.GotNN || !healthy.Full {
|
||||
isHealthy = false
|
||||
break
|
||||
|
@ -462,7 +460,7 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
|
|||
wg.Wait()
|
||||
log.Debug(fmt.Sprintf("nodes: %v", len(addrs)))
|
||||
// construct the peer pot, so that kademlia health can be checked
|
||||
check := func(ctx context.Context, id discover.NodeID) (bool, error) {
|
||||
check := func(ctx context.Context, id enode.ID) (bool, error) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return false, ctx.Err()
|
||||
|
@ -478,8 +476,7 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
|
|||
return false, fmt.Errorf("error getting node client: %s", err)
|
||||
}
|
||||
healthy := &network.Health{}
|
||||
addr := common.Bytes2Hex(network.ToOverlayAddr(id.Bytes()))
|
||||
if err := client.Call(&healthy, "hive_healthy", ppmap[addr]); err != nil {
|
||||
if err := client.Call(&healthy, "hive_healthy", ppmap[id.String()]); err != nil {
|
||||
return false, fmt.Errorf("error getting node health: %s", err)
|
||||
}
|
||||
log.Info(fmt.Sprintf("node %4s healthy: got nearest neighbours: %v, know nearest neighbours: %v, saturated: %v", id, healthy.GotNN, healthy.KnowNN, healthy.Full))
|
||||
|
@ -510,7 +507,7 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
|
|||
// triggerChecks triggers a simulation step check whenever a peer is added or
|
||||
// removed from the given node, and also every second to avoid a race between
|
||||
// peer events and kademlia becoming healthy
|
||||
func triggerChecks(trigger chan discover.NodeID, net *simulations.Network, id discover.NodeID) error {
|
||||
func triggerChecks(trigger chan enode.ID, net *simulations.Network, id enode.ID) error {
|
||||
node := net.GetNode(id)
|
||||
if node == nil {
|
||||
return fmt.Errorf("unknown node: %s", id)
|
||||
|
@ -548,9 +545,8 @@ func triggerChecks(trigger chan discover.NodeID, net *simulations.Network, id di
|
|||
}
|
||||
|
||||
func newService(ctx *adapters.ServiceContext) (node.Service, error) {
|
||||
host := adapters.ExternalIP()
|
||||
|
||||
addr := network.NewAddrFromNodeIDAndPort(ctx.Config.ID, host, ctx.Config.Port)
|
||||
node := enode.NewV4(&ctx.Config.PrivateKey.PublicKey, adapters.ExternalIP(), int(ctx.Config.Port), int(ctx.Config.Port))
|
||||
addr := network.NewAddr(node)
|
||||
|
||||
kp := network.NewKadParams()
|
||||
kp.MinProxBinSize = testMinProxBinSize
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
{"nodes":[{"node":{"config":null,"up":false}},{"node":{"config":null,"up":false}},{"node":{"config":null,"up":false}},{"node":{"config":null,"up":false}},{"node":{"config":null,"up":false}},{"node":{"config":null,"up":false}},{"node":{"config":null,"up":false}},{"node":{"config":null,"up":false}},{"node":{"config":null,"up":false}},{"node":{"config":null,"up":false}}],"conns":[{"one":"c04a0c47cb0c522ecf28d8841e93721e73f58790b30e92382816a4b453be2988","other":"d9283e5247a18d6564b3581217e9f4d9c93a4359944894c00bb2b22c690faadc","up":true},{"one":"dd99c11abe2abae112d64d902b96fe0c75243ea67eca759a2769058a30cc0e77","other":"c04a0c47cb0c522ecf28d8841e93721e73f58790b30e92382816a4b453be2988","up":true},{"one":"4f5dad2aa4f26ac5a23d4fbcc807296b474eab77761db6594debd60ef4287aed","other":"dd99c11abe2abae112d64d902b96fe0c75243ea67eca759a2769058a30cc0e77","up":true},{"one":"4f47f4e176d1c9f78d9a7e19723689ffe2a0603004a3d4506a2349e55a56fc17","other":"4f5dad2aa4f26ac5a23d4fbcc807296b474eab77761db6594debd60ef4287aed","up":true},{"one":"20b6a1be2cb8f966151682350e029d4f8da8ee92de10a2a1cb1727d110acebfa","other":"4f47f4e176d1c9f78d9a7e19723689ffe2a0603004a3d4506a2349e55a56fc17","up":true},{"one":"50cb92e77710582fa9cbee7a54cf25c95fd27d8d54b13ba5520a50139c309a22","other":"20b6a1be2cb8f966151682350e029d4f8da8ee92de10a2a1cb1727d110acebfa","up":true},{"one":"319dc901f99940f1339c540bc36fbabb10a96d326b13b9d7f53e7496980e2996","other":"50cb92e77710582fa9cbee7a54cf25c95fd27d8d54b13ba5520a50139c309a22","up":true},{"one":"dc285b6436a8bfd4d2e586d478b18d3fe7b705ce0b4fb27a651adcf6d27984f1","other":"319dc901f99940f1339c540bc36fbabb10a96d326b13b9d7f53e7496980e2996","up":true},{"one":"974dbe511377280f945a53a194b4bb397875b10b1ecb119a92425bbb16db68f1","other":"dc285b6436a8bfd4d2e586d478b18d3fe7b705ce0b4fb27a651adcf6d27984f1","up":true},{"one":"d9283e5247a18d6564b3581217e9f4d9c93a4359944894c00bb2b22c690faadc","other":"974dbe511377280f945a53a194b4bb397875b10b1ecb119a92425bbb16db68f1","up":true}]}
|
|
@ -29,7 +29,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
"github.com/ethereum/go-ethereum/swarm/network"
|
||||
|
@ -64,26 +64,26 @@ func init() {
|
|||
|
||||
type Simulation struct {
|
||||
mtx sync.Mutex
|
||||
stores map[discover.NodeID]*state.InmemoryStore
|
||||
stores map[enode.ID]*state.InmemoryStore
|
||||
}
|
||||
|
||||
func NewSimulation() *Simulation {
|
||||
return &Simulation{
|
||||
stores: make(map[discover.NodeID]*state.InmemoryStore),
|
||||
stores: make(map[enode.ID]*state.InmemoryStore),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Simulation) NewService(ctx *adapters.ServiceContext) (node.Service, error) {
|
||||
id := ctx.Config.ID
|
||||
node := ctx.Config.Node()
|
||||
s.mtx.Lock()
|
||||
store, ok := s.stores[id]
|
||||
store, ok := s.stores[node.ID()]
|
||||
if !ok {
|
||||
store = state.NewInmemoryStore()
|
||||
s.stores[id] = store
|
||||
s.stores[node.ID()] = store
|
||||
}
|
||||
s.mtx.Unlock()
|
||||
|
||||
addr := network.NewAddrFromNodeID(id)
|
||||
addr := network.NewAddr(node)
|
||||
|
||||
kp := network.NewKadParams()
|
||||
kp.MinProxBinSize = 2
|
||||
|
|
|
@ -26,7 +26,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations"
|
||||
"github.com/ethereum/go-ethereum/swarm/log"
|
||||
)
|
||||
|
@ -86,7 +86,7 @@ func TestOverlaySim(t *testing.T) {
|
|||
|
||||
//variables needed to wait for nodes being up
|
||||
var upCount int
|
||||
trigger := make(chan discover.NodeID)
|
||||
trigger := make(chan enode.ID)
|
||||
|
||||
//wait for all nodes to be up
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
|
@ -169,7 +169,7 @@ LOOP:
|
|||
}
|
||||
|
||||
//watch for events so we know when all nodes are up
|
||||
func watchSimEvents(net *simulations.Network, ctx context.Context, trigger chan discover.NodeID) {
|
||||
func watchSimEvents(net *simulations.Network, ctx context.Context, trigger chan enode.ID) {
|
||||
events := make(chan *simulations.Event)
|
||||
sub := net.Events().Subscribe(events)
|
||||
defer sub.Unsubscribe()
|
||||
|
|
|
@ -32,7 +32,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
|
||||
"github.com/ethereum/go-ethereum/swarm/network"
|
||||
"github.com/ethereum/go-ethereum/swarm/network/simulation"
|
||||
|
@ -114,12 +114,12 @@ func newStreamerTester(t *testing.T) (*p2ptest.ProtocolTester, *Registry, *stora
|
|||
|
||||
delivery := NewDelivery(to, netStore)
|
||||
netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
|
||||
streamer := NewRegistry(addr, delivery, netStore, state.NewInmemoryStore(), nil)
|
||||
streamer := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), nil)
|
||||
teardown := func() {
|
||||
streamer.Close()
|
||||
removeDataDir()
|
||||
}
|
||||
protocolTester := p2ptest.NewProtocolTester(t, network.NewNodeIDFromAddr(addr), 1, streamer.runProtocol)
|
||||
protocolTester := p2ptest.NewProtocolTester(t, addr.ID(), 1, streamer.runProtocol)
|
||||
|
||||
err = waitForPeers(streamer, 1*time.Second, 1)
|
||||
if err != nil {
|
||||
|
@ -240,7 +240,7 @@ func generateRandomFile() (string, error) {
|
|||
}
|
||||
|
||||
//create a local store for the given node
|
||||
func createTestLocalStorageForID(id discover.NodeID, addr *network.BzzAddr) (storage.ChunkStore, string, error) {
|
||||
func createTestLocalStorageForID(id enode.ID, addr *network.BzzAddr) (storage.ChunkStore, string, error) {
|
||||
var datadir string
|
||||
var err error
|
||||
datadir, err = ioutil.TempDir("", fmt.Sprintf("syncer-test-%s", id.TerminalString()))
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/swarm/log"
|
||||
"github.com/ethereum/go-ethereum/swarm/network"
|
||||
"github.com/ethereum/go-ethereum/swarm/spancontext"
|
||||
|
@ -47,7 +47,7 @@ var (
|
|||
type Delivery struct {
|
||||
chunkStore storage.SyncChunkStore
|
||||
kad *network.Kademlia
|
||||
getPeer func(discover.NodeID) *Peer
|
||||
getPeer func(enode.ID) *Peer
|
||||
}
|
||||
|
||||
func NewDelivery(kad *network.Kademlia, chunkStore storage.SyncChunkStore) *Delivery {
|
||||
|
@ -213,7 +213,7 @@ func (d *Delivery) handleChunkDeliveryMsg(ctx context.Context, sp *Peer, req *Ch
|
|||
}
|
||||
|
||||
// RequestFromPeers sends a chunk retrieve request to
|
||||
func (d *Delivery) RequestFromPeers(ctx context.Context, req *network.Request) (*discover.NodeID, chan struct{}, error) {
|
||||
func (d *Delivery) RequestFromPeers(ctx context.Context, req *network.Request) (*enode.ID, chan struct{}, error) {
|
||||
requestFromPeersCount.Inc(1)
|
||||
var sp *Peer
|
||||
spID := req.Source
|
||||
|
|
|
@ -45,7 +45,7 @@ func TestStreamerRetrieveRequest(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
peerID := tester.IDs[0]
|
||||
node := tester.Nodes[0]
|
||||
|
||||
ctx := context.Background()
|
||||
req := network.NewRequest(
|
||||
|
@ -64,7 +64,7 @@ func TestStreamerRetrieveRequest(t *testing.T) {
|
|||
Addr: hash0[:],
|
||||
SkipCheck: true,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -81,11 +81,11 @@ func TestStreamerUpstreamRetrieveRequestMsgExchangeWithoutStore(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
peerID := tester.IDs[0]
|
||||
node := tester.Nodes[0]
|
||||
|
||||
chunk := storage.NewChunk(storage.Address(hash0[:]), nil)
|
||||
|
||||
peer := streamer.getPeer(peerID)
|
||||
peer := streamer.getPeer(node.ID())
|
||||
|
||||
peer.handleSubscribeMsg(context.TODO(), &SubscribeMsg{
|
||||
Stream: NewStream(swarmChunkServerStreamName, "", false),
|
||||
|
@ -101,7 +101,7 @@ func TestStreamerUpstreamRetrieveRequestMsgExchangeWithoutStore(t *testing.T) {
|
|||
Msg: &RetrieveRequestMsg{
|
||||
Addr: chunk.Address()[:],
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
Expects: []p2ptest.Expect{
|
||||
|
@ -113,7 +113,7 @@ func TestStreamerUpstreamRetrieveRequestMsgExchangeWithoutStore(t *testing.T) {
|
|||
From: 0,
|
||||
To: 0,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -133,8 +133,8 @@ func TestStreamerUpstreamRetrieveRequestMsgExchange(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
peerID := tester.IDs[0]
|
||||
peer := streamer.getPeer(peerID)
|
||||
node := tester.Nodes[0]
|
||||
peer := streamer.getPeer(node.ID())
|
||||
|
||||
stream := NewStream(swarmChunkServerStreamName, "", false)
|
||||
|
||||
|
@ -159,7 +159,7 @@ func TestStreamerUpstreamRetrieveRequestMsgExchange(t *testing.T) {
|
|||
Msg: &RetrieveRequestMsg{
|
||||
Addr: hash,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
Expects: []p2ptest.Expect{
|
||||
|
@ -175,7 +175,7 @@ func TestStreamerUpstreamRetrieveRequestMsgExchange(t *testing.T) {
|
|||
To: 32,
|
||||
Stream: stream,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -200,7 +200,7 @@ func TestStreamerUpstreamRetrieveRequestMsgExchange(t *testing.T) {
|
|||
Addr: hash,
|
||||
SkipCheck: true,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
Expects: []p2ptest.Expect{
|
||||
|
@ -210,7 +210,7 @@ func TestStreamerUpstreamRetrieveRequestMsgExchange(t *testing.T) {
|
|||
Addr: hash,
|
||||
SData: hash,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -233,10 +233,10 @@ func TestStreamerDownstreamChunkDeliveryMsgExchange(t *testing.T) {
|
|||
}, nil
|
||||
})
|
||||
|
||||
peerID := tester.IDs[0]
|
||||
node := tester.Nodes[0]
|
||||
|
||||
stream := NewStream("foo", "", true)
|
||||
err = streamer.Subscribe(peerID, stream, NewRange(5, 8), Top)
|
||||
err = streamer.Subscribe(node.ID(), stream, NewRange(5, 8), Top)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
@ -254,7 +254,7 @@ func TestStreamerDownstreamChunkDeliveryMsgExchange(t *testing.T) {
|
|||
History: NewRange(5, 8),
|
||||
Priority: Top,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -267,7 +267,7 @@ func TestStreamerDownstreamChunkDeliveryMsgExchange(t *testing.T) {
|
|||
Addr: chunkKey,
|
||||
SData: chunkData,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -314,10 +314,9 @@ func TestDeliveryFromNodes(t *testing.T) {
|
|||
func testDeliveryFromNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck bool) {
|
||||
sim := simulation.New(map[string]simulation.ServiceFunc{
|
||||
"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
|
||||
|
||||
id := ctx.Config.ID
|
||||
addr := network.NewAddrFromNodeID(id)
|
||||
store, datadir, err := createTestLocalStorageForID(id, addr)
|
||||
node := ctx.Config.Node()
|
||||
addr := network.NewAddr(node)
|
||||
store, datadir, err := createTestLocalStorageForID(node.ID(), addr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -336,7 +335,7 @@ func testDeliveryFromNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck
|
|||
delivery := NewDelivery(kad, netStore)
|
||||
netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
|
||||
|
||||
r := NewRegistry(addr, delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
|
||||
r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
|
||||
SkipCheck: skipCheck,
|
||||
})
|
||||
bucket.Store(bucketKeyRegistry, r)
|
||||
|
@ -502,9 +501,9 @@ func BenchmarkDeliveryFromNodesWithCheck(b *testing.B) {
|
|||
func benchmarkDeliveryFromNodes(b *testing.B, nodes, conns, chunkCount int, skipCheck bool) {
|
||||
sim := simulation.New(map[string]simulation.ServiceFunc{
|
||||
"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
|
||||
id := ctx.Config.ID
|
||||
addr := network.NewAddrFromNodeID(id)
|
||||
store, datadir, err := createTestLocalStorageForID(id, addr)
|
||||
node := ctx.Config.Node()
|
||||
addr := network.NewAddr(node)
|
||||
store, datadir, err := createTestLocalStorageForID(node.ID(), addr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -522,7 +521,7 @@ func benchmarkDeliveryFromNodes(b *testing.B, nodes, conns, chunkCount int, skip
|
|||
delivery := NewDelivery(kad, netStore)
|
||||
netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
|
||||
|
||||
r := NewRegistry(addr, delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
|
||||
r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
|
||||
SkipCheck: skipCheck,
|
||||
DoSync: true,
|
||||
SyncUpdateDelay: 0,
|
||||
|
|
|
@ -30,7 +30,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
"github.com/ethereum/go-ethereum/swarm/network"
|
||||
"github.com/ethereum/go-ethereum/swarm/network/simulation"
|
||||
|
@ -62,10 +62,9 @@ func testIntervals(t *testing.T, live bool, history *Range, skipCheck bool) {
|
|||
|
||||
sim := simulation.New(map[string]simulation.ServiceFunc{
|
||||
"intervalsStreamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
|
||||
|
||||
id := ctx.Config.ID
|
||||
addr := network.NewAddrFromNodeID(id)
|
||||
store, datadir, err := createTestLocalStorageForID(id, addr)
|
||||
n := ctx.Config.Node()
|
||||
addr := network.NewAddr(n)
|
||||
store, datadir, err := createTestLocalStorageForID(n.ID(), addr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -83,7 +82,7 @@ func testIntervals(t *testing.T, live bool, history *Range, skipCheck bool) {
|
|||
delivery := NewDelivery(kad, netStore)
|
||||
netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
|
||||
|
||||
r := NewRegistry(addr, delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
|
||||
r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
|
||||
SkipCheck: skipCheck,
|
||||
})
|
||||
bucket.Store(bucketKeyRegistry, r)
|
||||
|
@ -281,7 +280,7 @@ func testIntervals(t *testing.T, live bool, history *Range, skipCheck bool) {
|
|||
}
|
||||
}
|
||||
|
||||
func getHashes(ctx context.Context, r *Registry, peerID discover.NodeID, s Stream) (chan []byte, error) {
|
||||
func getHashes(ctx context.Context, r *Registry, peerID enode.ID, s Stream) (chan []byte, error) {
|
||||
peer := r.getPeer(peerID)
|
||||
|
||||
client, err := peer.getClient(ctx, s)
|
||||
|
@ -294,7 +293,7 @@ func getHashes(ctx context.Context, r *Registry, peerID discover.NodeID, s Strea
|
|||
return c.hashes, nil
|
||||
}
|
||||
|
||||
func enableNotifications(r *Registry, peerID discover.NodeID, s Stream) error {
|
||||
func enableNotifications(r *Registry, peerID enode.ID, s Stream) error {
|
||||
peer := r.getPeer(peerID)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
|
|
|
@ -75,7 +75,7 @@ type RequestSubscriptionMsg struct {
|
|||
}
|
||||
|
||||
func (p *Peer) handleRequestSubscription(ctx context.Context, req *RequestSubscriptionMsg) (err error) {
|
||||
log.Debug(fmt.Sprintf("handleRequestSubscription: streamer %s to subscribe to %s with stream %s", p.streamer.addr.ID(), p.ID(), req.Stream))
|
||||
log.Debug(fmt.Sprintf("handleRequestSubscription: streamer %s to subscribe to %s with stream %s", p.streamer.addr, p.ID(), req.Stream))
|
||||
return p.streamer.Subscribe(p.ID(), req.Stream, req.History, req.Priority)
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ func (p *Peer) handleSubscribeMsg(ctx context.Context, req *SubscribeMsg) (err e
|
|||
}
|
||||
}()
|
||||
|
||||
log.Debug("received subscription", "from", p.streamer.addr.ID(), "peer", p.ID(), "stream", req.Stream, "history", req.History)
|
||||
log.Debug("received subscription", "from", p.streamer.addr, "peer", p.ID(), "stream", req.Stream, "history", req.History)
|
||||
|
||||
f, err := p.streamer.GetServerFunc(req.Stream.Name)
|
||||
if err != nil {
|
||||
|
@ -254,7 +254,7 @@ func (p *Peer) handleOfferedHashesMsg(ctx context.Context, req *OfferedHashesMsg
|
|||
c.sessionAt = req.From
|
||||
}
|
||||
from, to := c.nextBatch(req.To + 1)
|
||||
log.Trace("set next batch", "peer", p.ID(), "stream", req.Stream, "from", req.From, "to", req.To, "addr", p.streamer.addr.ID())
|
||||
log.Trace("set next batch", "peer", p.ID(), "stream", req.Stream, "from", req.From, "to", req.To, "addr", p.streamer.addr)
|
||||
if from == to {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
"github.com/ethereum/go-ethereum/swarm/log"
|
||||
"github.com/ethereum/go-ethereum/swarm/network"
|
||||
|
@ -116,10 +116,9 @@ The snapshot should have 'streamer' in its service list.
|
|||
func runFileRetrievalTest(nodeCount int) error {
|
||||
sim := simulation.New(map[string]simulation.ServiceFunc{
|
||||
"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
|
||||
|
||||
id := ctx.Config.ID
|
||||
addr := network.NewAddrFromNodeID(id)
|
||||
store, datadir, err := createTestLocalStorageForID(id, addr)
|
||||
node := ctx.Config.Node()
|
||||
addr := network.NewAddr(node)
|
||||
store, datadir, err := createTestLocalStorageForID(node.ID(), addr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -134,7 +133,7 @@ func runFileRetrievalTest(nodeCount int) error {
|
|||
delivery := NewDelivery(kad, netStore)
|
||||
netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
|
||||
|
||||
r := NewRegistry(addr, delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
|
||||
r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
|
||||
DoSync: true,
|
||||
SyncUpdateDelay: 3 * time.Second,
|
||||
})
|
||||
|
@ -158,9 +157,9 @@ func runFileRetrievalTest(nodeCount int) error {
|
|||
|
||||
conf := &synctestConfig{}
|
||||
//map of discover ID to indexes of chunks expected at that ID
|
||||
conf.idToChunksMap = make(map[discover.NodeID][]int)
|
||||
conf.idToChunksMap = make(map[enode.ID][]int)
|
||||
//map of overlay address to discover ID
|
||||
conf.addrToIDMap = make(map[string]discover.NodeID)
|
||||
conf.addrToIDMap = make(map[string]enode.ID)
|
||||
//array where the generated chunk hashes will be stored
|
||||
conf.hashes = make([]storage.Address, 0)
|
||||
|
||||
|
@ -176,11 +175,11 @@ func runFileRetrievalTest(nodeCount int) error {
|
|||
nodeIDs := sim.UpNodeIDs()
|
||||
for _, n := range nodeIDs {
|
||||
//get the kademlia overlay address from this ID
|
||||
a := network.ToOverlayAddr(n.Bytes())
|
||||
a := n.Bytes()
|
||||
//append it to the array of all overlay addresses
|
||||
conf.addrs = append(conf.addrs, a)
|
||||
//the proximity calculation is on overlay addr,
|
||||
//the p2p/simulations check func triggers on discover.NodeID,
|
||||
//the p2p/simulations check func triggers on enode.ID,
|
||||
//so we need to know which overlay addr maps to which nodeID
|
||||
conf.addrToIDMap[string(a)] = n
|
||||
}
|
||||
|
@ -266,10 +265,9 @@ The snapshot should have 'streamer' in its service list.
|
|||
func runRetrievalTest(chunkCount int, nodeCount int) error {
|
||||
sim := simulation.New(map[string]simulation.ServiceFunc{
|
||||
"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
|
||||
|
||||
id := ctx.Config.ID
|
||||
addr := network.NewAddrFromNodeID(id)
|
||||
store, datadir, err := createTestLocalStorageForID(id, addr)
|
||||
node := ctx.Config.Node()
|
||||
addr := network.NewAddr(node)
|
||||
store, datadir, err := createTestLocalStorageForID(node.ID(), addr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -284,7 +282,7 @@ func runRetrievalTest(chunkCount int, nodeCount int) error {
|
|||
delivery := NewDelivery(kad, netStore)
|
||||
netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
|
||||
|
||||
r := NewRegistry(addr, delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
|
||||
r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
|
||||
DoSync: true,
|
||||
SyncUpdateDelay: 0,
|
||||
})
|
||||
|
@ -307,9 +305,9 @@ func runRetrievalTest(chunkCount int, nodeCount int) error {
|
|||
|
||||
conf := &synctestConfig{}
|
||||
//map of discover ID to indexes of chunks expected at that ID
|
||||
conf.idToChunksMap = make(map[discover.NodeID][]int)
|
||||
conf.idToChunksMap = make(map[enode.ID][]int)
|
||||
//map of overlay address to discover ID
|
||||
conf.addrToIDMap = make(map[string]discover.NodeID)
|
||||
conf.addrToIDMap = make(map[string]enode.ID)
|
||||
//array where the generated chunk hashes will be stored
|
||||
conf.hashes = make([]storage.Address, 0)
|
||||
|
||||
|
@ -323,11 +321,11 @@ func runRetrievalTest(chunkCount int, nodeCount int) error {
|
|||
nodeIDs := sim.UpNodeIDs()
|
||||
for _, n := range nodeIDs {
|
||||
//get the kademlia overlay address from this ID
|
||||
a := network.ToOverlayAddr(n.Bytes())
|
||||
a := n.Bytes()
|
||||
//append it to the array of all overlay addresses
|
||||
conf.addrs = append(conf.addrs, a)
|
||||
//the proximity calculation is on overlay addr,
|
||||
//the p2p/simulations check func triggers on discover.NodeID,
|
||||
//the p2p/simulations check func triggers on enode.ID,
|
||||
//so we need to know which overlay addr maps to which nodeID
|
||||
conf.addrToIDMap[string(a)] = n
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
"github.com/ethereum/go-ethereum/swarm/network"
|
||||
"github.com/ethereum/go-ethereum/swarm/network/simulation"
|
||||
|
@ -45,14 +45,14 @@ const MaxTimeout = 600
|
|||
type synctestConfig struct {
|
||||
addrs [][]byte
|
||||
hashes []storage.Address
|
||||
idToChunksMap map[discover.NodeID][]int
|
||||
idToChunksMap map[enode.ID][]int
|
||||
//chunksToNodesMap map[string][]int
|
||||
addrToIDMap map[string]discover.NodeID
|
||||
addrToIDMap map[string]enode.ID
|
||||
}
|
||||
|
||||
// Tests in this file should not request chunks from peers.
|
||||
// This function will panic indicating that there is a problem if request has been made.
|
||||
func dummyRequestFromPeers(_ context.Context, req *network.Request) (*discover.NodeID, chan struct{}, error) {
|
||||
func dummyRequestFromPeers(_ context.Context, req *network.Request) (*enode.ID, chan struct{}, error) {
|
||||
panic(fmt.Sprintf("unexpected request: address %s, source %s", req.Addr.String(), req.Source.String()))
|
||||
}
|
||||
|
||||
|
@ -134,10 +134,9 @@ func TestSyncingViaDirectSubscribe(t *testing.T) {
|
|||
func testSyncingViaGlobalSync(t *testing.T, chunkCount int, nodeCount int) {
|
||||
sim := simulation.New(map[string]simulation.ServiceFunc{
|
||||
"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
|
||||
|
||||
id := ctx.Config.ID
|
||||
addr := network.NewAddrFromNodeID(id)
|
||||
store, datadir, err := createTestLocalStorageForID(id, addr)
|
||||
n := ctx.Config.Node()
|
||||
addr := network.NewAddr(n)
|
||||
store, datadir, err := createTestLocalStorageForID(n.ID(), addr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -151,7 +150,7 @@ func testSyncingViaGlobalSync(t *testing.T, chunkCount int, nodeCount int) {
|
|||
delivery := NewDelivery(kad, netStore)
|
||||
netStore.NewNetFetcherFunc = network.NewFetcherFactory(dummyRequestFromPeers, true).New
|
||||
|
||||
r := NewRegistry(addr, delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
|
||||
r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
|
||||
DoSync: true,
|
||||
SyncUpdateDelay: 3 * time.Second,
|
||||
})
|
||||
|
@ -173,9 +172,9 @@ func testSyncingViaGlobalSync(t *testing.T, chunkCount int, nodeCount int) {
|
|||
|
||||
conf := &synctestConfig{}
|
||||
//map of discover ID to indexes of chunks expected at that ID
|
||||
conf.idToChunksMap = make(map[discover.NodeID][]int)
|
||||
conf.idToChunksMap = make(map[enode.ID][]int)
|
||||
//map of overlay address to discover ID
|
||||
conf.addrToIDMap = make(map[string]discover.NodeID)
|
||||
conf.addrToIDMap = make(map[string]enode.ID)
|
||||
//array where the generated chunk hashes will be stored
|
||||
conf.hashes = make([]storage.Address, 0)
|
||||
|
||||
|
@ -209,11 +208,11 @@ func testSyncingViaGlobalSync(t *testing.T, chunkCount int, nodeCount int) {
|
|||
nodeIDs := sim.UpNodeIDs()
|
||||
for _, n := range nodeIDs {
|
||||
//get the kademlia overlay address from this ID
|
||||
a := network.ToOverlayAddr(n.Bytes())
|
||||
a := n.Bytes()
|
||||
//append it to the array of all overlay addresses
|
||||
conf.addrs = append(conf.addrs, a)
|
||||
//the proximity calculation is on overlay addr,
|
||||
//the p2p/simulations check func triggers on discover.NodeID,
|
||||
//the p2p/simulations check func triggers on enode.ID,
|
||||
//so we need to know which overlay addr maps to which nodeID
|
||||
conf.addrToIDMap[string(a)] = n
|
||||
}
|
||||
|
@ -317,10 +316,9 @@ kademlia network. The snapshot should have 'streamer' in its service list.
|
|||
func testSyncingViaDirectSubscribe(t *testing.T, chunkCount int, nodeCount int) error {
|
||||
sim := simulation.New(map[string]simulation.ServiceFunc{
|
||||
"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
|
||||
|
||||
id := ctx.Config.ID
|
||||
addr := network.NewAddrFromNodeID(id)
|
||||
store, datadir, err := createTestLocalStorageForID(id, addr)
|
||||
n := ctx.Config.Node()
|
||||
addr := network.NewAddr(n)
|
||||
store, datadir, err := createTestLocalStorageForID(n.ID(), addr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -334,7 +332,7 @@ func testSyncingViaDirectSubscribe(t *testing.T, chunkCount int, nodeCount int)
|
|||
delivery := NewDelivery(kad, netStore)
|
||||
netStore.NewNetFetcherFunc = network.NewFetcherFactory(dummyRequestFromPeers, true).New
|
||||
|
||||
r := NewRegistry(addr, delivery, netStore, state.NewInmemoryStore(), nil)
|
||||
r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), nil)
|
||||
bucket.Store(bucketKeyRegistry, r)
|
||||
|
||||
fileStore := storage.NewFileStore(netStore, storage.NewFileStoreParams())
|
||||
|
@ -357,9 +355,9 @@ func testSyncingViaDirectSubscribe(t *testing.T, chunkCount int, nodeCount int)
|
|||
|
||||
conf := &synctestConfig{}
|
||||
//map of discover ID to indexes of chunks expected at that ID
|
||||
conf.idToChunksMap = make(map[discover.NodeID][]int)
|
||||
conf.idToChunksMap = make(map[enode.ID][]int)
|
||||
//map of overlay address to discover ID
|
||||
conf.addrToIDMap = make(map[string]discover.NodeID)
|
||||
conf.addrToIDMap = make(map[string]enode.ID)
|
||||
//array where the generated chunk hashes will be stored
|
||||
conf.hashes = make([]storage.Address, 0)
|
||||
|
||||
|
@ -390,11 +388,11 @@ func testSyncingViaDirectSubscribe(t *testing.T, chunkCount int, nodeCount int)
|
|||
nodeIDs := sim.UpNodeIDs()
|
||||
for _, n := range nodeIDs {
|
||||
//get the kademlia overlay address from this ID
|
||||
a := network.ToOverlayAddr(n.Bytes())
|
||||
a := n.Bytes()
|
||||
//append it to the array of all overlay addresses
|
||||
conf.addrs = append(conf.addrs, a)
|
||||
//the proximity calculation is on overlay addr,
|
||||
//the p2p/simulations check func triggers on discover.NodeID,
|
||||
//the p2p/simulations check func triggers on enode.ID,
|
||||
//so we need to know which overlay addr maps to which nodeID
|
||||
conf.addrToIDMap[string(a)] = n
|
||||
}
|
||||
|
@ -525,9 +523,8 @@ func startSyncing(r *Registry, conf *synctestConfig) (int, error) {
|
|||
kad := r.delivery.kad
|
||||
subCnt := 0
|
||||
//iterate over each bin and solicit needed subscription to bins
|
||||
kad.EachBin(r.addr.Over(), pof, 0, func(conn *network.Peer, po int) bool {
|
||||
kad.EachBin(r.addr[:], pof, 0, func(conn *network.Peer, po int) bool {
|
||||
//identify begin and start index of the bin(s) we want to subscribe to
|
||||
|
||||
subCnt++
|
||||
err = r.RequestSubscription(conf.addrToIDMap[string(conn.Address())], NewStream("SYNC", FormatSyncBinKey(uint8(po)), true), NewRange(0, 0), High)
|
||||
if err != nil {
|
||||
|
@ -580,7 +577,7 @@ func mapKeysToNodes(conf *synctestConfig) {
|
|||
}
|
||||
|
||||
//upload a file(chunks) to a single local node store
|
||||
func uploadFileToSingleNodeStore(id discover.NodeID, chunkCount int, lstore *storage.LocalStore) ([]storage.Address, error) {
|
||||
func uploadFileToSingleNodeStore(id enode.ID, chunkCount int, lstore *storage.LocalStore) ([]storage.Address, error) {
|
||||
log.Debug(fmt.Sprintf("Uploading to node id: %s", id))
|
||||
fileStore := storage.NewFileStore(lstore, storage.NewFileStoreParams())
|
||||
size := chunkSize
|
||||
|
|
|
@ -25,7 +25,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/protocols"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/swarm/log"
|
||||
|
@ -48,15 +48,15 @@ const (
|
|||
|
||||
// Registry registry for outgoing and incoming streamer constructors
|
||||
type Registry struct {
|
||||
addr enode.ID
|
||||
api *API
|
||||
addr *network.BzzAddr
|
||||
skipCheck bool
|
||||
clientMu sync.RWMutex
|
||||
serverMu sync.RWMutex
|
||||
peersMu sync.RWMutex
|
||||
serverFuncs map[string]func(*Peer, string, bool) (Server, error)
|
||||
clientFuncs map[string]func(*Peer, string, bool) (Client, error)
|
||||
peers map[discover.NodeID]*Peer
|
||||
peers map[enode.ID]*Peer
|
||||
delivery *Delivery
|
||||
intervalsStore state.Store
|
||||
doRetrieve bool
|
||||
|
@ -71,7 +71,7 @@ type RegistryOptions struct {
|
|||
}
|
||||
|
||||
// NewRegistry is Streamer constructor
|
||||
func NewRegistry(addr *network.BzzAddr, delivery *Delivery, syncChunkStore storage.SyncChunkStore, intervalsStore state.Store, options *RegistryOptions) *Registry {
|
||||
func NewRegistry(localID enode.ID, delivery *Delivery, syncChunkStore storage.SyncChunkStore, intervalsStore state.Store, options *RegistryOptions) *Registry {
|
||||
if options == nil {
|
||||
options = &RegistryOptions{}
|
||||
}
|
||||
|
@ -79,11 +79,11 @@ func NewRegistry(addr *network.BzzAddr, delivery *Delivery, syncChunkStore stora
|
|||
options.SyncUpdateDelay = 15 * time.Second
|
||||
}
|
||||
streamer := &Registry{
|
||||
addr: addr,
|
||||
addr: localID,
|
||||
skipCheck: options.SkipCheck,
|
||||
serverFuncs: make(map[string]func(*Peer, string, bool) (Server, error)),
|
||||
clientFuncs: make(map[string]func(*Peer, string, bool) (Client, error)),
|
||||
peers: make(map[discover.NodeID]*Peer),
|
||||
peers: make(map[enode.ID]*Peer),
|
||||
delivery: delivery,
|
||||
intervalsStore: intervalsStore,
|
||||
doRetrieve: options.DoRetrieve,
|
||||
|
@ -220,7 +220,7 @@ func (r *Registry) GetServerFunc(stream string) (func(*Peer, string, bool) (Serv
|
|||
return f, nil
|
||||
}
|
||||
|
||||
func (r *Registry) RequestSubscription(peerId discover.NodeID, s Stream, h *Range, prio uint8) error {
|
||||
func (r *Registry) RequestSubscription(peerId enode.ID, s Stream, h *Range, prio uint8) error {
|
||||
// check if the stream is registered
|
||||
if _, err := r.GetServerFunc(s.Name); err != nil {
|
||||
return err
|
||||
|
@ -248,7 +248,7 @@ func (r *Registry) RequestSubscription(peerId discover.NodeID, s Stream, h *Rang
|
|||
}
|
||||
|
||||
// Subscribe initiates the streamer
|
||||
func (r *Registry) Subscribe(peerId discover.NodeID, s Stream, h *Range, priority uint8) error {
|
||||
func (r *Registry) Subscribe(peerId enode.ID, s Stream, h *Range, priority uint8) error {
|
||||
// check if the stream is registered
|
||||
if _, err := r.GetClientFunc(s.Name); err != nil {
|
||||
return err
|
||||
|
@ -288,7 +288,7 @@ func (r *Registry) Subscribe(peerId discover.NodeID, s Stream, h *Range, priorit
|
|||
return peer.SendPriority(context.TODO(), msg, priority)
|
||||
}
|
||||
|
||||
func (r *Registry) Unsubscribe(peerId discover.NodeID, s Stream) error {
|
||||
func (r *Registry) Unsubscribe(peerId enode.ID, s Stream) error {
|
||||
peer := r.getPeer(peerId)
|
||||
if peer == nil {
|
||||
return fmt.Errorf("peer not found %v", peerId)
|
||||
|
@ -307,7 +307,7 @@ func (r *Registry) Unsubscribe(peerId discover.NodeID, s Stream) error {
|
|||
|
||||
// Quit sends the QuitMsg to the peer to remove the
|
||||
// stream peer client and terminate the streaming.
|
||||
func (r *Registry) Quit(peerId discover.NodeID, s Stream) error {
|
||||
func (r *Registry) Quit(peerId enode.ID, s Stream) error {
|
||||
peer := r.getPeer(peerId)
|
||||
if peer == nil {
|
||||
log.Debug("stream quit: peer not found", "peer", peerId, "stream", s)
|
||||
|
@ -327,7 +327,7 @@ func (r *Registry) NodeInfo() interface{} {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *Registry) PeerInfo(id discover.NodeID) interface{} {
|
||||
func (r *Registry) PeerInfo(id enode.ID) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -335,7 +335,7 @@ func (r *Registry) Close() error {
|
|||
return r.intervalsStore.Close()
|
||||
}
|
||||
|
||||
func (r *Registry) getPeer(peerId discover.NodeID) *Peer {
|
||||
func (r *Registry) getPeer(peerId enode.ID) *Peer {
|
||||
r.peersMu.RLock()
|
||||
defer r.peersMu.RUnlock()
|
||||
|
||||
|
@ -390,7 +390,7 @@ func (r *Registry) updateSyncing() {
|
|||
// map of all SYNC streams for all peers
|
||||
// used at the and of the function to remove servers
|
||||
// that are not needed anymore
|
||||
subs := make(map[discover.NodeID]map[Stream]struct{})
|
||||
subs := make(map[enode.ID]map[Stream]struct{})
|
||||
r.peersMu.RLock()
|
||||
for id, peer := range r.peers {
|
||||
peer.serverMu.RLock()
|
||||
|
@ -407,8 +407,8 @@ func (r *Registry) updateSyncing() {
|
|||
r.peersMu.RUnlock()
|
||||
|
||||
// request subscriptions for all nodes and bins
|
||||
kad.EachBin(r.addr.Over(), pot.DefaultPof(256), 0, func(p *network.Peer, bin int) bool {
|
||||
log.Debug(fmt.Sprintf("Requesting subscription by: registry %s from peer %s for bin: %d", r.addr.ID(), p.ID(), bin))
|
||||
kad.EachBin(r.addr[:], pot.DefaultPof(256), 0, func(p *network.Peer, bin int) bool {
|
||||
log.Debug(fmt.Sprintf("Requesting subscription by: registry %s from peer %s for bin: %d", r.addr, p.ID(), bin))
|
||||
|
||||
// bin is always less then 256 and it is safe to convert it to type uint8
|
||||
stream := NewStream("SYNC", FormatSyncBinKey(uint8(bin)), true)
|
||||
|
@ -446,7 +446,7 @@ func (r *Registry) updateSyncing() {
|
|||
|
||||
func (r *Registry) runProtocol(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||
peer := protocols.NewPeer(p, rw, Spec)
|
||||
bp := network.NewBzzPeer(peer, r.addr)
|
||||
bp := network.NewBzzPeer(peer)
|
||||
np := network.NewPeer(bp, r.delivery.kad)
|
||||
r.delivery.kad.On(np)
|
||||
defer r.delivery.kad.Off(np)
|
||||
|
@ -724,10 +724,10 @@ func NewAPI(r *Registry) *API {
|
|||
}
|
||||
}
|
||||
|
||||
func (api *API) SubscribeStream(peerId discover.NodeID, s Stream, history *Range, priority uint8) error {
|
||||
func (api *API) SubscribeStream(peerId enode.ID, s Stream, history *Range, priority uint8) error {
|
||||
return api.streamer.Subscribe(peerId, s, history, priority)
|
||||
}
|
||||
|
||||
func (api *API) UnsubscribeStream(peerId discover.NodeID, s Stream) error {
|
||||
func (api *API) UnsubscribeStream(peerId enode.ID, s Stream) error {
|
||||
return api.streamer.Unsubscribe(peerId, s)
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ func TestStreamerSubscribe(t *testing.T) {
|
|||
}
|
||||
|
||||
stream := NewStream("foo", "", true)
|
||||
err = streamer.Subscribe(tester.IDs[0], stream, NewRange(0, 0), Top)
|
||||
err = streamer.Subscribe(tester.Nodes[0].ID(), stream, NewRange(0, 0), Top)
|
||||
if err == nil || err.Error() != "stream foo not registered" {
|
||||
t.Fatalf("Expected error %v, got %v", "stream foo not registered", err)
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ func TestStreamerRequestSubscription(t *testing.T) {
|
|||
}
|
||||
|
||||
stream := NewStream("foo", "", false)
|
||||
err = streamer.RequestSubscription(tester.IDs[0], stream, &Range{}, Top)
|
||||
err = streamer.RequestSubscription(tester.Nodes[0].ID(), stream, &Range{}, Top)
|
||||
if err == nil || err.Error() != "stream foo not registered" {
|
||||
t.Fatalf("Expected error %v, got %v", "stream foo not registered", err)
|
||||
}
|
||||
|
@ -135,10 +135,10 @@ func TestStreamerDownstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
|
|||
return newTestClient(t), nil
|
||||
})
|
||||
|
||||
peerID := tester.IDs[0]
|
||||
node := tester.Nodes[0]
|
||||
|
||||
stream := NewStream("foo", "", true)
|
||||
err = streamer.Subscribe(peerID, stream, NewRange(5, 8), Top)
|
||||
err = streamer.Subscribe(node.ID(), stream, NewRange(5, 8), Top)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ func TestStreamerDownstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
|
|||
History: NewRange(5, 8),
|
||||
Priority: Top,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -173,7 +173,7 @@ func TestStreamerDownstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
|
|||
To: 8,
|
||||
Stream: stream,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
Expects: []p2ptest.Expect{
|
||||
|
@ -185,7 +185,7 @@ func TestStreamerDownstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
|
|||
From: 9,
|
||||
To: 0,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -194,7 +194,7 @@ func TestStreamerDownstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = streamer.Unsubscribe(peerID, stream)
|
||||
err = streamer.Unsubscribe(node.ID(), stream)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ func TestStreamerDownstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
|
|||
Msg: &UnsubscribeMsg{
|
||||
Stream: stream,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -230,7 +230,7 @@ func TestStreamerUpstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
|
|||
return newTestServer(t), nil
|
||||
})
|
||||
|
||||
peerID := tester.IDs[0]
|
||||
node := tester.Nodes[0]
|
||||
|
||||
err = tester.TestExchanges(p2ptest.Exchange{
|
||||
Label: "Subscribe message",
|
||||
|
@ -242,7 +242,7 @@ func TestStreamerUpstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
|
|||
History: NewRange(5, 8),
|
||||
Priority: Top,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
Expects: []p2ptest.Expect{
|
||||
|
@ -257,7 +257,7 @@ func TestStreamerUpstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
|
|||
From: 6,
|
||||
To: 9,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -274,7 +274,7 @@ func TestStreamerUpstreamSubscribeUnsubscribeMsgExchange(t *testing.T) {
|
|||
Msg: &UnsubscribeMsg{
|
||||
Stream: stream,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -297,7 +297,7 @@ func TestStreamerUpstreamSubscribeUnsubscribeMsgExchangeLive(t *testing.T) {
|
|||
return newTestServer(t), nil
|
||||
})
|
||||
|
||||
peerID := tester.IDs[0]
|
||||
node := tester.Nodes[0]
|
||||
|
||||
err = tester.TestExchanges(p2ptest.Exchange{
|
||||
Label: "Subscribe message",
|
||||
|
@ -308,7 +308,7 @@ func TestStreamerUpstreamSubscribeUnsubscribeMsgExchangeLive(t *testing.T) {
|
|||
Stream: stream,
|
||||
Priority: Top,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
Expects: []p2ptest.Expect{
|
||||
|
@ -323,7 +323,7 @@ func TestStreamerUpstreamSubscribeUnsubscribeMsgExchangeLive(t *testing.T) {
|
|||
From: 1,
|
||||
To: 1,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -340,7 +340,7 @@ func TestStreamerUpstreamSubscribeUnsubscribeMsgExchangeLive(t *testing.T) {
|
|||
Msg: &UnsubscribeMsg{
|
||||
Stream: stream,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -363,7 +363,7 @@ func TestStreamerUpstreamSubscribeErrorMsgExchange(t *testing.T) {
|
|||
|
||||
stream := NewStream("bar", "", true)
|
||||
|
||||
peerID := tester.IDs[0]
|
||||
node := tester.Nodes[0]
|
||||
|
||||
err = tester.TestExchanges(p2ptest.Exchange{
|
||||
Label: "Subscribe message",
|
||||
|
@ -375,7 +375,7 @@ func TestStreamerUpstreamSubscribeErrorMsgExchange(t *testing.T) {
|
|||
History: NewRange(5, 8),
|
||||
Priority: Top,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
Expects: []p2ptest.Expect{
|
||||
|
@ -384,7 +384,7 @@ func TestStreamerUpstreamSubscribeErrorMsgExchange(t *testing.T) {
|
|||
Msg: &SubscribeErrorMsg{
|
||||
Error: "stream bar not registered",
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -409,7 +409,7 @@ func TestStreamerUpstreamSubscribeLiveAndHistory(t *testing.T) {
|
|||
}, nil
|
||||
})
|
||||
|
||||
peerID := tester.IDs[0]
|
||||
node := tester.Nodes[0]
|
||||
|
||||
err = tester.TestExchanges(p2ptest.Exchange{
|
||||
Label: "Subscribe message",
|
||||
|
@ -421,7 +421,7 @@ func TestStreamerUpstreamSubscribeLiveAndHistory(t *testing.T) {
|
|||
History: NewRange(5, 8),
|
||||
Priority: Top,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
Expects: []p2ptest.Expect{
|
||||
|
@ -436,7 +436,7 @@ func TestStreamerUpstreamSubscribeLiveAndHistory(t *testing.T) {
|
|||
From: 6,
|
||||
To: 9,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
{
|
||||
Code: 1,
|
||||
|
@ -449,7 +449,7 @@ func TestStreamerUpstreamSubscribeLiveAndHistory(t *testing.T) {
|
|||
To: 1,
|
||||
Hashes: make([]byte, HashSize),
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -475,9 +475,9 @@ func TestStreamerDownstreamOfferedHashesMsgExchange(t *testing.T) {
|
|||
return tc, nil
|
||||
})
|
||||
|
||||
peerID := tester.IDs[0]
|
||||
node := tester.Nodes[0]
|
||||
|
||||
err = streamer.Subscribe(peerID, stream, NewRange(5, 8), Top)
|
||||
err = streamer.Subscribe(node.ID(), stream, NewRange(5, 8), Top)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
@ -492,7 +492,7 @@ func TestStreamerDownstreamOfferedHashesMsgExchange(t *testing.T) {
|
|||
History: NewRange(5, 8),
|
||||
Priority: Top,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -510,7 +510,7 @@ func TestStreamerDownstreamOfferedHashesMsgExchange(t *testing.T) {
|
|||
To: 8,
|
||||
Stream: stream,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
Expects: []p2ptest.Expect{
|
||||
|
@ -522,7 +522,7 @@ func TestStreamerDownstreamOfferedHashesMsgExchange(t *testing.T) {
|
|||
From: 9,
|
||||
To: 0,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -569,10 +569,10 @@ func TestStreamerRequestSubscriptionQuitMsgExchange(t *testing.T) {
|
|||
return newTestServer(t), nil
|
||||
})
|
||||
|
||||
peerID := tester.IDs[0]
|
||||
node := tester.Nodes[0]
|
||||
|
||||
stream := NewStream("foo", "", true)
|
||||
err = streamer.RequestSubscription(peerID, stream, NewRange(5, 8), Top)
|
||||
err = streamer.RequestSubscription(node.ID(), stream, NewRange(5, 8), Top)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
@ -588,7 +588,7 @@ func TestStreamerRequestSubscriptionQuitMsgExchange(t *testing.T) {
|
|||
History: NewRange(5, 8),
|
||||
Priority: Top,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -602,7 +602,7 @@ func TestStreamerRequestSubscriptionQuitMsgExchange(t *testing.T) {
|
|||
History: NewRange(5, 8),
|
||||
Priority: Top,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
Expects: []p2ptest.Expect{
|
||||
|
@ -617,7 +617,7 @@ func TestStreamerRequestSubscriptionQuitMsgExchange(t *testing.T) {
|
|||
From: 6,
|
||||
To: 9,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
{
|
||||
Code: 1,
|
||||
|
@ -630,7 +630,7 @@ func TestStreamerRequestSubscriptionQuitMsgExchange(t *testing.T) {
|
|||
To: 1,
|
||||
Hashes: make([]byte, HashSize),
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -639,7 +639,7 @@ func TestStreamerRequestSubscriptionQuitMsgExchange(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = streamer.Quit(peerID, stream)
|
||||
err = streamer.Quit(node.ID(), stream)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
@ -652,7 +652,7 @@ func TestStreamerRequestSubscriptionQuitMsgExchange(t *testing.T) {
|
|||
Msg: &QuitMsg{
|
||||
Stream: stream,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -663,7 +663,7 @@ func TestStreamerRequestSubscriptionQuitMsgExchange(t *testing.T) {
|
|||
|
||||
historyStream := getHistoryStream(stream)
|
||||
|
||||
err = streamer.Quit(peerID, historyStream)
|
||||
err = streamer.Quit(node.ID(), historyStream)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
@ -676,7 +676,7 @@ func TestStreamerRequestSubscriptionQuitMsgExchange(t *testing.T) {
|
|||
Msg: &QuitMsg{
|
||||
Stream: historyStream,
|
||||
},
|
||||
Peer: peerID,
|
||||
Peer: node.ID(),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -197,7 +197,7 @@ func NewSwarmSyncerClient(p *Peer, store storage.SyncChunkStore, stream Stream)
|
|||
|
||||
// // StartSyncing is called on the Peer to start the syncing process
|
||||
// // the idea is that it is called only after kademlia is close to healthy
|
||||
// func StartSyncing(s *Streamer, peerId discover.NodeID, po uint8, nn bool) {
|
||||
// func StartSyncing(s *Streamer, peerId enode.ID, po uint8, nn bool) {
|
||||
// lastPO := po
|
||||
// if nn {
|
||||
// lastPO = maxPO
|
||||
|
|
|
@ -31,7 +31,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
"github.com/ethereum/go-ethereum/swarm/log"
|
||||
"github.com/ethereum/go-ethereum/swarm/network"
|
||||
|
@ -50,7 +50,7 @@ func TestSyncerSimulation(t *testing.T) {
|
|||
testSyncBetweenNodes(t, 16, 1, dataChunkCount, true, 1)
|
||||
}
|
||||
|
||||
func createMockStore(globalStore *mockdb.GlobalStore, id discover.NodeID, addr *network.BzzAddr) (lstore storage.ChunkStore, datadir string, err error) {
|
||||
func createMockStore(globalStore *mockdb.GlobalStore, id enode.ID, addr *network.BzzAddr) (lstore storage.ChunkStore, datadir string, err error) {
|
||||
address := common.BytesToAddress(id.Bytes())
|
||||
mockStore := globalStore.NewNodeStore(address)
|
||||
params := storage.NewDefaultLocalStoreParams()
|
||||
|
@ -72,8 +72,8 @@ func testSyncBetweenNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck
|
|||
var globalStore *mockdb.GlobalStore
|
||||
var gDir, datadir string
|
||||
|
||||
id := ctx.Config.ID
|
||||
addr := network.NewAddrFromNodeID(id)
|
||||
node := ctx.Config.Node()
|
||||
addr := network.NewAddr(node)
|
||||
//hack to put addresses in same space
|
||||
addr.OAddr[0] = byte(0)
|
||||
|
||||
|
@ -82,9 +82,9 @@ func testSyncBetweenNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck
|
|||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Something went wrong; using mockStore enabled but globalStore is nil")
|
||||
}
|
||||
store, datadir, err = createMockStore(globalStore, id, addr)
|
||||
store, datadir, err = createMockStore(globalStore, node.ID(), addr)
|
||||
} else {
|
||||
store, datadir, err = createTestLocalStorageForID(id, addr)
|
||||
store, datadir, err = createTestLocalStorageForID(node.ID(), addr)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -113,7 +113,7 @@ func testSyncBetweenNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck
|
|||
|
||||
bucket.Store(bucketKeyDelivery, delivery)
|
||||
|
||||
r := NewRegistry(addr, delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
|
||||
r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
|
||||
SkipCheck: skipCheck,
|
||||
})
|
||||
|
||||
|
@ -139,7 +139,7 @@ func testSyncBetweenNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck
|
|||
result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) error {
|
||||
nodeIDs := sim.UpNodeIDs()
|
||||
|
||||
nodeIndex := make(map[discover.NodeID]int)
|
||||
nodeIndex := make(map[enode.ID]int)
|
||||
for i, id := range nodeIDs {
|
||||
nodeIndex[id] = i
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue