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-xor
Commits
17d1be95
Commit
17d1be95
authored
Mar 25, 2020
by
Petar Maymounkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add table health report.
parent
e37b9d1e
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
140 additions
and
9 deletions
+140
-9
kademlia/bucket.go
kademlia/bucket.go
+21
-0
kademlia/health.go
kademlia/health.go
+95
-6
trie/find.go
trie/find.go
+3
-0
trie/trie.go
trie/trie.go
+21
-3
No files found.
kademlia/bucket.go
0 → 100644
View file @
17d1be95
package
kademlia
import
(
"github.com/libp2p/go-libp2p-xor/key"
"github.com/libp2p/go-libp2p-xor/trie"
)
// BucketAtDepth returns the bucket in the routing table at a given depth.
// A bucket at depth D holds contacts that share a prefix of exactly D bits with node.
func
BucketAtDepth
(
node
key
.
Key
,
table
*
trie
.
Trie
,
depth
int
)
*
trie
.
Trie
{
dir
:=
node
.
BitAt
(
depth
)
if
table
.
IsLeaf
()
{
return
nil
}
else
{
if
depth
==
0
{
return
table
.
Branch
[
1
-
dir
]
}
else
{
return
BucketAtDepth
(
node
,
table
.
Branch
[
dir
],
depth
-
1
)
}
}
}
kademlia/health.go
View file @
17d1be95
package
kademlia
package
kademlia
import
(
import
(
"sort"
"github.com/libp2p/go-libp2p-xor/key"
"github.com/libp2p/go-libp2p-xor/key"
"github.com/libp2p/go-libp2p-xor/trie"
"github.com/libp2p/go-libp2p-xor/trie"
)
)
// TableHealthReport describes the discrepancy between a node's routing table from the theoretical ideal,
// TableHealthReport describes the discrepancy between a node's routing table from the theoretical ideal,
// given knowledge of all nodes present in the network.
// given knowledge of all nodes present in the network.
// TODO: Make printable in a way easy to ingest in Python/matplotlib for viewing in a Jupyter notebook.
// E.g. one would like to see a histogram of (IdealDepth - ActualDepth) across all tables.
type
TableHealthReport
struct
{
type
TableHealthReport
struct
{
// IdealDepth is the depth that the node's rouing table should have.
// IdealDepth is the depth that the node's rou
t
ing table should have.
IdealDepth
int
IdealDepth
int
// ActualDepth is the depth that the node's routing table has.
// ActualDepth is the depth that the node's routing table has.
ActualDepth
int
ActualDepth
int
// Bucket
..
.
// Bucket
contains the individual health reports for each of the node's routing buckets
.
Bucket
[]
*
BucketHealthReport
Bucket
[]
*
BucketHealthReport
}
}
// BucketHealth describes the discrepancy between a node's routing bucket and the theoretical ideal,
// BucketHealth describes the discrepancy between a node's routing bucket and the theoretical ideal,
// given knowledge of all nodes present in the network (aka the "known" nodes).
// given knowledge of all nodes present in the network (aka the "known" nodes).
type
BucketHealthReport
struct
{
type
BucketHealthReport
struct
{
// Depth is the bucket depth, starting from zero.
Depth
int
// MaxKnownContacts is the number of all known network nodes,
// MaxKnownContacts is the number of all known network nodes,
// which are eligible to be in this bucket.
// which are eligible to be in this bucket.
MaxKnownContacts
int
MaxKnownContacts
int
...
@@ -30,6 +36,40 @@ type BucketHealthReport struct {
...
@@ -30,6 +36,40 @@ type BucketHealthReport struct {
ActualUnknownContacts
int
ActualUnknownContacts
int
}
}
// sortedBucketHealthReport sorts bucket health reports in ascending order of depth.
type
sortedBucketHealthReport
[]
*
BucketHealthReport
func
(
s
sortedBucketHealthReport
)
Less
(
i
,
j
int
)
bool
{
return
s
[
i
]
.
Depth
<
s
[
j
]
.
Depth
}
func
(
s
sortedBucketHealthReport
)
Swap
(
i
,
j
int
)
{
s
[
i
],
s
[
j
]
=
s
[
j
],
s
[
i
]
}
func
(
s
sortedBucketHealthReport
)
Len
()
int
{
return
len
(
s
)
}
type
Table
struct
{
Node
key
.
Key
Contacts
[]
key
.
Key
}
// AllTablesHealth ...
func
AllTablesHealth
(
tables
[]
*
Table
)
(
report
[]
*
TableHealthReport
)
{
// Construct global network view trie
knownNodes
:=
trie
.
New
()
for
_
,
table
:=
range
tables
{
knownNodes
.
Add
(
table
.
Node
)
}
// Compute individual table health
for
_
,
table
:=
range
tables
{
report
=
append
(
report
,
TableHealth
(
table
.
Node
,
table
.
Contacts
,
knownNodes
))
}
return
}
// TableHealth computes the health report for a node,
// TableHealth computes the health report for a node,
// given its routing contacts and a list of all known nodes in the network currently.
// given its routing contacts and a list of all known nodes in the network currently.
func
TableHealth
(
node
key
.
Key
,
nodeContacts
[]
key
.
Key
,
knownNodes
*
trie
.
Trie
)
*
TableHealthReport
{
func
TableHealth
(
node
key
.
Key
,
nodeContacts
[]
key
.
Key
,
knownNodes
*
trie
.
Trie
)
*
TableHealthReport
{
...
@@ -52,8 +92,57 @@ func TableHealth(node key.Key, nodeContacts []key.Key, knownNodes *trie.Trie) *T
...
@@ -52,8 +92,57 @@ func TableHealth(node key.Key, nodeContacts []key.Key, knownNodes *trie.Trie) *T
// BucketHealth computes the health report for each bucket in a node's routing table,
// BucketHealth computes the health report for each bucket in a node's routing table,
// given the node's routing table and a list of all known nodes in the network currently.
// given the node's routing table and a list of all known nodes in the network currently.
func
BucketHealth
(
node
key
.
Key
,
nodeTable
,
knownNodes
*
trie
.
Trie
)
[]
*
BucketHealthReport
{
func
BucketHealth
(
node
key
.
Key
,
nodeTable
,
knownNodes
*
trie
.
Trie
)
[]
*
BucketHealthReport
{
panic
(
"u"
)
r
:=
walkBucketHealth
(
0
,
node
,
nodeTable
,
knownNodes
)
// actualDepth, _ := nodeTable.Find(node)
sort
.
Sort
(
sortedBucketHealthReport
(
r
))
// bucket := makeXXX
return
r
// return bucket
}
func
walkBucketHealth
(
depth
int
,
node
key
.
Key
,
nodeTable
,
knownNodes
*
trie
.
Trie
)
[]
*
BucketHealthReport
{
if
nodeTable
.
IsLeaf
()
{
return
nil
}
else
{
dir
:=
node
.
BitAt
(
depth
)
switch
{
case
knownNodes
==
nil
||
knownNodes
.
IsEmptyLeaf
()
:
r
:=
walkBucketHealth
(
depth
+
1
,
node
,
nodeTable
.
Branch
[
dir
],
nil
)
return
append
(
r
,
&
BucketHealthReport
{
Depth
:
depth
,
MaxKnownContacts
:
0
,
ActualKnownContacts
:
0
,
ActualUnknownContacts
:
nodeTable
.
Branch
[
1
-
dir
]
.
Size
(),
})
case
knownNodes
.
IsNonEmptyLeaf
()
:
if
knownNodes
.
Key
.
BitAt
(
depth
)
==
dir
{
r
:=
walkBucketHealth
(
depth
+
1
,
node
,
nodeTable
.
Branch
[
dir
],
knownNodes
)
return
append
(
r
,
&
BucketHealthReport
{
Depth
:
depth
,
MaxKnownContacts
:
0
,
ActualKnownContacts
:
0
,
ActualUnknownContacts
:
nodeTable
.
Branch
[
1
-
dir
]
.
Size
(),
})
}
else
{
r
:=
walkBucketHealth
(
depth
+
1
,
node
,
nodeTable
.
Branch
[
dir
],
nil
)
return
append
(
r
,
bucketReportFromTries
(
depth
,
nodeTable
.
Branch
[
1
-
dir
],
knownNodes
))
}
case
!
knownNodes
.
IsLeaf
()
:
r
:=
walkBucketHealth
(
depth
+
1
,
node
,
nodeTable
.
Branch
[
dir
],
knownNodes
.
Branch
[
dir
])
return
append
(
r
,
bucketReportFromTries
(
depth
,
nodeTable
.
Branch
[
1
-
dir
],
knownNodes
.
Branch
[
1
-
dir
]))
default
:
panic
(
"unreachable"
)
}
}
}
func
bucketReportFromTries
(
depth
int
,
actualBucket
,
maxBucket
*
trie
.
Trie
)
*
BucketHealthReport
{
actualKnown
:=
trie
.
Intersect
(
actualBucket
,
maxBucket
)
actualKnownSize
:=
actualKnown
.
Size
()
return
&
BucketHealthReport
{
Depth
:
depth
,
MaxKnownContacts
:
maxBucket
.
Size
(),
ActualKnownContacts
:
actualKnownSize
,
ActualUnknownContacts
:
actualBucket
.
Size
()
-
actualKnownSize
,
}
}
}
trie/find.go
View file @
17d1be95
...
@@ -4,6 +4,9 @@ import (
...
@@ -4,6 +4,9 @@ import (
"github.com/libp2p/go-libp2p-xor/key"
"github.com/libp2p/go-libp2p-xor/key"
)
)
// Find looks for the key q in the trie.
// It returns the depth of the leaf reached along the path of q, regardless of whether q was found in that leaf.
// It also returns a boolean flag indicating whether the key was found.
func
(
trie
*
Trie
)
Find
(
q
key
.
Key
)
(
reachedDepth
int
,
found
bool
)
{
func
(
trie
*
Trie
)
Find
(
q
key
.
Key
)
(
reachedDepth
int
,
found
bool
)
{
return
trie
.
FindAtDepth
(
0
,
q
)
return
trie
.
FindAtDepth
(
0
,
q
)
}
}
...
...
trie/trie.go
View file @
17d1be95
...
@@ -19,14 +19,14 @@ func New() *Trie {
...
@@ -19,14 +19,14 @@ func New() *Trie {
}
}
func
(
trie
*
Trie
)
Depth
()
int
{
func
(
trie
*
Trie
)
Depth
()
int
{
return
trie
.
d
epth
(
0
)
return
trie
.
DepthAtD
epth
(
0
)
}
}
func
(
trie
*
Trie
)
d
epth
(
depth
int
)
int
{
func
(
trie
*
Trie
)
DepthAtD
epth
(
depth
int
)
int
{
if
trie
.
Branch
[
0
]
==
nil
&&
trie
.
Branch
[
1
]
==
nil
{
if
trie
.
Branch
[
0
]
==
nil
&&
trie
.
Branch
[
1
]
==
nil
{
return
depth
return
depth
}
else
{
}
else
{
return
max
(
trie
.
Branch
[
0
]
.
d
epth
(
depth
+
1
),
trie
.
Branch
[
1
]
.
d
epth
(
depth
+
1
))
return
max
(
trie
.
Branch
[
0
]
.
DepthAtD
epth
(
depth
+
1
),
trie
.
Branch
[
1
]
.
DepthAtD
epth
(
depth
+
1
))
}
}
}
}
...
@@ -37,6 +37,24 @@ func max(x, y int) int {
...
@@ -37,6 +37,24 @@ func max(x, y int) int {
return
y
return
y
}
}
// Size returns the number of keys added to the trie.
// In other words, it returns the number of non-empty leaves in the trie.
func
(
trie
*
Trie
)
Size
()
int
{
return
trie
.
SizeAtDepth
(
0
)
}
func
(
trie
*
Trie
)
SizeAtDepth
(
depth
int
)
int
{
if
trie
.
Branch
[
0
]
==
nil
&&
trie
.
Branch
[
1
]
==
nil
{
if
trie
.
IsEmpty
()
{
return
0
}
else
{
return
1
}
}
else
{
return
trie
.
Branch
[
0
]
.
SizeAtDepth
(
depth
+
1
)
+
trie
.
Branch
[
1
]
.
SizeAtDepth
(
depth
+
1
)
}
}
func
(
trie
*
Trie
)
IsEmpty
()
bool
{
func
(
trie
*
Trie
)
IsEmpty
()
bool
{
return
trie
.
Key
==
nil
return
trie
.
Key
==
nil
}
}
...
...
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