Commit e37b9d1e authored by Petar Maymounkov's avatar Petar Maymounkov

Refactor in trie. Add Kademlia routing table health reports skeleton.

parent 8b25ab89
package kademlia
import (
"github.com/libp2p/go-libp2p-xor/key"
"github.com/libp2p/go-libp2p-xor/trie"
)
// TableHealthReport describes the discrepancy between a node's routing table from the theoretical ideal,
// given knowledge of all nodes present in the network.
type TableHealthReport struct {
// IdealDepth is the depth that the node's rouing table should have.
IdealDepth int
// ActualDepth is the depth that the node's routing table has.
ActualDepth int
// Bucket...
Bucket []*BucketHealthReport
}
// 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).
type BucketHealthReport struct {
// MaxKnownContacts is the number of all known network nodes,
// which are eligible to be in this bucket.
MaxKnownContacts int
// ActualKnownContacts is the number of known network nodes,
// that are actually in the node's routing table.
ActualKnownContacts int
// ActualUnknownContacts is the number of contacts in the node's routing table,
// that are not known to be in the network currently.
ActualUnknownContacts int
}
// TableHealth computes the health report for a node,
// 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 {
// Reconstruct the node's routing table as a trie
nodeTable := trie.New()
nodeTable.Add(node)
for _, u := range nodeContacts {
nodeTable.Add(u)
}
// Compute health report
idealDepth, _ := knownNodes.Find(node)
actualDepth, _ := nodeTable.Find(node)
return &TableHealthReport{
IdealDepth: idealDepth,
ActualDepth: actualDepth,
Bucket: BucketHealth(node, nodeTable, knownNodes),
}
}
// 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.
func BucketHealth(node key.Key, nodeTable, knownNodes *trie.Trie) []*BucketHealthReport {
panic("u")
// actualDepth, _ := nodeTable.Find(node)
// bucket := makeXXX
// return bucket
}
// Package kademlia provides Kademlia routing-related facilities.
package kademlia
......@@ -4,34 +4,62 @@ import (
"github.com/libp2p/go-libp2p-xor/key"
)
// Add adds the key q to the trie. Add mutates the trie.
// TODO: Also implement an immutable version of Add.
func (trie *Trie) Add(q key.Key) (insertedDepth int, insertedOK bool) {
return trie.AddAtDepth(0, q)
}
func (trie *Trie) AddAtDepth(depth int, q key.Key) (insertedDepth int, insertedOK bool) {
if qb := trie.Branch[q.BitAt(depth)]; qb != nil {
return qb.AddAtDepth(depth+1, q)
} else {
if trie.Key == nil {
trie.Key = q
return depth, true
} else {
if key.Equal(trie.Key, q) {
return depth, false
} else {
p := trie.Key
trie.Key = nil
// both Branches are nil
trie.Branch[0], trie.Branch[1] = &Trie{}, &Trie{}
trie.Branch[p.BitAt(depth)].AddAtDepth(depth+1, p)
return trie.Branch[q.BitAt(depth)].AddAtDepth(depth+1, q)
}
}
}
}
// Add adds the key q to trie, returning a new trie.
// Add is immutable/non-destructive: The original trie remains unchanged.
func Add(trie *XorTrie, q key.Key) *XorTrie {
return add(0, trie, q)
func Add(trie *Trie, q key.Key) *Trie {
return AddAtDepth(0, trie, q)
}
func add(depth int, trie *XorTrie, q key.Key) *XorTrie {
func AddAtDepth(depth int, trie *Trie, q key.Key) *Trie {
dir := q.BitAt(depth)
if !trie.isLeaf() {
s := &XorTrie{}
s.Branch[dir] = add(depth+1, trie.Branch[dir], q)
if !trie.IsLeaf() {
s := &Trie{}
s.Branch[dir] = AddAtDepth(depth+1, trie.Branch[dir], q)
s.Branch[1-dir] = trie.Branch[1-dir]
return s
} else {
if trie.Key == nil {
return &XorTrie{Key: q}
return &Trie{Key: q}
} else {
if key.Equal(trie.Key, q) {
return trie
} else {
s := &XorTrie{}
s := &Trie{}
if q.BitAt(depth) == trie.Key.BitAt(depth) {
s.Branch[dir] = add(depth+1, &XorTrie{Key: trie.Key}, q)
s.Branch[1-dir] = &XorTrie{}
s.Branch[dir] = AddAtDepth(depth+1, &Trie{Key: trie.Key}, q)
s.Branch[1-dir] = &Trie{}
return s
} else {
s.Branch[dir] = add(depth+1, &XorTrie{Key: trie.Key}, q)
s.Branch[1-dir] = &XorTrie{}
s.Branch[dir] = AddAtDepth(depth+1, &Trie{Key: trie.Key}, q)
s.Branch[1-dir] = &Trie{}
}
return s
}
......
......@@ -4,11 +4,11 @@ import (
"github.com/libp2p/go-libp2p-xor/key"
)
func Equal(p, q *XorTrie) bool {
func Equal(p, q *Trie) bool {
switch {
case p.isLeaf() && q.isLeaf():
case p.IsLeaf() && q.IsLeaf():
return key.Equal(p.Key, q.Key)
case !p.isLeaf() && !q.isLeaf():
case !p.IsLeaf() && !q.IsLeaf():
return Equal(p.Branch[0], q.Branch[0]) && Equal(p.Branch[1], q.Branch[1])
}
return false
......
package trie
import (
"github.com/libp2p/go-libp2p-xor/key"
)
func (trie *Trie) Find(q key.Key) (reachedDepth int, found bool) {
return trie.FindAtDepth(0, q)
}
func (trie *Trie) FindAtDepth(depth int, q key.Key) (reachedDepth int, found bool) {
if qb := trie.Branch[q.BitAt(depth)]; qb != nil {
return qb.FindAtDepth(depth+1, q)
} else {
if trie.Key == nil {
return depth, false
} else {
return depth, key.Equal(trie.Key, q)
}
}
}
......@@ -6,39 +6,39 @@ import (
// Intersect computes the intersection of the keys in p and q.
// p and q must be non-nil. The returned trie is never nil.
func Intersect(p, q *XorTrie) *XorTrie {
return intersect(0, p, q)
func Intersect(p, q *Trie) *Trie {
return IntersectAtDepth(0, p, q)
}
func intersect(depth int, p, q *XorTrie) *XorTrie {
func IntersectAtDepth(depth int, p, q *Trie) *Trie {
switch {
case p.isLeaf() && q.isLeaf():
if p.isEmpty() || q.isEmpty() {
return &XorTrie{} // empty set
case p.IsLeaf() && q.IsLeaf():
if p.IsEmpty() || q.IsEmpty() {
return &Trie{} // empty set
} else {
if key.Equal(p.Key, q.Key) {
return &XorTrie{Key: p.Key} // singleton
return &Trie{Key: p.Key} // singleton
} else {
return &XorTrie{} // empty set
return &Trie{} // empty set
}
}
case p.isLeaf() && !q.isLeaf():
if p.isEmpty() {
return &XorTrie{} // empty set
case p.IsLeaf() && !q.IsLeaf():
if p.IsEmpty() {
return &Trie{} // empty set
} else {
if _, found := q.find(depth, p.Key); found {
return &XorTrie{Key: p.Key}
if _, found := q.FindAtDepth(depth, p.Key); found {
return &Trie{Key: p.Key}
} else {
return &XorTrie{} // empty set
return &Trie{} // empty set
}
}
case !p.isLeaf() && q.isLeaf():
case !p.IsLeaf() && q.IsLeaf():
return Intersect(q, p)
case !p.isLeaf() && !q.isLeaf():
disjointUnion := &XorTrie{
Branch: [2]*XorTrie{
intersect(depth+1, p.Branch[0], q.Branch[0]),
intersect(depth+1, p.Branch[1], q.Branch[1]),
case !p.IsLeaf() && !q.IsLeaf():
disjointUnion := &Trie{
Branch: [2]*Trie{
IntersectAtDepth(depth+1, p.Branch[0], q.Branch[0]),
IntersectAtDepth(depth+1, p.Branch[1], q.Branch[1]),
},
}
disjointUnion.shrink()
......
package trie
import (
"github.com/libp2p/go-libp2p-xor/key"
)
// Remove removes the key q from the trie. Remove mutates the trie.
// TODO: Also implement an immutable version of Add.
func (trie *Trie) Remove(q key.Key) (removedDepth int, removed bool) {
return trie.RemoveAtDepth(0, q)
}
func (trie *Trie) RemoveAtDepth(depth int, q key.Key) (reachedDepth int, removed bool) {
if qb := trie.Branch[q.BitAt(depth)]; qb != nil {
if d, ok := qb.RemoveAtDepth(depth+1, q); ok {
trie.shrink()
return d, true
} else {
return d, false
}
} else {
if trie.Key != nil && key.Equal(q, trie.Key) {
trie.Key = nil
return depth, true
} else {
return depth, false
}
}
}
package trie
import (
"github.com/libp2p/go-libp2p-xor/key"
)
// Trie is a trie for equal-length bit vectors, which stores values only in the leaves.
// Trie node invariants:
// (1) Either both branches are nil, or both are non-nil.
// (2) If branches are non-nil, key must be nil.
// (3) If both branches are leaves, then they are both non-empty (have keys).
type Trie struct {
Branch [2]*Trie
Key key.Key
}
func New() *Trie {
return &Trie{}
}
func (trie *Trie) Depth() int {
return trie.depth(0)
}
func (trie *Trie) depth(depth int) int {
if trie.Branch[0] == nil && trie.Branch[1] == nil {
return depth
} else {
return max(trie.Branch[0].depth(depth+1), trie.Branch[1].depth(depth+1))
}
}
func max(x, y int) int {
if x > y {
return x
}
return y
}
func (trie *Trie) IsEmpty() bool {
return trie.Key == nil
}
func (trie *Trie) IsLeaf() bool {
return trie.Branch[0] == nil && trie.Branch[1] == nil
}
func (trie *Trie) IsEmptyLeaf() bool {
return trie.IsEmpty() && trie.IsLeaf()
}
func (trie *Trie) IsNonEmptyLeaf() bool {
return !trie.IsEmpty() && trie.IsLeaf()
}
func (trie *Trie) shrink() {
b0, b1 := trie.Branch[0], trie.Branch[1]
switch {
case b0.IsEmptyLeaf() && b1.IsEmptyLeaf():
trie.Branch[0], trie.Branch[1] = nil, nil
case b0.IsEmptyLeaf() && b1.IsNonEmptyLeaf():
trie.Key = b1.Key
trie.Branch[0], trie.Branch[1] = nil, nil
case b0.IsNonEmptyLeaf() && b1.IsEmptyLeaf():
trie.Key = b0.Key
trie.Branch[0], trie.Branch[1] = nil, nil
}
}
......@@ -12,7 +12,7 @@ func TestInsertRemove(t *testing.T) {
testSeq(r, t)
}
func testSeq(r *XorTrie, t *testing.T) {
func testSeq(r *Trie, t *testing.T) {
for _, s := range testInsertSeq {
depth, _ := r.Add(key.Key(s.key))
if depth != s.insertedDepth {
......
package trie
import (
"github.com/libp2p/go-libp2p-xor/key"
)
// XorTrie is a trie for equal-length bit vectors, which stores values only in the leaves.
// XorTrie node invariants:
// (1) Either both branches are nil, or both are non-nil.
// (2) If branches are non-nil, key must be nil.
// (3) If both branches are leaves, then they are both non-empty (have keys).
type XorTrie struct {
Branch [2]*XorTrie
Key key.Key
}
func New() *XorTrie {
return &XorTrie{}
}
func (trie *XorTrie) Depth() int {
return trie.depth(0)
}
func (trie *XorTrie) depth(depth int) int {
if trie.Branch[0] == nil && trie.Branch[1] == nil {
return depth
} else {
return max(trie.Branch[0].depth(depth+1), trie.Branch[1].depth(depth+1))
}
}
func max(x, y int) int {
if x > y {
return x
}
return y
}
func (trie *XorTrie) Find(q key.Key) (reachedDepth int, found bool) {
return trie.find(0, q)
}
func (trie *XorTrie) find(depth int, q key.Key) (reachedDepth int, found bool) {
if qb := trie.Branch[q.BitAt(depth)]; qb != nil {
return qb.find(depth+1, q)
} else {
if trie.Key == nil {
return depth, false
} else {
return depth, key.Equal(trie.Key, q)
}
}
}
// Add adds the key q to the trie. Add mutates the trie.
// TODO: Also implement an immutable version of Add.
func (trie *XorTrie) Add(q key.Key) (insertedDepth int, insertedOK bool) {
return trie.add(0, q)
}
func (trie *XorTrie) add(depth int, q key.Key) (insertedDepth int, insertedOK bool) {
if qb := trie.Branch[q.BitAt(depth)]; qb != nil {
return qb.add(depth+1, q)
} else {
if trie.Key == nil {
trie.Key = q
return depth, true
} else {
if key.Equal(trie.Key, q) {
return depth, false
} else {
p := trie.Key
trie.Key = nil
// both Branches are nil
trie.Branch[0], trie.Branch[1] = &XorTrie{}, &XorTrie{}
trie.Branch[p.BitAt(depth)].add(depth+1, p)
return trie.Branch[q.BitAt(depth)].add(depth+1, q)
}
}
}
}
// Remove removes the key q from the trie. Remove mutates the trie.
// TODO: Also implement an immutable version of Add.
func (trie *XorTrie) Remove(q key.Key) (removedDepth int, removed bool) {
return trie.remove(0, q)
}
func (trie *XorTrie) remove(depth int, q key.Key) (reachedDepth int, removed bool) {
if qb := trie.Branch[q.BitAt(depth)]; qb != nil {
if d, ok := qb.remove(depth+1, q); ok {
trie.shrink()
return d, true
} else {
return d, false
}
} else {
if trie.Key != nil && key.Equal(q, trie.Key) {
trie.Key = nil
return depth, true
} else {
return depth, false
}
}
}
func (trie *XorTrie) isEmpty() bool {
return trie.Key == nil
}
func (trie *XorTrie) isLeaf() bool {
return trie.Branch[0] == nil && trie.Branch[1] == nil
}
func (trie *XorTrie) isEmptyLeaf() bool {
return trie.isEmpty() && trie.isLeaf()
}
func (trie *XorTrie) isNonEmptyLeaf() bool {
return !trie.isEmpty() && trie.isLeaf()
}
func (trie *XorTrie) shrink() {
b0, b1 := trie.Branch[0], trie.Branch[1]
switch {
case b0.isEmptyLeaf() && b1.isEmptyLeaf():
trie.Branch[0], trie.Branch[1] = nil, nil
case b0.isEmptyLeaf() && b1.isNonEmptyLeaf():
trie.Key = b1.Key
trie.Branch[0], trie.Branch[1] = nil, nil
case b0.isNonEmptyLeaf() && b1.isEmptyLeaf():
trie.Key = b0.Key
trie.Branch[0], trie.Branch[1] = nil, nil
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment