Skip to content
This repository has been archived by the owner on Aug 2, 2021. It is now read-only.

Commit

Permalink
pot: Add Distance methods with tests (#1621)
Browse files Browse the repository at this point in the history
* pot: Add Distance methods with tests

* pot: Add comment

* pot: Remove commented old function declarations

* network, pot: Remove redundant indirection for prox/distance calcs

* pot: Table-driven tests

* pot: Remove redundant comments, correct varnames in remaining comments

* network: Remove commented line
  • Loading branch information
nolash authored and zelig committed Aug 14, 2019
1 parent 76d2eba commit b246437
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 10 deletions.
2 changes: 1 addition & 1 deletion network/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func NotifyPeer(p *BzzAddr, k *Kademlia) {
// unless already notified during the connection session
func (d *Peer) NotifyPeer(a *BzzAddr, po uint8) {
// immediately return
if (po < d.getDepth() && pot.ProxCmp(d.kad.BaseAddr(), d, a) != 1) || d.seen(a) {
if (po < d.getDepth() && pot.ProxCmp(d.kad.BaseAddr(), d.Address(), a.Address()) != 1) || d.seen(a) {
return
}
resp := &peersMsg{
Expand Down
55 changes: 46 additions & 9 deletions pot/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ package pot

import (
"encoding/binary"
"errors"
"fmt"
"math/big"
"math/rand"
"strconv"
"strings"
Expand Down Expand Up @@ -77,22 +79,57 @@ func (a Address) Bytes() []byte {
return a[:]
}

// ProxCmp compares the distances a->target and b->target.
// Returns -1 if a is closer to target, 1 if b is closer to target
// and 0 if they are equal.
func ProxCmp(a, x, y interface{}) int {
return proxCmp(ToBytes(a), ToBytes(x), ToBytes(y))
// Distance returns the distance between address x and address y as a (comparable) big integer using the distance metric defined in the swarm specification
// Fails if not all addresses are of equal length
func Distance(x, y []byte) (*big.Int, error) {
distanceBytes, err := DistanceRaw(x, y)
if err != nil {
return nil, err
}
r := big.NewInt(0)
r.SetBytes(distanceBytes)
return r, nil
}

func proxCmp(a, x, y []byte) int {
// DistanceRaw returns the distance between address x and address y in big-endian binary format using the distance metric defined in the swarm specfication
// Fails if not all addresses are of equal length
func DistanceRaw(x, y []byte) ([]byte, error) {
if len(x) != len(y) {
return nil, errors.New("address length must match")
}
c := NewAddressFromBytes(make([]byte, len(x)))
for i, addr := range x {
c[i] = addr ^ y[i]
}
return c.Bytes(), nil
}

// DistanceCmp compares x and y to a in terms of the distance metric defined in the swarm specfication
// it returns:
// 1 if x is closer to a than y
// 0 if x and y are equally far apart from (this means that x and y are the same address)
// -1 if x is farther from a than y
// Fails if not all addresses are of equal length
func DistanceCmp(a, x, y []byte) (int, error) {
if len(a) != len(x) || len(a) != len(y) {
return 0, errors.New("address length must match")
}
return ProxCmp(a, x, y), nil
}

// ProxCmp compares the distances x->a and y->a
// Returns -1 if x is closer to a, 1 if y is closer to a
// and 0 if they are equal.
func ProxCmp(a, x, y []byte) int {
for i := range a {
dx := x[i] ^ a[i]
dy := y[i] ^ a[i]
if dx > dy {
if dx == dy {
continue
} else if dx > dy {
return 1
} else if dx < dy {
return -1
}
return -1
}
return 0
}
Expand Down
80 changes: 80 additions & 0 deletions pot/address_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package pot

import (
"testing"

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethersphere/swarm/log"
)

type distanceTest struct {
x []byte
y []byte
result string
}

type distanceCmpTest struct {
x []byte
y []byte
z []byte
result int
}

var (
distanceTests = []distanceTest{
{
x: hexutil.MustDecode("0x9100000000000000000000000000000000000000000000000000000000000000"),
y: hexutil.MustDecode("0x8200000000000000000000000000000000000000000000000000000000000000"),
result: "8593944123082061379093159043613555660984881674403010612303492563087302590464",
},
}

distanceCmpTests = []distanceCmpTest{
{
x: hexutil.MustDecode("0x9100000000000000000000000000000000000000000000000000000000000000"),
y: hexutil.MustDecode("0x8200000000000000000000000000000000000000000000000000000000000000"),
z: hexutil.MustDecode("0x1200000000000000000000000000000000000000000000000000000000000000"),
result: -1,
},
{
x: hexutil.MustDecode("0x9100000000000000000000000000000000000000000000000000000000000000"),
y: hexutil.MustDecode("0x1200000000000000000000000000000000000000000000000000000000000000"),
z: hexutil.MustDecode("0x8200000000000000000000000000000000000000000000000000000000000000"),
result: 1,
},
{
x: hexutil.MustDecode("0x9100000000000000000000000000000000000000000000000000000000000000"),
y: hexutil.MustDecode("0x1200000000000000000000000000000000000000000000000000000000000000"),
z: hexutil.MustDecode("0x1200000000000000000000000000000000000000000000000000000000000000"),
result: 0,
},
}
)

// TestDistance tests the correctness of the distance calculation
func TestDistance(t *testing.T) {
for i, dt := range distanceTests {
log.Debug("Distance test", "i", i, "dt", dt)
distance, err := Distance(dt.x, dt.y)
if err != nil {
t.Fatal(err)
}
if distance.String() != dt.result {
t.Fatalf("incorrect distance, expected %s, got %s (x: %x, y: %x)", dt.result, distance.String(), dt.x, dt.y)
}
}
}

// TestDistanceCmp tests the distance comparison method
func TestDistanceCmp(t *testing.T) {
for i, dt := range distanceCmpTests {
log.Debug("DistanceCmp test", "i", i, "dt", dt)
direction, err := DistanceCmp(dt.x, dt.y, dt.z)
if err != nil {
t.Fatal(err)
}
if direction != dt.result {
t.Fatalf("incorrect distance compare, expected %d, got %d (x: %x, y: %x, z: %x)", dt.result, direction, dt.x, dt.y, dt.z)
}
}
}

0 comments on commit b246437

Please sign in to comment.