-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from plprobelab/kad-import
Kad Import
- Loading branch information
Showing
25 changed files
with
2,951 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
module github.com/plprobelab/go-libdht | ||
|
||
go 1.20 | ||
|
||
require github.com/stretchr/testify v1.8.4 | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= | ||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// Package kad provides interfaces defining core Kademlia types | ||
package kad |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package kad | ||
|
||
// Key is the interface all Kademlia key types support. | ||
// | ||
// A Kademlia key is defined as a bit string of arbitrary size. In practice, different Kademlia implementations use | ||
// different key sizes. For instance, the Kademlia paper (https://pdos.csail.mit.edu/~petar/papers/maymounkov-kademlia-lncs.pdf) | ||
// defines keys as 160-bits long and IPFS uses 256-bit keys. | ||
// | ||
// Keys are usually generated using cryptographic hash functions, however the specifics of key generation | ||
// do not matter for key operations. | ||
// | ||
// A Key is not necessarily used to identify a node in the network but a derived | ||
// representation. Implementations may choose to hash a logical node identifier | ||
// to derive a Kademlia Key. Therefore, there also exists the concept of a NodeID | ||
// which just defines a method to return the associated Kademlia Key. | ||
type Key[K any] interface { | ||
// BitLen returns the length of the key in bits. | ||
BitLen() int | ||
|
||
// Bit returns the value of the i'th bit of the key from most significant to least. It is equivalent to (key>>(bitlen-i-1))&1. | ||
// Bit will panic if i is out of the range [0,BitLen()-1]. | ||
Bit(i int) uint | ||
|
||
// Xor returns the result of the eXclusive OR operation between the key and another key of the same type. | ||
Xor(other K) K | ||
|
||
// CommonPrefixLength returns the number of leading bits the key shares with another key of the same type. | ||
// The CommonPrefixLength of a key with itself is equal to BitLen. | ||
CommonPrefixLength(other K) int | ||
|
||
// Compare compares the numeric value of the key with another key of the same type. | ||
// It returns -1 if the key is numerically less than other, +1 if it is greater | ||
// and 0 if both keys are equal. | ||
Compare(other K) int | ||
} | ||
|
||
// RoutingTable is the interface all Kademlia Routing Tables types support. | ||
type RoutingTable[K Key[K], N NodeID[K]] interface { | ||
// AddNode tries to add a peer to the routing table. It returns true if | ||
// the node was added and false if it wasn't added, e.g., because it | ||
// was already part of the routing table. | ||
// | ||
// Because NodeID[K]'s are often preimages to Kademlia keys K | ||
// there's no way to derive a NodeID[K] from just K. Therefore, to be | ||
// able to return NodeID[K]'s from the `NearestNodes` method, this | ||
// `AddNode` method signature takes a NodeID[K] instead of only K. | ||
// | ||
// Nodes added to the routing table are grouped into buckets based on their | ||
// XOR distance to the local node's identifier. The details of the XOR | ||
// arithmetics are defined on K. | ||
AddNode(N) bool | ||
|
||
// RemoveKey tries to remove a node identified by its Kademlia key from the | ||
// routing table. | ||
// | ||
// It returns true if the key existed in the routing table and was removed. | ||
// It returns false if the key didn't exist in the routing table and | ||
// therefore, was not removed. | ||
RemoveKey(K) bool | ||
|
||
// NearestNodes returns the given number of closest nodes to a given | ||
// Kademlia key that are currently present in the routing table. | ||
// The returned list of nodes will be ordered from closest to furthest and | ||
// contain at maximum the given number of entries, but also possibly less | ||
// if the number exceeds the number of nodes in the routing table. | ||
NearestNodes(K, int) []N | ||
|
||
// GetNode returns the node identified by the supplied Kademlia key or a zero | ||
// value if the node is not present in the routing table. The boolean second | ||
// return value indicates whether the node was found in the table. | ||
GetNode(K) (N, bool) | ||
} | ||
|
||
// NodeID is a generic node identifier and not equal to a Kademlia key. Some | ||
// implementations use NodeID's as preimages for Kademlia keys. Kademlia keys | ||
// are used for calculating distances between nodes while NodeID's are the | ||
// original logical identifier of a node. | ||
// | ||
// The NodeID interface only defines a method that returns the Kademlia key | ||
// for the given NodeID. E.g., the operation to go from a NodeID to a Kademlia key | ||
// can be as simple as hashing the NodeID. | ||
// | ||
// Implementations may choose to equate NodeID's and Kademlia keys. | ||
type NodeID[K Key[K]] interface { | ||
// Key returns the Kademlia key of the given NodeID. E.g., NodeID's can be | ||
// preimages to Kademlia keys, in which case, Key() could return the SHA256 | ||
// of NodeID. | ||
Key() K | ||
|
||
// String returns a string reprensentation for this NodeID. | ||
// TODO: Try to get rid of this as it's also used for map keys which is not great. | ||
String() string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
//go:build go1.20 | ||
|
||
package kadtest | ||
|
||
import "testing" | ||
|
||
// ReportTimePerItemMetric adds a custom metric to a benchmark that reports the number of nanoseconds taken per item. | ||
func ReportTimePerItemMetric(b *testing.B, n int, name string) { | ||
// b.Elapsed was added in Go 1.20 | ||
b.ReportMetric(float64(b.Elapsed().Nanoseconds())/float64(n), "ns/"+name) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package kadtest | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
"time" | ||
) | ||
|
||
// Ctx returns a Context and a CancelFunc. The context will be | ||
// cancelled just before the test binary deadline (as | ||
// specified by the -timeout flag when running the test). The | ||
// CancelFunc may be called to cancel the context earlier than | ||
// the deadline. | ||
func Ctx(t *testing.T) (context.Context, context.CancelFunc) { | ||
t.Helper() | ||
|
||
deadline, ok := t.Deadline() | ||
if !ok { | ||
deadline = time.Now().Add(time.Minute) | ||
} else { | ||
deadline = deadline.Add(-time.Second) | ||
} | ||
return context.WithDeadline(context.Background(), deadline) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package kadtest | ||
|
||
import ( | ||
"crypto/sha256" | ||
|
||
"github.com/plprobelab/go-libdht/kad" | ||
"github.com/plprobelab/go-libdht/kad/key" | ||
"github.com/plprobelab/go-libdht/kad/key/bit256" | ||
) | ||
|
||
// ID is a concrete implementation of the NodeID interface. | ||
type ID[K kad.Key[K]] struct { | ||
key K | ||
} | ||
|
||
// interface assertion. Using the concrete key type of key.Key8 does not | ||
// limit the validity of the assertion for other key types. | ||
var _ kad.NodeID[Key8] = (*ID[Key8])(nil) | ||
|
||
// NewID returns a new Kademlia identifier that implements the NodeID interface. | ||
// Instead of deriving the Kademlia key from a NodeID, this method directly takes | ||
// the Kademlia key. | ||
func NewID[K kad.Key[K]](k K) *ID[K] { | ||
return &ID[K]{key: k} | ||
} | ||
|
||
// Key returns the Kademlia key that is used by, e.g., the routing table | ||
// implementation to group nodes into buckets. The returned key was manually | ||
// defined in the ID constructor NewID and not derived via, e.g., hashing | ||
// a preimage. | ||
func (i ID[K]) Key() K { | ||
return i.key | ||
} | ||
|
||
func (i ID[K]) Equal(other K) bool { | ||
return i.key.Compare(other) == 0 | ||
} | ||
|
||
func (i ID[K]) String() string { | ||
return key.HexString(i.key) | ||
} | ||
|
||
type StringID string | ||
|
||
var _ kad.NodeID[bit256.Key] = (*StringID)(nil) | ||
|
||
func NewStringID(s string) *StringID { | ||
return (*StringID)(&s) | ||
} | ||
|
||
func (s StringID) Key() bit256.Key { | ||
h := sha256.New() | ||
h.Write([]byte(s)) | ||
return bit256.NewKey(h.Sum(nil)) | ||
} | ||
|
||
func (s StringID) NodeID() kad.NodeID[bit256.Key] { | ||
return &s | ||
} | ||
|
||
func (s StringID) Equal(other string) bool { | ||
return string(s) == other | ||
} | ||
|
||
func (s StringID) String() string { | ||
return string(s) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package kadtest | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/plprobelab/go-libdht/kad" | ||
) | ||
|
||
const bitPanicMsg = "bit index out of range" | ||
|
||
// Key32 is a 32-bit Kademlia key, suitable for testing and simulation of small networks. | ||
type Key32 uint32 | ||
|
||
var _ kad.Key[Key32] = Key32(0) | ||
|
||
// BitLen returns the length of the key in bits, which is always 32. | ||
func (Key32) BitLen() int { | ||
return 32 | ||
} | ||
|
||
// Bit returns the value of the i'th bit of the key from most significant to least. | ||
func (k Key32) Bit(i int) uint { | ||
if i < 0 || i > 31 { | ||
panic(bitPanicMsg) | ||
} | ||
return uint((k >> (31 - i)) & 1) | ||
} | ||
|
||
// Xor returns the result of the eXclusive OR operation between the key and another key of the same type. | ||
func (k Key32) Xor(o Key32) Key32 { | ||
return k ^ o | ||
} | ||
|
||
// CommonPrefixLength returns the number of leading bits the key shares with another key of the same type. | ||
func (k Key32) CommonPrefixLength(o Key32) int { | ||
a := uint32(k) | ||
b := uint32(o) | ||
for i := 32; i > 0; i-- { | ||
if a == b { | ||
return i | ||
} | ||
a >>= 1 | ||
b >>= 1 | ||
} | ||
return 0 | ||
} | ||
|
||
// Compare compares the numeric value of the key with another key of the same type. | ||
func (k Key32) Compare(o Key32) int { | ||
if k < o { | ||
return -1 | ||
} else if k > o { | ||
return 1 | ||
} | ||
return 0 | ||
} | ||
|
||
// HexString returns a string containing the hexadecimal representation of the key. | ||
func (k Key32) HexString() string { | ||
return fmt.Sprintf("%04x", uint32(k)) | ||
} | ||
|
||
// BitString returns a string containing the binary representation of the key. | ||
func (k Key32) BitString() string { | ||
return fmt.Sprintf("%032b", uint32(k)) | ||
} | ||
|
||
func (k Key32) String() string { | ||
return k.HexString() | ||
} | ||
|
||
// Key8 is an 8-bit Kademlia key, suitable for testing and simulation of very small networks. | ||
type Key8 uint8 | ||
|
||
var _ kad.Key[Key8] = Key8(0) | ||
|
||
// BitLen returns the length of the key in bits, which is always 8. | ||
func (Key8) BitLen() int { | ||
return 8 | ||
} | ||
|
||
// Bit returns the value of the i'th bit of the key from most significant to least. | ||
func (k Key8) Bit(i int) uint { | ||
if i < 0 || i > 7 { | ||
panic(bitPanicMsg) | ||
} | ||
return uint((k >> (7 - i)) & 1) | ||
} | ||
|
||
// Xor returns the result of the eXclusive OR operation between the key and another key of the same type. | ||
func (k Key8) Xor(o Key8) Key8 { | ||
return k ^ o | ||
} | ||
|
||
// CommonPrefixLength returns the number of leading bits the key shares with another key of the same type. | ||
func (k Key8) CommonPrefixLength(o Key8) int { | ||
a := uint8(k) | ||
b := uint8(o) | ||
for i := 8; i > 0; i-- { | ||
if a == b { | ||
return i | ||
} | ||
a >>= 1 | ||
b >>= 1 | ||
} | ||
return 0 | ||
} | ||
|
||
// Compare compares the numeric value of the key with another key of the same type. | ||
func (k Key8) Compare(o Key8) int { | ||
if k < o { | ||
return -1 | ||
} else if k > o { | ||
return 1 | ||
} | ||
return 0 | ||
} | ||
|
||
// HexString returns a string containing the hexadecimal representation of the key. | ||
func (k Key8) HexString() string { | ||
return fmt.Sprintf("%x", uint8(k)) | ||
} | ||
|
||
func (k Key8) String() string { | ||
return k.HexString() | ||
} | ||
|
||
// HexString returns a string containing the binary representation of the key. | ||
func (k Key8) BitString() string { | ||
return fmt.Sprintf("%08b", uint8(k)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package kadtest | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/plprobelab/go-libdht/kad/key/test" | ||
) | ||
|
||
func TestKey32(t *testing.T) { | ||
tester := &test.KeyTester[Key32]{ | ||
Key0: Key32(0), | ||
Key1: Key32(1), | ||
Key2: Key32(2), | ||
Key1xor2: Key32(3), | ||
Key100: Key32(0x80000000), | ||
Key010: Key32(0x40000000), | ||
KeyX: Key32(0x23e4dd03), | ||
} | ||
|
||
tester.RunTests(t) | ||
} | ||
|
||
func TestKey8(t *testing.T) { | ||
tester := &test.KeyTester[Key8]{ | ||
Key0: Key8(0), | ||
Key1: Key8(1), | ||
Key2: Key8(2), | ||
Key1xor2: Key8(3), | ||
Key100: Key8(0x80), | ||
Key010: Key8(0x40), | ||
KeyX: Key8(0x23), | ||
} | ||
|
||
tester.RunTests(t) | ||
} |
Oops, something went wrong.