Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kad Import #2

Merged
merged 3 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module github.com/libp2p/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
)
10 changes: 10 additions & 0 deletions go.sum
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=
2 changes: 2 additions & 0 deletions kad/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package kad provides interfaces defining core Kademlia types
package kad
11 changes: 11 additions & 0 deletions kad/internal/test/bench.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//go:build go1.20

package test

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)
}
24 changes: 24 additions & 0 deletions kad/internal/test/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package test

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)
}
108 changes: 108 additions & 0 deletions kad/internal/test/ids.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package test

import (
"crypto/sha256"
"net"

"github.com/libp2p/go-libdht/kad"
"github.com/libp2p/go-libdht/kad/key"
"github.com/libp2p/go-libdht/kad/key/key256"
)

// 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[key256.Key256] = (*StringID)(nil)

func NewStringID(s string) *StringID {
return (*StringID)(&s)
}

func (s StringID) Key() key256.Key256 {
h := sha256.New()
h.Write([]byte(s))
return key256.NewKey256(h.Sum(nil))
}

func (s StringID) NodeID() kad.NodeID[key256.Key256] {
return &s
}

func (s StringID) Equal(other string) bool {
return string(s) == other
}

func (s StringID) String() string {
return string(s)
}

type Info[K kad.Key[K], A kad.Address[A]] struct {
id *ID[K]
addrs []A
}

var _ kad.NodeInfo[Key8, net.IP] = (*Info[Key8, net.IP])(nil)

func NewInfo[K kad.Key[K], A kad.Address[A]](id *ID[K], addrs []A) *Info[K, A] {
return &Info[K, A]{
id: id,
addrs: addrs,
}
}

func (a *Info[K, A]) AddAddr(addr A) {
a.addrs = append(a.addrs, addr)
}

func (a *Info[K, A]) RemoveAddr(addr A) {
writeIndex := 0
// remove all occurrences of addr
for _, ad := range a.addrs {
if !ad.Equal(addr) {
a.addrs[writeIndex] = ad
writeIndex++
}
}
a.addrs = a.addrs[:writeIndex]
}

func (a *Info[K, A]) ID() kad.NodeID[K] {
return a.id
}

func (a *Info[K, A]) Addresses() []A {
addresses := make([]A, len(a.addrs))
copy(addresses, a.addrs)
return addresses
}
131 changes: 131 additions & 0 deletions kad/internal/test/key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package test

import (
"fmt"

"github.com/libp2p/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))
}
35 changes: 35 additions & 0 deletions kad/internal/test/key_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package test

import (
"testing"

"github.com/libp2p/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)
}
45 changes: 45 additions & 0 deletions kad/internal/test/rand.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package test

import (
"math/rand"
"strconv"

"github.com/libp2p/go-libdht/kad/key/key256"
)

var rng = rand.New(rand.NewSource(299792458))

// RandomKey returns a random 32-bit Kademlia key.
func RandomKey() Key32 {
return Key32(rng.Uint32())
}

// RandomKeyWithPrefix returns a 32-bit Kademlia key having a prefix equal to the bit pattern held in s and
// random following bits. A prefix of up to 32 bits is supported.
func RandomKeyWithPrefix(s string) Key32 {
kk := RandomKey()
if s == "" {
return kk
}

prefixbits := len(s)
if prefixbits > 32 {
panic("RandomKeyWithPrefix: prefix too long")
}
n, err := strconv.ParseInt(s, 2, 32)
if err != nil {
panic("RandomKeyWithPrefix: " + err.Error())
}
prefix := uint32(n) << (32 - prefixbits)

v := uint32(kk) << prefixbits
v >>= prefixbits

return Key32(v | prefix)
}

// Key256WithLeadingBytes returns a 256-bit Kademlia key consisting of the given leading bytes padded by
// zero bytes to the end of the key.
func Key256WithLeadingBytes(in []byte) key256.Key256 {
return key256.NewKey256(append(in, make([]byte, 32-len(in))...))
}
Loading