Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
24d44f35
Commit
24d44f35
authored
May 06, 2015
by
Jeffrey Wilcke
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #791 from fjl/discover-sha3-distance
p2p/discover: sha3-based node distance
parents
323216ed
2adcc31b
Changes
14
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
651 additions
and
292 deletions
+651
-292
types.go
common/types.go
+14
-1
backend.go
eth/backend.go
+2
-2
database.go
p2p/discover/database.go
+2
-0
database_test.go
p2p/discover/database_test.go
+43
-31
node.go
p2p/discover/node.go
+48
-50
node_test.go
p2p/discover/node_test.go
+57
-38
table.go
p2p/discover/table.go
+37
-35
table_test.go
p2p/discover/table_test.go
+297
-55
udp.go
p2p/discover/udp.go
+80
-34
udp_test.go
p2p/discover/udp_test.go
+62
-37
handshake_test.go
p2p/handshake_test.go
+6
-6
peer.go
p2p/peer.go
+1
-1
server.go
p2p/server.go
+1
-1
server_test.go
p2p/server_test.go
+1
-1
No files found.
common/types.go
View file @
24d44f35
package
common
import
"math/big"
import
(
"math/big"
"math/rand"
"reflect"
)
const
(
hashLength
=
32
...
...
@@ -48,6 +52,15 @@ func (h *Hash) Set(other Hash) {
}
}
// Generate implements testing/quick.Generator.
func
(
h
Hash
)
Generate
(
rand
*
rand
.
Rand
,
size
int
)
reflect
.
Value
{
m
:=
rand
.
Intn
(
len
(
h
))
for
i
:=
len
(
h
)
-
1
;
i
>
m
;
i
--
{
h
[
i
]
=
byte
(
rand
.
Uint32
())
}
return
reflect
.
ValueOf
(
h
)
}
/////////// Address
func
BytesToAddress
(
b
[]
byte
)
Address
{
var
a
Address
...
...
eth/backend.go
View file @
24d44f35
...
...
@@ -277,8 +277,8 @@ func (s *Ethereum) NodeInfo() *NodeInfo {
NodeUrl
:
node
.
String
(),
NodeID
:
node
.
ID
.
String
(),
IP
:
node
.
IP
.
String
(),
DiscPort
:
node
.
DiscPort
,
TCPPort
:
node
.
TCPPort
,
DiscPort
:
int
(
node
.
UDP
)
,
TCPPort
:
int
(
node
.
TCP
)
,
ListenAddr
:
s
.
net
.
ListenAddr
,
Td
:
s
.
ChainManager
()
.
Td
()
.
String
(),
}
...
...
p2p/discover/database.go
View file @
24d44f35
...
...
@@ -10,6 +10,7 @@ import (
"sync"
"time"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
...
...
@@ -167,6 +168,7 @@ func (db *nodeDB) node(id NodeID) *Node {
glog
.
V
(
logger
.
Warn
)
.
Infof
(
"failed to decode node RLP: %v"
,
err
)
return
nil
}
node
.
sha
=
crypto
.
Sha3Hash
(
node
.
ID
[
:
])
return
node
}
...
...
p2p/discover/database_test.go
View file @
24d44f35
...
...
@@ -6,6 +6,7 @@ import (
"net"
"os"
"path/filepath"
"reflect"
"testing"
"time"
)
...
...
@@ -85,11 +86,12 @@ func TestNodeDBInt64(t *testing.T) {
}
func
TestNodeDBFetchStore
(
t
*
testing
.
T
)
{
node
:=
&
Node
{
ID
:
MustHexID
(
"0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
IP
:
net
.
IP
([]
byte
{
192
,
168
,
0
,
1
}),
TCPPort
:
30303
,
}
node
:=
newNode
(
MustHexID
(
"0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
net
.
IP
{
192
,
168
,
0
,
1
},
30303
,
30303
,
)
inst
:=
time
.
Now
()
db
,
_
:=
newNodeDB
(
""
,
Version
)
...
...
@@ -124,34 +126,40 @@ func TestNodeDBFetchStore(t *testing.T) {
}
if
stored
:=
db
.
node
(
node
.
ID
);
stored
==
nil
{
t
.
Errorf
(
"node: not found"
)
}
else
if
!
bytes
.
Equal
(
stored
.
ID
[
:
],
node
.
ID
[
:
])
||
!
stored
.
IP
.
Equal
(
node
.
IP
)
||
stored
.
TCPPort
!=
node
.
TCPPort
{
}
else
if
!
reflect
.
DeepEqual
(
stored
,
node
)
{
t
.
Errorf
(
"node: data mismatch: have %v, want %v"
,
stored
,
node
)
}
}
var
nodeDBSeedQueryNodes
=
[]
struct
{
node
Node
node
*
Node
pong
time
.
Time
}{
{
node
:
Node
{
ID
:
MustHexID
(
"0x01d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
IP
:
[]
byte
{
127
,
0
,
0
,
1
},
},
node
:
newNode
(
MustHexID
(
"0x01d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
net
.
IP
{
127
,
0
,
0
,
1
},
30303
,
30303
,
),
pong
:
time
.
Now
()
.
Add
(
-
2
*
time
.
Second
),
},
{
node
:
Node
{
ID
:
MustHexID
(
"0x02d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
IP
:
[]
byte
{
127
,
0
,
0
,
2
},
},
node
:
newNode
(
MustHexID
(
"0x02d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
net
.
IP
{
127
,
0
,
0
,
2
},
30303
,
30303
,
),
pong
:
time
.
Now
()
.
Add
(
-
3
*
time
.
Second
),
},
{
node
:
Node
{
ID
:
MustHexID
(
"0x03d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
IP
:
[]
byte
{
127
,
0
,
0
,
3
},
},
node
:
newNode
(
MustHexID
(
"0x03d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
net
.
IP
{
127
,
0
,
0
,
3
},
30303
,
30303
,
),
pong
:
time
.
Now
()
.
Add
(
-
1
*
time
.
Second
),
},
}
...
...
@@ -162,7 +170,7 @@ func TestNodeDBSeedQuery(t *testing.T) {
// Insert a batch of nodes for querying
for
i
,
seed
:=
range
nodeDBSeedQueryNodes
{
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
)
}
}
...
...
@@ -202,7 +210,7 @@ func TestNodeDBSeedQueryContinuation(t *testing.T) {
// Insert a batch of nodes for querying
for
i
,
seed
:=
range
nodeDBSeedQueryNodes
{
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
)
}
}
...
...
@@ -266,22 +274,26 @@ func TestNodeDBPersistency(t *testing.T) {
}
var
nodeDBExpirationNodes
=
[]
struct
{
node
Node
node
*
Node
pong
time
.
Time
exp
bool
}{
{
node
:
Node
{
ID
:
MustHexID
(
"0x01d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
IP
:
[]
byte
{
127
,
0
,
0
,
1
},
},
node
:
newNode
(
MustHexID
(
"0x01d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
net
.
IP
{
127
,
0
,
0
,
1
},
30303
,
30303
,
),
pong
:
time
.
Now
()
.
Add
(
-
nodeDBNodeExpiration
+
time
.
Minute
),
exp
:
false
,
},
{
node
:
Node
{
ID
:
MustHexID
(
"0x02d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
IP
:
[]
byte
{
127
,
0
,
0
,
2
},
},
node
:
newNode
(
MustHexID
(
"0x02d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
net
.
IP
{
127
,
0
,
0
,
2
},
30303
,
30303
,
),
pong
:
time
.
Now
()
.
Add
(
-
nodeDBNodeExpiration
-
time
.
Minute
),
exp
:
true
,
},
...
...
@@ -293,7 +305,7 @@ func TestNodeDBExpiration(t *testing.T) {
// 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
.
updateLastPong
(
seed
.
node
.
ID
,
seed
.
pong
);
err
!=
nil
{
...
...
p2p/discover/node.go
View file @
24d44f35
...
...
@@ -6,7 +6,6 @@ import (
"encoding/hex"
"errors"
"fmt"
"io"
"math/big"
"math/rand"
"net"
...
...
@@ -14,51 +13,55 @@ import (
"strconv"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/ethereum/go-ethereum/rlp"
)
const
nodeIDBits
=
512
// Node represents a host on the network.
type
Node
struct
{
ID
NodeID
IP
net
.
IP
IP
net
.
IP
// len 4 for IPv4 or 16 for IPv6
UDP
,
TCP
uint16
// port numbers
ID
NodeID
// the node's public key
DiscPort
int
// UDP listening port for discovery protocol
TCPPort
int
// TCP listening port for RLPx
// 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
}
func
newNode
(
id
NodeID
,
addr
*
net
.
UDPAddr
)
*
Node
{
func
newNode
(
id
NodeID
,
ip
net
.
IP
,
udpPort
,
tcpPort
uint16
)
*
Node
{
if
ipv4
:=
ip
.
To4
();
ipv4
!=
nil
{
ip
=
ipv4
}
return
&
Node
{
ID
:
id
,
IP
:
addr
.
IP
,
DiscPort
:
addr
.
Port
,
TCPPort
:
addr
.
Port
,
IP
:
ip
,
UDP
:
udpPort
,
TCP
:
tcpPort
,
ID
:
id
,
sha
:
crypto
.
Sha3Hash
(
id
[
:
]),
}
}
func
(
n
*
Node
)
isValid
()
bool
{
// TODO: don't accept localhost, LAN addresses from internet hosts
return
!
n
.
IP
.
IsMulticast
()
&&
!
n
.
IP
.
IsUnspecified
()
&&
n
.
TCPPort
!=
0
&&
n
.
DiscPort
!=
0
}
func
(
n
*
Node
)
addr
()
*
net
.
UDPAddr
{
return
&
net
.
UDPAddr
{
IP
:
n
.
IP
,
Port
:
n
.
DiscPort
}
return
&
net
.
UDPAddr
{
IP
:
n
.
IP
,
Port
:
int
(
n
.
UDP
)
}
}
// The string representation of a Node is a URL.
// Please see ParseNode for a description of the format.
func
(
n
*
Node
)
String
()
string
{
addr
:=
net
.
TCPAddr
{
IP
:
n
.
IP
,
Port
:
n
.
TCPPort
}
addr
:=
net
.
TCPAddr
{
IP
:
n
.
IP
,
Port
:
int
(
n
.
TCP
)
}
u
:=
url
.
URL
{
Scheme
:
"enode"
,
User
:
url
.
User
(
fmt
.
Sprintf
(
"%x"
,
n
.
ID
[
:
])),
Host
:
addr
.
String
(),
}
if
n
.
DiscPort
!=
n
.
TCPPort
{
u
.
RawQuery
=
"discport="
+
strconv
.
Itoa
(
n
.
DiscPort
)
if
n
.
UDP
!=
n
.
TCP
{
u
.
RawQuery
=
"discport="
+
strconv
.
Itoa
(
int
(
n
.
UDP
)
)
}
return
u
.
String
()
}
...
...
@@ -80,36 +83,47 @@ func (n *Node) String() string {
//
// enode://<hex node id>@10.3.58.6:30303?discport=30301
func
ParseNode
(
rawurl
string
)
(
*
Node
,
error
)
{
var
n
Node
var
(
id
NodeID
ip
net
.
IP
tcpPort
,
udpPort
uint64
)
u
,
err
:=
url
.
Parse
(
rawurl
)
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
n
.
ID
,
err
=
HexID
(
u
.
User
.
String
());
err
!=
nil
{
if
id
,
err
=
HexID
(
u
.
User
.
String
());
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"invalid node ID (%v)"
,
err
)
}
ip
,
port
,
err
:=
net
.
SplitHostPort
(
u
.
Host
)
// Parse the IP address.
host
,
port
,
err
:=
net
.
SplitHostPort
(
u
.
Host
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"invalid host: %v"
,
err
)
}
if
n
.
IP
=
net
.
ParseIP
(
ip
);
n
.
IP
==
nil
{
if
ip
=
net
.
ParseIP
(
host
);
ip
==
nil
{
return
nil
,
errors
.
New
(
"invalid IP address"
)
}
if
n
.
TCPPort
,
err
=
strconv
.
Atoi
(
port
);
err
!=
nil
{
// 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"
)
==
""
{
n
.
DiscPort
=
n
.
TCPPort
}
else
{
if
n
.
DiscPort
,
err
=
strconv
.
Atoi
(
qv
.
Get
(
"discport"
));
err
!=
nil
{
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
&
n
,
nil
return
newNode
(
id
,
ip
,
uint16
(
udpPort
),
uint16
(
tcpPort
))
,
nil
}
// MustParseNode parses a node URL. It panics if the URL is not valid.
...
...
@@ -121,22 +135,6 @@ func MustParseNode(rawurl string) *Node {
return
n
}
func
(
n
Node
)
EncodeRLP
(
w
io
.
Writer
)
error
{
return
rlp
.
Encode
(
w
,
rpcNode
{
IP
:
n
.
IP
.
String
(),
Port
:
uint16
(
n
.
TCPPort
),
ID
:
n
.
ID
})
}
func
(
n
*
Node
)
DecodeRLP
(
s
*
rlp
.
Stream
)
(
err
error
)
{
var
ext
rpcNode
if
err
=
s
.
Decode
(
&
ext
);
err
==
nil
{
n
.
TCPPort
=
int
(
ext
.
Port
)
n
.
DiscPort
=
int
(
ext
.
Port
)
n
.
ID
=
ext
.
ID
if
n
.
IP
=
net
.
ParseIP
(
ext
.
IP
);
n
.
IP
==
nil
{
return
errors
.
New
(
"invalid IP string"
)
}
}
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
...
...
@@ -221,7 +219,7 @@ func recoverNodeID(hash, sig []byte) (id NodeID, err error) {
// 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
NodeID
)
int
{
func
distcmp
(
target
,
a
,
b
common
.
Hash
)
int
{
for
i
:=
range
target
{
da
:=
a
[
i
]
^
target
[
i
]
db
:=
b
[
i
]
^
target
[
i
]
...
...
@@ -271,7 +269,7 @@ var lzcount = [256]int{
}
// logdist returns the logarithmic distance between a and b, log2(a ^ b).
func
logdist
(
a
,
b
NodeID
)
int
{
func
logdist
(
a
,
b
common
.
Hash
)
int
{
lz
:=
0
for
i
:=
range
a
{
x
:=
a
[
i
]
^
b
[
i
]
...
...
@@ -285,8 +283,8 @@ func logdist(a, b NodeID) int {
return
len
(
a
)
*
8
-
lz
}
//
randomID returns a random NodeID
such that logdist(a, b) == n
func
randomID
(
a
NodeID
,
n
int
)
(
b
NodeID
)
{
//
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
}
...
...
p2p/discover/node_test.go
View file @
24d44f35
...
...
@@ -9,6 +9,7 @@ import (
"testing/quick"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
...
...
@@ -48,46 +49,61 @@ var parseNodeTests = []struct {
},
{
rawurl
:
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150"
,
wantResult
:
&
Node
{
ID
:
MustHexID
(
"0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
IP
:
net
.
ParseIP
(
"127.0.0.1"
)
,
DiscPort
:
52150
,
TCPPort
:
52150
,
}
,
wantResult
:
newNode
(
MustHexID
(
"0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
net
.
IP
{
0x7f
,
0x0
,
0x0
,
0x1
}
,
52150
,
52150
,
)
,
},
{
rawurl
:
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150"
,
wantResult
:
&
Node
{
ID
:
MustHexID
(
"0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
IP
:
net
.
ParseIP
(
"::"
),
DiscPort
:
52150
,
TCPPort
:
52150
,
}
,
wantResult
:
newNode
(
MustHexID
(
"0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
net
.
ParseIP
(
"::"
),
52150
,
52150
,
)
,
},
{
rawurl
:
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150?discport=223344"
,
wantResult
:
&
Node
{
ID
:
MustHexID
(
"0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
IP
:
net
.
ParseIP
(
"127.0.0.1"
),
DiscPort
:
223344
,
TCPPort
:
52150
,
},
rawurl
:
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:52150"
,
wantResult
:
newNode
(
MustHexID
(
"0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
net
.
ParseIP
(
"2001:db8:3c4d:15::abcd:ef12"
),
52150
,
52150
,
),
},
{
rawurl
:
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150?discport=22334"
,
wantResult
:
newNode
(
MustHexID
(
"0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"
),
net
.
IP
{
0x7f
,
0x0
,
0x0
,
0x1
},
22334
,
52150
,
),
},
}
func
TestParseNode
(
t
*
testing
.
T
)
{
for
i
,
test
:=
range
parseNodeTests
{
n
,
err
:=
ParseNode
(
test
.
rawurl
)
if
err
==
nil
&&
test
.
wantError
!=
""
{
t
.
Errorf
(
"test %d: got nil error, expected %#q"
,
i
,
test
.
wantError
)
continue
}
if
err
!=
nil
&&
err
.
Error
()
!=
test
.
wantError
{
t
.
Errorf
(
"test %d: got error %#q, expected %#q"
,
i
,
err
.
Error
(),
test
.
wantError
)
continue
}
if
!
reflect
.
DeepEqual
(
n
,
test
.
wantResult
)
{
t
.
Errorf
(
"test %d: result mismatch:
\n
got: %#v, want: %#v"
,
i
,
n
,
test
.
wantResult
)
if
test
.
wantError
!=
""
{
if
err
==
nil
{
t
.
Errorf
(
"test %d: got nil error, expected %#q"
,
i
,
test
.
wantError
)
continue
}
else
if
err
.
Error
()
!=
test
.
wantError
{
t
.
Errorf
(
"test %d: got error %#q, expected %#q"
,
i
,
err
.
Error
(),
test
.
wantError
)
continue
}
}
else
{
if
err
!=
nil
{
t
.
Errorf
(
"test %d: unexpected error: %v"
,
i
,
err
)
continue
}
if
!
reflect
.
DeepEqual
(
n
,
test
.
wantResult
)
{
t
.
Errorf
(
"test %d: result mismatch:
\n
got: %#v, want: %#v"
,
i
,
n
,
test
.
wantResult
)
}
}
}
}
...
...
@@ -154,7 +170,7 @@ func TestNodeID_pubkeyBad(t *testing.T) {
}
func
TestNodeID_distcmp
(
t
*
testing
.
T
)
{
distcmpBig
:=
func
(
target
,
a
,
b
NodeID
)
int
{
distcmpBig
:=
func
(
target
,
a
,
b
common
.
Hash
)
int
{
tbig
:=
new
(
big
.
Int
)
.
SetBytes
(
target
[
:
])
abig
:=
new
(
big
.
Int
)
.
SetBytes
(
a
[
:
])
bbig
:=
new
(
big
.
Int
)
.
SetBytes
(
b
[
:
])
...
...
@@ -167,15 +183,15 @@ func TestNodeID_distcmp(t *testing.T) {
// the random tests is likely to miss the case where they're equal.
func
TestNodeID_distcmpEqual
(
t
*
testing
.
T
)
{
base
:=
NodeID
{
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
,
11
,
12
,
13
,
14
,
15
}
x
:=
NodeID
{
15
,
14
,
13
,
12
,
11
,
10
,
9
,
8
,
7
,
6
,
5
,
4
,
3
,
2
,
1
,
0
}
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"
)
}
}
func
TestNodeID_logdist
(
t
*
testing
.
T
)
{
logdistBig
:=
func
(
a
,
b
NodeID
)
int
{
logdistBig
:=
func
(
a
,
b
common
.
Hash
)
int
{
abig
,
bbig
:=
new
(
big
.
Int
)
.
SetBytes
(
a
[
:
]),
new
(
big
.
Int
)
.
SetBytes
(
b
[
:
])
return
new
(
big
.
Int
)
.
Xor
(
abig
,
bbig
)
.
BitLen
()
}
...
...
@@ -186,19 +202,19 @@ func TestNodeID_logdist(t *testing.T) {
// the random tests is likely to miss the case where they're equal.
func
TestNodeID_logdistEqual
(
t
*
testing
.
T
)
{
x
:=
NodeID
{
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
,
11
,
12
,
13
,
14
,
15
}
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"
)
}
}
func
TestNodeID_
randomID
(
t
*
testing
.
T
)
{
func
TestNodeID_
hashAtDistance
(
t
*
testing
.
T
)
{
// we don't use quick.Check here because its output isn't
// very helpful when the test fails.
for
i
:=
0
;
i
<
quickcfg
.
MaxCount
;
i
++
{
a
:=
gen
(
NodeID
{},
quickrand
)
.
(
NodeID
)
dist
:=
quickrand
.
Intn
(
len
(
NodeID
{})
*
8
)
result
:=
randomID
(
a
,
dist
)
a
:=
gen
(
common
.
Hash
{},
quickrand
)
.
(
common
.
Hash
)
dist
:=
quickrand
.
Intn
(
len
(
common
.
Hash
{})
*
8
)
result
:=
hashAtDistance
(
a
,
dist
)
actualdist
:=
logdist
(
result
,
a
)
if
dist
!=
actualdist
{
...
...
@@ -209,6 +225,9 @@ func TestNodeID_randomID(t *testing.T) {
}
}
// TODO: this 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
))
...
...
p2p/discover/table.go
View file @
24d44f35
...
...
@@ -7,19 +7,24 @@
package
discover
import
(
"crypto/rand"
"net"
"sort"
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
)
const
(
alpha
=
3
// Kademlia concurrency factor
bucketSize
=
16
// Kademlia bucket size
nBuckets
=
nodeIDBits
+
1
// Number of buckets
alpha
=
3
// Kademlia concurrency factor
bucketSize
=
16
// Kademlia bucket size
hashBits
=
len
(
common
.
Hash
{})
*
8
nBuckets
=
hashBits
+
1
// Number of buckets
maxBondingPingPongs
=
10
)
...
...
@@ -71,7 +76,7 @@ func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string
tab
:=
&
Table
{
net
:
t
,
db
:
db
,
self
:
newNode
(
ourID
,
ourAddr
),
self
:
newNode
(
ourID
,
ourAddr
.
IP
,
uint16
(
ourAddr
.
Port
),
uint16
(
ourAddr
.
Port
)
),
bonding
:
make
(
map
[
NodeID
]
*
bondproc
),
bondslots
:
make
(
chan
struct
{},
maxBondingPingPongs
),
}
...
...
@@ -105,6 +110,7 @@ func (tab *Table) Bootstrap(nodes []*Node) {
tab
.
nursery
=
make
([]
*
Node
,
0
,
len
(
nodes
))
for
_
,
n
:=
range
nodes
{
cpy
:=
*
n
cpy
.
sha
=
crypto
.
Sha3Hash
(
n
.
ID
[
:
])
tab
.
nursery
=
append
(
tab
.
nursery
,
&
cpy
)
}
tab
.
mutex
.
Unlock
()
...
...
@@ -114,21 +120,23 @@ func (tab *Table) Bootstrap(nodes []*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.
func
(
tab
*
Table
)
Lookup
(
target
NodeID
)
[]
*
Node
{
// The given target does not need to be an actual node
// identifier.
func
(
tab
*
Table
)
Lookup
(
targetID
NodeID
)
[]
*
Node
{
var
(
target
=
crypto
.
Sha3Hash
(
targetID
[
:
])
asked
=
make
(
map
[
NodeID
]
bool
)
seen
=
make
(
map
[
NodeID
]
bool
)
reply
=
make
(
chan
[]
*
Node
,
alpha
)
pendingQueries
=
0
)
// don't query further if we hit
the target or
ourself.
// don't query further if we hit ourself.
// unlikely to happen often in practice.
asked
[
target
]
=
true
asked
[
tab
.
self
.
ID
]
=
true
tab
.
mutex
.
Lock
()
// update last lookup stamp (for refresh logic)
tab
.
buckets
[
logdist
(
tab
.
self
.
ID
,
target
)]
.
lastLookup
=
time
.
Now
()
tab
.
buckets
[
logdist
(
tab
.
self
.
sha
,
target
)]
.
lastLookup
=
time
.
Now
()
// generate initial result set
result
:=
tab
.
closest
(
target
,
bucketSize
)
tab
.
mutex
.
Unlock
()
...
...
@@ -141,7 +149,7 @@ func (tab *Table) Lookup(target NodeID) []*Node {
asked
[
n
.
ID
]
=
true
pendingQueries
++
go
func
()
{
r
,
_
:=
tab
.
net
.
findnode
(
n
.
ID
,
n
.
addr
(),
target
)
r
,
_
:=
tab
.
net
.
findnode
(
n
.
ID
,
n
.
addr
(),
target
ID
)
reply
<-
tab
.
bondall
(
r
)
}()
}
...
...
@@ -164,17 +172,16 @@ func (tab *Table) Lookup(target NodeID) []*Node {
// refresh performs a lookup for a random target to keep buckets full.
func
(
tab
*
Table
)
refresh
()
{
ld
:=
-
1
// logdist of chosen bucket
tab
.
mutex
.
Lock
()
for
i
,
b
:=
range
tab
.
buckets
{
if
i
>
0
&&
b
.
lastLookup
.
Before
(
time
.
Now
()
.
Add
(
-
1
*
time
.
Hour
))
{
ld
=
i
break
}
}
tab
.
mutex
.
Unlock
()
result
:=
tab
.
Lookup
(
randomID
(
tab
.
self
.
ID
,
ld
))
// The Kademlia paper specifies that the bucket refresh should
// perform a refresh in the least recently used bucket. We cannot
// adhere to this because the findnode target is a 512bit value
// (not hash-sized) and it is not easily possible to generate a
// sha3 preimage that falls into a chosen bucket.
//
// We perform a lookup with a random target instead.
var
target
NodeID
rand
.
Read
(
target
[
:
])
result
:=
tab
.
Lookup
(
target
)
if
len
(
result
)
==
0
{
// Pick a batch of previously know seeds to lookup with
seeds
:=
tab
.
db
.
querySeeds
(
10
)
...
...
@@ -194,7 +201,7 @@ func (tab *Table) refresh() {
// 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
NodeID
,
nresults
int
)
*
nodesByDistance
{
func
(
tab
*
Table
)
closest
(
target
common
.
Hash
,
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.
...
...
@@ -220,7 +227,7 @@ func (tab *Table) bondall(nodes []*Node) (result []*Node) {
rc
:=
make
(
chan
*
Node
,
len
(
nodes
))
for
i
:=
range
nodes
{
go
func
(
n
*
Node
)
{
nn
,
_
:=
tab
.
bond
(
false
,
n
.
ID
,
n
.
addr
(),
uint16
(
n
.
TCP
Port
))
nn
,
_
:=
tab
.
bond
(
false
,
n
.
ID
,
n
.
addr
(),
uint16
(
n
.
TCP
))
rc
<-
nn
}(
nodes
[
i
])
}
...
...
@@ -276,7 +283,8 @@ func (tab *Table) bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16
}
tab
.
mutex
.
Lock
()
defer
tab
.
mutex
.
Unlock
()
if
b
:=
tab
.
buckets
[
logdist
(
tab
.
self
.
ID
,
n
.
ID
)];
!
b
.
bump
(
n
)
{
b
:=
tab
.
buckets
[
logdist
(
tab
.
self
.
sha
,
n
.
sha
)]
if
!
b
.
bump
(
n
)
{
tab
.
pingreplace
(
n
,
b
)
}
return
n
,
nil
...
...
@@ -299,12 +307,7 @@ func (tab *Table) pingpong(w *bondproc, pinged bool, id NodeID, addr *net.UDPAdd
tab
.
net
.
waitping
(
id
)
}
// Bonding succeeded, update the node database
w
.
n
=
&
Node
{
ID
:
id
,
IP
:
addr
.
IP
,
DiscPort
:
addr
.
Port
,
TCPPort
:
int
(
tcpPort
),
}
w
.
n
=
newNode
(
id
,
addr
.
IP
,
uint16
(
addr
.
Port
),
tcpPort
)
tab
.
db
.
updateNode
(
w
.
n
)
close
(
w
.
done
)
}
...
...
@@ -345,12 +348,11 @@ func (tab *Table) ping(id NodeID, addr *net.UDPAddr) error {
func
(
tab
*
Table
)
add
(
entries
[]
*
Node
)
{
outer
:
for
_
,
n
:=
range
entries
{
if
n
==
nil
||
n
.
ID
==
tab
.
self
.
ID
{
// skip bad entries. The RLP decoder returns nil for empty
// input lists.
if
n
.
ID
==
tab
.
self
.
ID
{
// don't add self.
continue
}
bucket
:=
tab
.
buckets
[
logdist
(
tab
.
self
.
ID
,
n
.
ID
)]
bucket
:=
tab
.
buckets
[
logdist
(
tab
.
self
.
sha
,
n
.
sha
)]
for
i
:=
range
bucket
.
entries
{
if
bucket
.
entries
[
i
]
.
ID
==
n
.
ID
{
// already in bucket
...
...
@@ -379,13 +381,13 @@ func (b *bucket) bump(n *Node) bool {
// distance to target.
type
nodesByDistance
struct
{
entries
[]
*
Node
target
NodeID
target
common
.
Hash
}
// push adds the given node to the list, keeping the total size below maxElems.
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
]
.
ID
,
n
.
ID
)
>
0
return
distcmp
(
h
.
target
,
h
.
entries
[
i
]
.
sha
,
n
.
sha
)
>
0
})
if
len
(
h
.
entries
)
<
maxElems
{
h
.
entries
=
append
(
h
.
entries
,
n
)
...
...
p2p/discover/table_test.go
View file @
24d44f35
This diff is collapsed.
Click to expand it.
p2p/discover/udp.go
View file @
24d44f35
...
...
@@ -15,7 +15,7 @@ import (
"github.com/ethereum/go-ethereum/rlp"
)
const
Version
=
3
const
Version
=
4
// Errors
var
(
...
...
@@ -49,36 +49,67 @@ const (
// RPC request structures
type
(
ping
struct
{
Version
uint
// must match Version
IP
string
// our IP
Port
uint16
// our port
Version
uint
From
,
To
rpcEndpoint
Expiration
uint64
}
//
reply to Ping
//
pong is the reply to ping.
pong
struct
{
ReplyTok
[]
byte
Expiration
uint64
// This field should mirror the UDP envelope address
// of the ping packet, which provides a way to discover the
// the external address (after NAT).
To
rpcEndpoint
ReplyTok
[]
byte
// This contains the hash of the ping packet.
Expiration
uint64
// Absolute timestamp at which the packet becomes invalid.
}
// findnode is a query for nodes close to the given target.
findnode
struct
{
// Id to look up. The responding node will send back nodes
// closest to the target.
Target
NodeID
Target
NodeID
// doesn't need to be an actual public key
Expiration
uint64
}
// reply to findnode
neighbors
struct
{
Nodes
[]
*
Node
Nodes
[]
rpc
Node
Expiration
uint64
}
rpcNode
struct
{
IP
net
.
IP
// len 4 for IPv4 or 16 for IPv6
UDP
uint16
// for discovery protocol
TCP
uint16
// for RLPx protocol
ID
NodeID
}
rpcEndpoint
struct
{
IP
net
.
IP
// len 4 for IPv4 or 16 for IPv6
UDP
uint16
// for discovery protocol
TCP
uint16
// for RLPx protocol
}
)
type
rpcNode
struct
{
IP
string
Port
uint16
ID
NodeID
func
makeEndpoint
(
addr
*
net
.
UDPAddr
,
tcpPort
uint16
)
rpcEndpoint
{
ip
:=
addr
.
IP
.
To4
()
if
ip
==
nil
{
ip
=
addr
.
IP
.
To16
()
}
return
rpcEndpoint
{
IP
:
ip
,
UDP
:
uint16
(
addr
.
Port
),
TCP
:
tcpPort
}
}
func
nodeFromRPC
(
rn
rpcNode
)
(
n
*
Node
,
valid
bool
)
{
// TODO: don't accept localhost, LAN addresses from internet hosts
// TODO: check public key is on secp256k1 curve
if
rn
.
IP
.
IsMulticast
()
||
rn
.
IP
.
IsUnspecified
()
||
rn
.
UDP
==
0
{
return
nil
,
false
}
return
newNode
(
rn
.
ID
,
rn
.
IP
,
rn
.
UDP
,
rn
.
TCP
),
true
}
func
nodeToRPC
(
n
*
Node
)
rpcNode
{
return
rpcNode
{
ID
:
n
.
ID
,
IP
:
n
.
IP
,
UDP
:
n
.
UDP
,
TCP
:
n
.
TCP
}
}
type
packet
interface
{
...
...
@@ -94,8 +125,9 @@ type conn interface {
// udp implements the RPC protocol.
type
udp
struct
{
conn
conn
priv
*
ecdsa
.
PrivateKey
conn
conn
priv
*
ecdsa
.
PrivateKey
ourEndpoint
rpcEndpoint
addpending
chan
*
pending
gotreply
chan
reply
...
...
@@ -176,6 +208,8 @@ func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface, nodeDBPath strin
realaddr
=
&
net
.
UDPAddr
{
IP
:
ext
,
Port
:
realaddr
.
Port
}
}
}
// TODO: separate TCP port
udp
.
ourEndpoint
=
makeEndpoint
(
realaddr
,
uint16
(
realaddr
.
Port
))
udp
.
Table
=
newTable
(
udp
,
PubkeyID
(
&
priv
.
PublicKey
),
realaddr
,
nodeDBPath
)
go
udp
.
loop
()
go
udp
.
readLoop
()
...
...
@@ -194,8 +228,8 @@ func (t *udp) ping(toid NodeID, toaddr *net.UDPAddr) error {
errc
:=
t
.
pending
(
toid
,
pongPacket
,
func
(
interface
{})
bool
{
return
true
})
t
.
send
(
toaddr
,
pingPacket
,
ping
{
Version
:
Version
,
IP
:
t
.
self
.
IP
.
String
()
,
Port
:
uint16
(
t
.
self
.
TCPPort
),
From
:
t
.
ourEndpoint
,
To
:
makeEndpoint
(
toaddr
,
0
),
// TODO: maybe use known TCP port from DB
Expiration
:
uint64
(
time
.
Now
()
.
Add
(
expiration
)
.
Unix
()),
})
return
<-
errc
...
...
@@ -212,9 +246,9 @@ func (t *udp) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node
nreceived
:=
0
errc
:=
t
.
pending
(
toid
,
neighborsPacket
,
func
(
r
interface
{})
bool
{
reply
:=
r
.
(
*
neighbors
)
for
_
,
n
:=
range
reply
.
Nodes
{
for
_
,
r
n
:=
range
reply
.
Nodes
{
nreceived
++
if
n
.
isValid
()
{
if
n
,
valid
:=
nodeFromRPC
(
rn
);
valid
{
nodes
=
append
(
nodes
,
n
)
}
}
...
...
@@ -374,19 +408,24 @@ func (t *udp) readLoop() {
if
err
!=
nil
{
return
}
packet
,
fromID
,
hash
,
err
:=
decodePacket
(
buf
[
:
nbytes
])
if
err
!=
nil
{
glog
.
V
(
logger
.
Debug
)
.
Infof
(
"Bad packet from %v: %v
\n
"
,
from
,
err
)
continue
}
status
:=
"ok"
if
err
:=
packet
.
handle
(
t
,
from
,
fromID
,
hash
);
err
!=
nil
{
status
=
err
.
Error
()
}
glog
.
V
(
logger
.
Detail
)
.
Infof
(
"<<< %v %T: %s
\n
"
,
from
,
packet
,
status
)
t
.
handlePacket
(
from
,
buf
[
:
nbytes
])
}
}
func
(
t
*
udp
)
handlePacket
(
from
*
net
.
UDPAddr
,
buf
[]
byte
)
error
{
packet
,
fromID
,
hash
,
err
:=
decodePacket
(
buf
)
if
err
!=
nil
{
glog
.
V
(
logger
.
Debug
)
.
Infof
(
"Bad packet from %v: %v
\n
"
,
from
,
err
)
return
err
}
status
:=
"ok"
if
err
=
packet
.
handle
(
t
,
from
,
fromID
,
hash
);
err
!=
nil
{
status
=
err
.
Error
()
}
glog
.
V
(
logger
.
Detail
)
.
Infof
(
"<<< %v %T: %s
\n
"
,
from
,
packet
,
status
)
return
err
}
func
decodePacket
(
buf
[]
byte
)
(
packet
,
NodeID
,
[]
byte
,
error
)
{
if
len
(
buf
)
<
headSize
+
1
{
return
nil
,
NodeID
{},
nil
,
errPacketTooSmall
...
...
@@ -425,12 +464,13 @@ func (req *ping) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) er
return
errBadVersion
}
t
.
send
(
from
,
pongPacket
,
pong
{
To
:
makeEndpoint
(
from
,
req
.
From
.
TCP
),
ReplyTok
:
mac
,
Expiration
:
uint64
(
time
.
Now
()
.
Add
(
expiration
)
.
Unix
()),
})
if
!
t
.
handleReply
(
fromID
,
pingPacket
,
req
)
{
// Note: we're ignoring the provided IP address right now
go
t
.
bond
(
true
,
fromID
,
from
,
req
.
Port
)
go
t
.
bond
(
true
,
fromID
,
from
,
req
.
From
.
TCP
)
}
return
nil
}
...
...
@@ -459,12 +499,18 @@ func (req *findnode) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte
// (which is a much bigger packet than findnode) to the victim.
return
errUnknownNode
}
target
:=
crypto
.
Sha3Hash
(
req
.
Target
[
:
])
t
.
mutex
.
Lock
()
closest
:=
t
.
closest
(
req
.
T
arget
,
bucketSize
)
.
entries
closest
:=
t
.
closest
(
t
arget
,
bucketSize
)
.
entries
t
.
mutex
.
Unlock
()
// TODO: this conversion could use a cached version of the slice
closestrpc
:=
make
([]
rpcNode
,
len
(
closest
))
for
i
,
n
:=
range
closest
{
closestrpc
[
i
]
=
nodeToRPC
(
n
)
}
t
.
send
(
from
,
neighborsPacket
,
neighbors
{
Nodes
:
closest
,
Nodes
:
closest
rpc
,
Expiration
:
uint64
(
time
.
Now
()
.
Add
(
expiration
)
.
Unix
()),
})
return
nil
...
...
p2p/discover/udp_test.go
View file @
24d44f35
...
...
@@ -16,6 +16,7 @@ import (
"testing"
"time"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
)
...
...
@@ -23,6 +24,15 @@ func init() {
logger
.
AddLogSystem
(
logger
.
NewStdLogSystem
(
os
.
Stdout
,
logpkg
.
LstdFlags
,
logger
.
ErrorLevel
))
}
// 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
}
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
}
)
type
udpTest
struct
{
t
*
testing
.
T
pipe
*
dgramPipe
...
...
@@ -52,8 +62,7 @@ func (test *udpTest) packetIn(wantError error, ptype byte, data packet) error {
return
test
.
errorf
(
"packet (%d) encode error: %v"
,
err
)
}
test
.
sent
=
append
(
test
.
sent
,
enc
)
err
=
data
.
handle
(
test
.
udp
,
test
.
remoteaddr
,
PubkeyID
(
&
test
.
remotekey
.
PublicKey
),
enc
[
:
macSize
])
if
err
!=
wantError
{
if
err
=
test
.
udp
.
handlePacket
(
test
.
remoteaddr
,
enc
);
err
!=
wantError
{
return
test
.
errorf
(
"error mismatch: got %q, want %q"
,
err
,
wantError
)
}
return
nil
...
...
@@ -90,18 +99,12 @@ func (test *udpTest) errorf(format string, args ...interface{}) error {
return
err
}
// shared test variables
var
(
futureExp
=
uint64
(
time
.
Now
()
.
Add
(
10
*
time
.
Hour
)
.
Unix
())
testTarget
=
MustHexID
(
"01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101"
)
)
func
TestUDP_packetErrors
(
t
*
testing
.
T
)
{
test
:=
newUDPTest
(
t
)
defer
test
.
table
.
Close
()
test
.
packetIn
(
errExpired
,
pingPacket
,
&
ping
{
IP
:
"foo"
,
Port
:
99
,
Version
:
Version
})
test
.
packetIn
(
errBadVersion
,
pingPacket
,
&
ping
{
IP
:
"foo"
,
Port
:
99
,
Version
:
99
,
Expiration
:
futureExp
})
test
.
packetIn
(
errExpired
,
pingPacket
,
&
ping
{
From
:
testRemote
,
To
:
testLocalAnnounced
,
Version
:
Version
})
test
.
packetIn
(
errBadVersion
,
pingPacket
,
&
ping
{
From
:
testRemote
,
To
:
testLocalAnnounced
,
Version
:
99
,
Expiration
:
futureExp
})
test
.
packetIn
(
errUnsolicitedReply
,
pongPacket
,
&
pong
{
ReplyTok
:
[]
byte
{},
Expiration
:
futureExp
})
test
.
packetIn
(
errUnknownNode
,
findnodePacket
,
&
findnode
{
Expiration
:
futureExp
})
test
.
packetIn
(
errUnsolicitedReply
,
neighborsPacket
,
&
neighbors
{
Expiration
:
futureExp
})
...
...
@@ -143,30 +146,25 @@ func TestUDP_findnode(t *testing.T) {
// put a few nodes into the table. their exact
// distribution shouldn't matter much, altough we need to
// take care not to overflow any bucket.
target
:=
testTarget
nodes
:=
&
nodesByDistance
{
target
:
target
}
target
Hash
:=
crypto
.
Sha3Hash
(
testTarget
[
:
])
nodes
:=
&
nodesByDistance
{
target
:
target
Hash
}
for
i
:=
0
;
i
<
bucketSize
;
i
++
{
nodes
.
push
(
&
Node
{
IP
:
net
.
IP
{
1
,
2
,
3
,
byte
(
i
)},
DiscPort
:
i
+
2
,
TCPPort
:
i
+
2
,
ID
:
randomID
(
test
.
table
.
self
.
ID
,
i
+
2
),
},
bucketSize
)
nodes
.
push
(
nodeAtDistance
(
test
.
table
.
self
.
sha
,
i
+
2
),
bucketSize
)
}
test
.
table
.
add
(
nodes
.
entries
)
// ensure there's a bond with the test node,
// findnode won't be accepted otherwise.
test
.
table
.
db
.
updateNode
(
&
Node
{
ID
:
PubkeyID
(
&
test
.
remotekey
.
PublicKey
),
IP
:
test
.
remoteaddr
.
IP
,
DiscPort
:
test
.
remoteaddr
.
Port
,
TCPPort
:
99
,
}
)
test
.
table
.
db
.
updateNode
(
newNode
(
PubkeyID
(
&
test
.
remotekey
.
PublicKey
),
test
.
remoteaddr
.
IP
,
uint16
(
test
.
remoteaddr
.
Port
)
,
99
,
)
)
// check that closest neighbors are returned.
test
.
packetIn
(
nil
,
findnodePacket
,
&
findnode
{
Target
:
testTarget
,
Expiration
:
futureExp
})
test
.
waitPacketOut
(
func
(
p
*
neighbors
)
{
expected
:=
test
.
table
.
closest
(
t
estTarget
,
bucketSize
)
expected
:=
test
.
table
.
closest
(
t
argetHash
,
bucketSize
)
if
len
(
p
.
Nodes
)
!=
bucketSize
{
t
.
Errorf
(
"wrong number of results: got %d, want %d"
,
len
(
p
.
Nodes
),
bucketSize
)
}
...
...
@@ -204,13 +202,17 @@ func TestUDP_findnodeMultiReply(t *testing.T) {
// send the reply as two packets.
list
:=
[]
*
Node
{
MustParseNode
(
"enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303"
),
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"
),
MustParseNode
(
"enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301
?discport=17
"
),
MustParseNode
(
"enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303"
),
}
test
.
packetIn
(
nil
,
neighborsPacket
,
&
neighbors
{
Expiration
:
futureExp
,
Nodes
:
list
[
:
2
]})
test
.
packetIn
(
nil
,
neighborsPacket
,
&
neighbors
{
Expiration
:
futureExp
,
Nodes
:
list
[
2
:
]})
rpclist
:=
make
([]
rpcNode
,
len
(
list
))
for
i
:=
range
list
{
rpclist
[
i
]
=
nodeToRPC
(
list
[
i
])
}
test
.
packetIn
(
nil
,
neighborsPacket
,
&
neighbors
{
Expiration
:
futureExp
,
Nodes
:
rpclist
[
:
2
]})
test
.
packetIn
(
nil
,
neighborsPacket
,
&
neighbors
{
Expiration
:
futureExp
,
Nodes
:
rpclist
[
2
:
]})
// check that the sent neighbors are all returned by findnode
select
{
...
...
@@ -231,7 +233,8 @@ func TestUDP_successfulPing(t *testing.T) {
done
:=
make
(
chan
struct
{})
go
func
()
{
test
.
packetIn
(
nil
,
pingPacket
,
&
ping
{
IP
:
"foo"
,
Port
:
99
,
Version
:
Version
,
Expiration
:
futureExp
})
// The remote side sends a ping packet to initiate the exchange.
test
.
packetIn
(
nil
,
pingPacket
,
&
ping
{
From
:
testRemote
,
To
:
testLocalAnnounced
,
Version
:
Version
,
Expiration
:
futureExp
})
close
(
done
)
}()
...
...
@@ -239,12 +242,34 @@ func TestUDP_successfulPing(t *testing.T) {
test
.
waitPacketOut
(
func
(
p
*
pong
)
{
pinghash
:=
test
.
sent
[
0
][
:
macSize
]
if
!
bytes
.
Equal
(
p
.
ReplyTok
,
pinghash
)
{
t
.
Errorf
(
"got ReplyTok %x, want %x"
,
p
.
ReplyTok
,
pinghash
)
t
.
Errorf
(
"got pong.ReplyTok %x, want %x"
,
p
.
ReplyTok
,
pinghash
)
}
wantTo
:=
rpcEndpoint
{
// The mirrored UDP address is the UDP packet sender
IP
:
test
.
remoteaddr
.
IP
,
UDP
:
uint16
(
test
.
remoteaddr
.
Port
),
// The mirrored TCP port is the one from the ping packet
TCP
:
testRemote
.
TCP
,
}
if
!
reflect
.
DeepEqual
(
p
.
To
,
wantTo
)
{
t
.
Errorf
(
"got pong.To %v, want %v"
,
p
.
To
,
wantTo
)
}
})
// remote is unknown, the table pings back.
test
.
waitPacketOut
(
func
(
p
*
ping
)
error
{
return
nil
})
test
.
waitPacketOut
(
func
(
p
*
ping
)
error
{
if
!
reflect
.
DeepEqual
(
p
.
From
,
test
.
udp
.
ourEndpoint
)
{
t
.
Errorf
(
"got ping.From %v, want %v"
,
p
.
From
,
test
.
udp
.
ourEndpoint
)
}
wantTo
:=
rpcEndpoint
{
// The mirrored UDP address is the UDP packet sender.
IP
:
test
.
remoteaddr
.
IP
,
UDP
:
uint16
(
test
.
remoteaddr
.
Port
),
TCP
:
0
,
}
if
!
reflect
.
DeepEqual
(
p
.
To
,
wantTo
)
{
t
.
Errorf
(
"got ping.To %v, want %v"
,
p
.
To
,
wantTo
)
}
return
nil
})
test
.
packetIn
(
nil
,
pongPacket
,
&
pong
{
Expiration
:
futureExp
})
// ping should return shortly after getting the pong packet.
...
...
@@ -259,11 +284,11 @@ func TestUDP_successfulPing(t *testing.T) {
if
!
bytes
.
Equal
(
rnode
.
IP
,
test
.
remoteaddr
.
IP
)
{
t
.
Errorf
(
"node has wrong IP: got %v, want: %v"
,
rnode
.
IP
,
test
.
remoteaddr
.
IP
)
}
if
rnode
.
DiscPort
!=
test
.
remoteaddr
.
Port
{
t
.
Errorf
(
"node has wrong
Port: got %v, want: %v"
,
rnode
.
DiscPort
,
test
.
remoteaddr
.
Port
)
if
int
(
rnode
.
UDP
)
!=
test
.
remoteaddr
.
Port
{
t
.
Errorf
(
"node has wrong
UDP port: got %v, want: %v"
,
rnode
.
UDP
,
test
.
remoteaddr
.
Port
)
}
if
rnode
.
TCP
Port
!=
99
{
t
.
Errorf
(
"node has wrong
Port: got %v, want: %v"
,
rnode
.
TCPPort
,
99
)
if
rnode
.
TCP
!=
testRemote
.
TCP
{
t
.
Errorf
(
"node has wrong
TCP port: got %v, want: %v"
,
rnode
.
TCP
,
testRemote
.
TCP
)
}
}
...
...
@@ -327,7 +352,7 @@ func (c *dgramPipe) Close() error {
}
func
(
c
*
dgramPipe
)
LocalAddr
()
net
.
Addr
{
return
&
net
.
UDPAddr
{}
return
&
net
.
UDPAddr
{
IP
:
testLocal
.
IP
,
Port
:
int
(
testLocal
.
UDP
)
}
}
func
(
c
*
dgramPipe
)
waitPacketOut
()
[]
byte
{
...
...
p2p/handshake_test.go
View file @
24d44f35
...
...
@@ -119,14 +119,14 @@ func TestSetupConn(t *testing.T) {
prv0
,
_
:=
crypto
.
GenerateKey
()
prv1
,
_
:=
crypto
.
GenerateKey
()
node0
:=
&
discover
.
Node
{
ID
:
discover
.
PubkeyID
(
&
prv0
.
PublicKey
),
IP
:
net
.
IP
{
1
,
2
,
3
,
4
},
TCP
Port
:
33
,
ID
:
discover
.
PubkeyID
(
&
prv0
.
PublicKey
),
IP
:
net
.
IP
{
1
,
2
,
3
,
4
},
TCP
:
33
,
}
node1
:=
&
discover
.
Node
{
ID
:
discover
.
PubkeyID
(
&
prv1
.
PublicKey
),
IP
:
net
.
IP
{
5
,
6
,
7
,
8
},
TCP
Port
:
44
,
ID
:
discover
.
PubkeyID
(
&
prv1
.
PublicKey
),
IP
:
net
.
IP
{
5
,
6
,
7
,
8
},
TCP
:
44
,
}
hs0
:=
&
protoHandshake
{
Version
:
baseProtocolVersion
,
...
...
p2p/peer.go
View file @
24d44f35
...
...
@@ -15,7 +15,7 @@ import (
)
const
(
baseProtocolVersion
=
3
baseProtocolVersion
=
4
baseProtocolLength
=
uint64
(
16
)
baseProtocolMaxMsgSize
=
10
*
1024
*
1024
...
...
p2p/server.go
View file @
24d44f35
...
...
@@ -394,7 +394,7 @@ func (srv *Server) dialLoop() {
}
func
(
srv
*
Server
)
dialNode
(
dest
*
discover
.
Node
)
{
addr
:=
&
net
.
TCPAddr
{
IP
:
dest
.
IP
,
Port
:
dest
.
TCPPort
}
addr
:=
&
net
.
TCPAddr
{
IP
:
dest
.
IP
,
Port
:
int
(
dest
.
TCP
)
}
glog
.
V
(
logger
.
Debug
)
.
Infof
(
"Dialing %v
\n
"
,
dest
)
conn
,
err
:=
srv
.
Dialer
.
Dial
(
"tcp"
,
addr
.
String
())
if
err
!=
nil
{
...
...
p2p/server_test.go
View file @
24d44f35
...
...
@@ -102,7 +102,7 @@ func TestServerDial(t *testing.T) {
// tell the server to connect
tcpAddr
:=
listener
.
Addr
()
.
(
*
net
.
TCPAddr
)
srv
.
SuggestPeer
(
&
discover
.
Node
{
IP
:
tcpAddr
.
IP
,
TCP
Port
:
tcpAddr
.
Port
})
srv
.
SuggestPeer
(
&
discover
.
Node
{
IP
:
tcpAddr
.
IP
,
TCP
:
uint16
(
tcpAddr
.
Port
)
})
select
{
case
conn
:=
<-
accepted
:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment