mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-22 12:46:44 +00:00
cademlia proximity
- distance -> proximity - proximity bin properly implemented - fix update and adjust prox functions - rename fields and methods - comments
This commit is contained in:
parent
26c625d8e6
commit
c55c67dbb4
1 changed files with 72 additions and 32 deletions
106
p2p/cademlia.go
106
p2p/cademlia.go
|
|
@ -2,6 +2,7 @@ package p2p
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -11,8 +12,9 @@ import (
|
||||||
var cadlogger = ethlogger.NewLogger("CAD")
|
var cadlogger = ethlogger.NewLogger("CAD")
|
||||||
|
|
||||||
const (
|
const (
|
||||||
hashBits = 160
|
hashBytes = 20
|
||||||
rowLength = 10
|
rowLength = 10
|
||||||
|
maxProx = 20
|
||||||
)
|
)
|
||||||
|
|
||||||
var maxAge = 180 * time.Nanosecond
|
var maxAge = 180 * time.Nanosecond
|
||||||
|
|
@ -20,10 +22,11 @@ var purgeInterval = 300 * time.Second
|
||||||
|
|
||||||
type Cademlia struct {
|
type Cademlia struct {
|
||||||
Hash []byte
|
Hash []byte
|
||||||
HashBits int
|
HashBytes int
|
||||||
RowLength int
|
RowLength int
|
||||||
|
|
||||||
MaxProxSize int
|
MaxProx int
|
||||||
|
MaxProxBinSize int
|
||||||
|
|
||||||
MaxAge time.Duration
|
MaxAge time.Duration
|
||||||
PurgeInterval time.Duration
|
PurgeInterval time.Duration
|
||||||
|
|
@ -37,12 +40,16 @@ type Cademlia struct {
|
||||||
quitC chan bool
|
quitC chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// public constructor with compulsory arguments
|
||||||
|
// hash is a byte slice of length equal to self.HashBytes
|
||||||
func NewCademlia(hash []byte) *Cademlia {
|
func NewCademlia(hash []byte) *Cademlia {
|
||||||
return &Cademlia{
|
return &Cademlia{
|
||||||
Hash: hash, // compulsory fields without default
|
Hash: hash, // compulsory fields without default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start brings up a pool of peers potentially from an offline persisted source
|
||||||
|
// and sets default values for optional parameters
|
||||||
func (self *Cademlia) Start() error {
|
func (self *Cademlia) Start() error {
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
|
|
@ -51,15 +58,18 @@ func (self *Cademlia) Start() error {
|
||||||
}
|
}
|
||||||
// these + self.Hash can and should be checked against the
|
// these + self.Hash can and should be checked against the
|
||||||
// saved file/db
|
// saved file/db
|
||||||
if self.HashBits == 0 {
|
if self.HashBytes == 0 {
|
||||||
self.HashBits = hashBits
|
self.HashBytes = hashBytes
|
||||||
|
}
|
||||||
|
if self.MaxProx == 0 {
|
||||||
|
self.MaxProx = maxProx
|
||||||
}
|
}
|
||||||
if self.RowLength == 0 {
|
if self.RowLength == 0 {
|
||||||
self.RowLength = rowLength
|
self.RowLength = rowLength
|
||||||
}
|
}
|
||||||
// runtime parameters
|
// runtime parameters
|
||||||
if self.MaxProxSize == 0 {
|
if self.MaxProxBinSize == 0 {
|
||||||
self.MaxProxSize = self.RowLength
|
self.MaxProxBinSize = self.RowLength
|
||||||
}
|
}
|
||||||
if self.MaxAge == time.Duration(0) {
|
if self.MaxAge == time.Duration(0) {
|
||||||
self.MaxAge = maxAge
|
self.MaxAge = maxAge
|
||||||
|
|
@ -67,7 +77,7 @@ func (self *Cademlia) Start() error {
|
||||||
if self.PurgeInterval == time.Duration(0) {
|
if self.PurgeInterval == time.Duration(0) {
|
||||||
self.PurgeInterval = purgeInterval
|
self.PurgeInterval = purgeInterval
|
||||||
}
|
}
|
||||||
self.rows = make([]*row, self.HashBits)
|
self.rows = make([]*row, self.MaxProx)
|
||||||
for i, _ := range self.rows {
|
for i, _ := range self.rows {
|
||||||
self.rows[i] = &row{} // will initialise row{int(0),[]*entry(nil),sync.Mutex}
|
self.rows[i] = &row{} // will initialise row{int(0),[]*entry(nil),sync.Mutex}
|
||||||
}
|
}
|
||||||
|
|
@ -76,6 +86,7 @@ func (self *Cademlia) Start() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop saves the routing table into a persistant form
|
||||||
func (self *Cademlia) Stop() {
|
func (self *Cademlia) Stop() {
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
|
|
@ -86,10 +97,13 @@ func (self *Cademlia) Stop() {
|
||||||
self.quitC = nil
|
self.quitC = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddPeer is the entry point where new peers are suggested for addition to the peer pool
|
||||||
|
// peers conform to the peerrInfo interface
|
||||||
|
// AddPeer(peer) returns an error if it deems the peer unworthy
|
||||||
func (self *Cademlia) AddPeer(peer peerInfo) (err error) {
|
func (self *Cademlia) AddPeer(peer peerInfo) (err error) {
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
index := self.DistanceTo(peer.Hash())
|
index := self.ProximityBin(peer.Hash())
|
||||||
row := self.rows[index]
|
row := self.rows[index]
|
||||||
added := row.insert(&entry{peer: peer})
|
added := row.insert(&entry{peer: peer})
|
||||||
if added {
|
if added {
|
||||||
|
|
@ -104,35 +118,41 @@ func (self *Cademlia) AddPeer(peer peerInfo) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adjust Prox (proxLimit and proxSize after an insertion of add entries into row r)
|
||||||
func (self *Cademlia) adjustProx(r int, add int) {
|
func (self *Cademlia) adjustProx(r int, add int) {
|
||||||
if self.proxSize+add > self.MaxProxSize &&
|
self.lock.Lock()
|
||||||
|
defer self.lock.Unlock()
|
||||||
|
if r >= self.proxLimit &&
|
||||||
|
self.proxSize+add > self.MaxProxBinSize &&
|
||||||
self.rows[r].len() > 0 {
|
self.rows[r].len() > 0 {
|
||||||
self.proxLimit--
|
self.proxLimit++
|
||||||
} else {
|
} else {
|
||||||
self.proxSize += add
|
self.proxSize += add
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updates Prox (proxLimit and proxSize after purging entries)
|
||||||
func (self *Cademlia) updateProx() {
|
func (self *Cademlia) updateProx() {
|
||||||
var sum, proxSize int
|
|
||||||
for _, r := range self.rows {
|
|
||||||
sum += r.len()
|
|
||||||
if sum <= self.MaxProxSize || r.len() == 0 {
|
|
||||||
proxSize = sum
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
self.proxSize = proxSize
|
defer self.lock.Unlock()
|
||||||
self.lock.Unlock()
|
var sum int
|
||||||
|
for i := self.MaxProx - 1; i >= 0; i-- {
|
||||||
|
l := self.rows[i].len()
|
||||||
|
sum += l
|
||||||
|
if sum <= self.MaxProxBinSize || l == 0 {
|
||||||
|
self.proxSize = sum
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPeers(target) returns the list of peers belonging to the same proximity bin as the target. The most proximate bin will be the union of the bins between proxLimit and MaxProx. proxLimit is dynamically adjusted so that 1) there is no empty rows in bin < proxLimit and 2) the sum of all items are the maximum possible but lower than MaxProxBinSize
|
||||||
func (self *Cademlia) GetPeers(target []byte) (peers []peerInfo) {
|
func (self *Cademlia) GetPeers(target []byte) (peers []peerInfo) {
|
||||||
self.lock.RLock()
|
self.lock.RLock()
|
||||||
defer self.lock.RUnlock()
|
defer self.lock.RUnlock()
|
||||||
index := self.DistanceTo(target)
|
index := self.ProximityBin(target)
|
||||||
var entries []*entry
|
var entries []*entry
|
||||||
if index >= self.proxLimit {
|
if index >= self.proxLimit {
|
||||||
for i := self.proxLimit; i < self.HashBits; i++ {
|
for i := self.proxLimit; i < self.MaxProx; i++ {
|
||||||
entries = append(entries, self.rows[i].row...)
|
entries = append(entries, self.rows[i].row...)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -144,11 +164,13 @@ func (self *Cademlia) GetPeers(target []byte) (peers []peerInfo) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// entry wrapper type for peer object adding potentially persisted metadata for offline permanent record
|
||||||
type entry struct {
|
type entry struct {
|
||||||
peer peerInfo
|
peer peerInfo
|
||||||
// metadata
|
// metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// in situ mutable row
|
||||||
type row struct {
|
type row struct {
|
||||||
length int
|
length int
|
||||||
row []*entry
|
row []*entry
|
||||||
|
|
@ -161,6 +183,7 @@ func (self *row) len() int {
|
||||||
return self.length
|
return self.length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// insert adds a peer to a row either by appending to existing items if row length does not exceed RowLength, or by replacing the worst entry in the row
|
||||||
func (self *row) insert(entry *entry) (added bool) {
|
func (self *row) insert(entry *entry) (added bool) {
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
|
|
@ -176,6 +199,7 @@ func (self *row) insert(entry *entry) (added bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// worst expunges the single worst entry in a row, where worst entry is with a peer that has not been active the longests
|
||||||
func (self *row) worst() (index int) {
|
func (self *row) worst() (index int) {
|
||||||
var oldest time.Time
|
var oldest time.Time
|
||||||
for i, entry := range self.row {
|
for i, entry := range self.row {
|
||||||
|
|
@ -187,6 +211,8 @@ func (self *row) worst() (index int) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// expunges entries from a row that were last active more that MaxAge ago
|
||||||
|
// calls Disconnect on entry.peer
|
||||||
func (self *row) purge(recently time.Time) {
|
func (self *row) purge(recently time.Time) {
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
var newRow []*entry
|
var newRow []*entry
|
||||||
|
|
@ -223,25 +249,39 @@ func (self *Cademlia) purgeLoop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Xor(one, other []byte) (xor []byte) {
|
/*
|
||||||
for i := 0; i < len(one); i++ {
|
Taking the proximity value relative to a fix point x classifies the points in the space (n byte long byte sequences) into bins the items in which are each at most half as distant from x as items in the previous bin. Given a sample of uniformly distrbuted items (a hash function over arbitrary sequence) the proximity scale maps onto series of subsets with cardinalities on a negative exponential scale.
|
||||||
xor[i] = one[i] ^ other[i]
|
|
||||||
}
|
It also has the property that any two item belonging to the same bin are at most half as distant from each other as they are from x.
|
||||||
return
|
|
||||||
|
If we think of random sample of items in the bins as connections in a network of interconnected nodes than relative proximity can serve as the basis for local decisions for graph traversal where the task is to find a route between two points. Since in every step of forwarding, the finite distance halves, there is a guaranteed constant maximum limit on the number of hops needed to reach one node from the other.
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (self *Cademlia) ProximityBin(other []byte) (ret int) {
|
||||||
|
return int(math.Min(float64(self.MaxProx), float64(self.Proximity(self.Hash, other))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Cademlia) DistanceTo(other []byte) (ret int) {
|
/*
|
||||||
return self.Distance(self.Hash, other)
|
The distance metric MSB(x, y) of two equal length bytesequences x an y is the value of the
|
||||||
}
|
binary integer cast of the xor-ed bytesequence most significant bit first.
|
||||||
|
Proximity(x, y) counts the common zeros in the front of this distance measure.
|
||||||
|
*/
|
||||||
|
|
||||||
func (self *Cademlia) Distance(one, other []byte) (ret int) {
|
func (self *Cademlia) Proximity(one, other []byte) (ret int) {
|
||||||
xor := Xor(one, other)
|
xor := Xor(one, other)
|
||||||
for i := 0; i < self.HashBits; i++ {
|
for i := 0; i < self.HashBytes; i++ {
|
||||||
for j := 0; j < 8; j++ {
|
for j := 0; j < 8; j++ {
|
||||||
if (xor[i]>>uint8(7-j))&0x1 != 0 {
|
if (xor[i]>>uint8(7-j))&0x1 != 0 {
|
||||||
return i*8 + j
|
return i*8 + j
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self.HashBits*8 - 1
|
return self.HashBytes*8 - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func Xor(one, other []byte) (xor []byte) {
|
||||||
|
for i := 0; i < len(one); i++ {
|
||||||
|
xor[i] = one[i] ^ other[i]
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue