Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
p2p
go-p2p-kbucket
Commits
776df761
Commit
776df761
authored
Aug 11, 2019
by
Aarsh Shah
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fixed the generation of random peer ID's
parent
b797ed11
Changes
6
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
4221 additions
and
4138 deletions
+4221
-4138
bucket_prefixmap.go
bucket_prefixmap.go
+4097
-4097
generate/main.go
generate/main.go
+3
-2
table.go
table.go
+51
-20
table_test.go
table_test.go
+20
-18
util.go
util.go
+17
-1
util_test.go
util_test.go
+33
-0
No files found.
bucket_prefixmap.go
View file @
776df761
This diff is collapsed.
Click to expand it.
generate/main.go
View file @
776df761
...
...
@@ -28,7 +28,8 @@ func main() {
hasher
:=
sha256
.
New
()
for
i
:=
uint64
(
0
);
count
<
target
;
i
++
{
binary
.
BigEndian
.
PutUint64
(
inp
[
idLen
-
8
:
],
i
)
binary
.
BigEndian
.
PutUint64
(
inp
[
2
:
],
i
)
hasher
.
Write
(
inp
[
:
])
out
=
hasher
.
Sum
(
out
[
:
0
])
hasher
.
Reset
()
...
...
@@ -55,7 +56,7 @@ func main() {
printf
(
"package %s
\n\n
"
,
pkg
)
printf
(
"// Code generated by generate/generate_map.go DO NOT EDIT
\n
"
)
printf
(
"var keyPrefixMap = [...]uint
32
{"
)
printf
(
"var keyPrefixMap = [...]uint
64
{"
)
for
i
,
j
:=
range
ids
[
:
]
{
if
i
%
16
==
0
{
printf
(
"
\n\t
"
)
...
...
table.go
View file @
776df761
...
...
@@ -2,9 +2,9 @@
package
kbucket
import
(
"encoding/binary"
"errors"
"fmt"
"io"
"math/rand"
"sync"
"time"
...
...
@@ -20,7 +20,6 @@ var log = logging.Logger("table")
var
ErrPeerRejectedHighLatency
=
errors
.
New
(
"peer rejected; latency too high"
)
var
ErrPeerRejectedNoCapacity
=
errors
.
New
(
"peer rejected; insufficient capacity"
)
var
ErrGenRandPeerIDFailed
=
errors
.
New
(
"failed to generate random peerID in bucket: exhausted attempts"
)
// RoutingTable defines the routing table.
type
RoutingTable
struct
{
...
...
@@ -60,13 +59,19 @@ func NewRoutingTable(bucketsize int, localID ID, latency time.Duration, m peerst
return
rt
}
// GetAllBuckets is safe to call as rt.Buckets is append-only
// caller SHOULD NOT modify the returned slice
func
(
rt
*
RoutingTable
)
GetAllBuckets
()
[]
*
Bucket
{
rt
.
tabLock
.
RLock
()
defer
rt
.
tabLock
.
RUnlock
()
return
rt
.
Buckets
}
// GenRandPeerID generates a random peerID in bucket=bucketID
func
(
rt
*
RoutingTable
)
GenRandPeerID
(
bucketID
int
)
(
peer
.
ID
,
error
)
{
if
bucketID
<
0
{
return
""
,
errors
.
New
(
"bucketID must be non-negative"
)
}
rt
.
tabLock
.
RLock
()
bucketLen
:=
len
(
rt
.
Buckets
)
rt
.
tabLock
.
RUnlock
()
...
...
@@ -78,28 +83,54 @@ func (rt *RoutingTable) GenRandPeerID(bucketID int) (peer.ID, error) {
targetCpl
=
bucketID
}
// should give up after a fixed number of attempts so we don't take up too much time/cpu
for
i
:=
0
;
i
<
1000
;
i
++
{
peerID
,
err
:=
randPeerID
()
if
err
!=
nil
{
log
.
Debugf
(
"failed to generate random peerID in bucket %d, error is %+v"
,
bucketID
,
err
)
continue
// generate random 16 bits
r
:=
rand
.
New
(
rand
.
NewSource
(
time
.
Now
()
.
UnixNano
()))
buf
:=
make
([]
byte
,
2
)
_
,
err
:=
r
.
Read
(
buf
)
if
err
!=
nil
{
return
""
,
err
}
// replace the first targetCPL bits with those from the hashed local peer ID & toggle the (targetCpl+1)th bit
// so that exactly targetCpl bits match
numBytes
:=
targetCpl
/
8
// number of bytes we need to replace
numBits
:=
targetCpl
%
8
// number of bits we need to replace after numBytes have been replaced
// replace the bytes
byteIndex
:=
0
for
;
byteIndex
<
numBytes
;
byteIndex
++
{
buf
[
byteIndex
]
=
rt
.
local
[
byteIndex
]
}
// replace the bits
if
byteIndex
<
len
(
buf
)
{
dstByte
:=
buf
[
byteIndex
]
srcByte
:=
rt
.
local
[
byteIndex
]
j
:=
uint
(
7
)
for
k
:=
1
;
k
<=
numBits
;
k
++
{
if
isSet
(
srcByte
,
j
)
{
dstByte
=
setBit
(
dstByte
,
j
)
}
else
{
dstByte
=
clearBit
(
dstByte
,
j
)
}
j
--
}
if
CommonPrefixLen
(
ConvertPeerID
(
peerID
),
rt
.
local
)
==
targetCpl
{
return
peerID
,
err
// toggle the next bit
if
isSet
(
srcByte
,
j
)
{
dstByte
=
clearBit
(
dstByte
,
j
)
}
else
{
dstByte
=
setBit
(
dstByte
,
j
)
}
buf
[
byteIndex
]
=
dstByte
}
return
""
,
ErrGenRandPeerIDFailed
}
func
randPeerID
()
(
peer
.
ID
,
error
)
{
r
:=
rand
.
New
(
rand
.
NewSource
(
time
.
Now
()
.
UnixNano
()))
buf
:=
make
([]
byte
,
16
)
if
_
,
err
:=
io
.
ReadFull
(
r
,
buf
);
err
!=
nil
{
return
""
,
err
}
h
,
_
:=
mh
.
Sum
(
buf
,
mh
.
SHA2_256
,
-
1
)
return
peer
.
ID
(
h
),
nil
// get the seed using buf & use it as the hash digest for a SHA2-256 Multihash to get the desired peer ID
prefix
:=
binary
.
BigEndian
.
Uint16
(
buf
)
key
:=
keyPrefixMap
[
prefix
]
h
:=
[
34
]
byte
{
mh
.
SHA2_256
,
32
}
binary
.
BigEndian
.
PutUint64
(
h
[
2
:
],
key
)
return
peer
.
ID
(
h
[
:
]),
err
}
// Update adds or moves the given peer to the front of its respective bucket
...
...
table_test.go
View file @
776df761
...
...
@@ -49,12 +49,13 @@ func TestBucket(t *testing.T) {
}
func
TestGenRandPeerID
(
t
*
testing
.
T
)
{
nBuckets
:=
16
local
:=
test
.
RandPeerIDFatal
(
t
)
m
:=
pstore
.
NewMetrics
()
rt
:=
NewRoutingTable
(
1
,
ConvertPeerID
(
local
),
time
.
Hour
,
m
)
// create
3 b
uckets
for
i
:=
0
;
i
<
5
;
i
++
{
// create
nB
uckets
for
i
:=
0
;
i
<
nBuckets
;
i
++
{
for
{
if
p
:=
test
.
RandPeerIDFatal
(
t
);
CommonPrefixLen
(
ConvertPeerID
(
local
),
ConvertPeerID
(
p
))
==
i
{
rt
.
Update
(
p
)
...
...
@@ -63,30 +64,31 @@ func TestGenRandPeerID(t *testing.T) {
}
}
for
i
:=
0
;
i
<
5
;
i
++
{
peerID
,
err
:=
rt
.
GenRandPeerID
(
i
)
for
bucketID
:=
0
;
bucketID
<
nBuckets
;
bucketID
++
{
peerID
,
err
:=
rt
.
GenRandPeerID
(
bucketID
)
if
err
!=
nil
||
len
(
peerID
)
==
0
{
t
.
Fatalf
(
"error %+v & peerID %s for bucket %d"
,
err
,
peerID
,
i
)
t
.
Fatalf
(
"error %+v & peerID %s for bucket %d"
,
err
,
peerID
,
bucketID
)
}
var
expectedCpl
int
if
i
<
len
(
rt
.
Buckets
)
-
1
{
expectedCpl
=
i
// except for the last bucket, CPL should be Exactly bucketID
if
bucketID
<
len
(
rt
.
Buckets
)
-
1
{
if
CommonPrefixLen
(
ConvertPeerID
(
peerID
),
rt
.
local
)
!=
bucketID
{
t
.
Fatalf
(
"cpl should be %d for bucket %d but got %d, generated peerID is %s"
,
bucketID
,
bucketID
,
CommonPrefixLen
(
ConvertPeerID
(
peerID
),
rt
.
local
),
peerID
)
}
}
else
{
expectedCpl
=
len
(
rt
.
B
ucket
s
)
}
if
CommonPrefixLen
(
ConvertPeerID
(
peerID
),
rt
.
local
)
!=
expectedCpl
{
t
.
Fatalf
(
"cpl should be %d for bucket %d, generated peerID is %s"
,
expectedCpl
,
i
,
peerID
)
// for the last bucket, CPL should be Atleast the length of the b
ucket
if
CommonPrefixLen
(
ConvertPeerID
(
peerID
),
rt
.
local
)
<
len
(
rt
.
Buckets
)
{
t
.
Fatalf
(
"cpl should be ATLEAST %d for bucket %d but got %d, generated peerID is %s"
,
len
(
rt
.
Buckets
),
bucketID
,
CommonPrefixLen
(
ConvertPeerID
(
peerID
),
rt
.
local
)
,
peerID
)
}
}
}
// test error case
peerID
,
err
:=
rt
.
GenRandPeerID
(
-
1
)
if
err
!=
ErrGenRandPeerIDFailed
||
len
(
peerID
)
!=
0
{
t
.
Fatalf
(
"should have got err ErrGenRandPeerIDFailed for bucketID -1"
)
_
,
err
:=
rt
.
GenRandPeerID
(
-
1
)
if
err
==
nil
{
t
.
Fatalf
(
"should get erorr for bucketID=-1"
)
}
}
func
TestTableCallbacks
(
t
*
testing
.
T
)
{
...
...
util.go
View file @
776df761
...
...
@@ -8,7 +8,7 @@ import (
ks
"github.com/libp2p/go-libp2p-kbucket/keyspace"
u
"github.com/ipfs/go-ipfs-util"
sha256
"github.com/minio/sha256-simd"
"github.com/minio/sha256-simd"
)
// Returned if a routing table query returns no results. This is NOT expected
...
...
@@ -35,6 +35,22 @@ func xor(a, b ID) ID {
return
ID
(
u
.
XOR
(
a
,
b
))
}
func
setBit
(
n
byte
,
pos
uint
)
byte
{
n
|=
(
1
<<
pos
)
return
n
}
func
clearBit
(
n
byte
,
pos
uint
)
byte
{
mask
:=
byte
(
^
(
1
<<
pos
))
n
&=
mask
return
n
}
func
isSet
(
n
byte
,
pos
uint
)
bool
{
val
:=
n
&
(
1
<<
pos
)
return
(
val
>
0
)
}
func
CommonPrefixLen
(
a
,
b
ID
)
int
{
return
ks
.
ZeroPrefixLen
(
u
.
XOR
(
a
,
b
))
}
...
...
util_test.go
0 → 100644
View file @
776df761
package
kbucket
import
(
"testing"
)
func
TestIsSet
(
t
*
testing
.
T
)
{
a
:=
byte
(
2
)
if
!
isSet
(
a
,
1
)
{
t
.
Fatal
(
"1st bit should be set"
)
}
if
isSet
(
a
,
0
)
{
t
.
Fatal
(
"0th bit should not be set"
)
}
}
func
TestSetBit
(
t
*
testing
.
T
)
{
a
:=
byte
(
1
)
if
setBit
(
a
,
1
)
!=
3
{
t
.
Fatal
(
"1st bit should have been set"
)
}
}
func
TestClearBit
(
t
*
testing
.
T
)
{
a
:=
byte
(
3
)
if
clearBit
(
a
,
0
)
!=
2
{
t
.
Fatal
(
"0th bit should have been cleared"
)
}
}
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