mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 23:26:44 +00:00
feat:make portal wire out of p2p package
Signed-off-by: Chen Kai <281165273grape@gmail.com>
This commit is contained in:
parent
8bb4364475
commit
71d8f7a842
27 changed files with 5138 additions and 258 deletions
|
|
@ -163,7 +163,7 @@ func discv4Ping(ctx *cli.Context) error {
|
|||
defer disc.Close()
|
||||
|
||||
start := time.Now()
|
||||
if err := disc.Ping(n); err != nil {
|
||||
if err := disc.PingWithoutResp(n); err != nil {
|
||||
return fmt.Errorf("node didn't respond: %v", err)
|
||||
}
|
||||
fmt.Printf("node responded to ping (RTT %v).\n", time.Since(start))
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ func discv5Ping(ctx *cli.Context) error {
|
|||
disc, _ := startV5(ctx)
|
||||
defer disc.Close()
|
||||
|
||||
fmt.Println(disc.Ping(n))
|
||||
fmt.Println(disc.PingWithoutResp(n))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ func (d *DiscV5API) GetEnr(nodeId string) (bool, error) {
|
|||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
n := d.DiscV5.tab.getNode(id)
|
||||
n := d.DiscV5.tab.GetNode(id)
|
||||
if n == nil {
|
||||
return false, errors.New("record not in local routing table")
|
||||
}
|
||||
|
|
@ -128,7 +128,7 @@ func (d *DiscV5API) DeleteEnr(nodeId string) (bool, error) {
|
|||
return false, err
|
||||
}
|
||||
|
||||
n := d.DiscV5.tab.getNode(id)
|
||||
n := d.DiscV5.tab.GetNode(id)
|
||||
if n == nil {
|
||||
return false, errors.New("record not in local routing table")
|
||||
}
|
||||
|
|
@ -161,7 +161,7 @@ func (d *DiscV5API) Ping(enr string) (*DiscV5PongResp, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
pong, err := d.DiscV5.pingInner(n)
|
||||
pong, err := d.DiscV5.PingWithResp(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -178,7 +178,7 @@ func (d *DiscV5API) FindNodes(enr string, distances []uint) ([]string, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
findNodes, err := d.DiscV5.findnode(n, distances)
|
||||
findNodes, err := d.DiscV5.Findnode(n, distances)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -283,7 +283,7 @@ func (p *PortalProtocolAPI) GetEnr(nodeId string) (string, error) {
|
|||
return p.portalProtocol.localNode.Node().String(), nil
|
||||
}
|
||||
|
||||
n := p.portalProtocol.table.getNode(id)
|
||||
n := p.portalProtocol.table.GetNode(id)
|
||||
if n == nil {
|
||||
return "", errors.New("record not in local routing table")
|
||||
}
|
||||
|
|
@ -297,7 +297,7 @@ func (p *PortalProtocolAPI) DeleteEnr(nodeId string) (bool, error) {
|
|||
return false, err
|
||||
}
|
||||
|
||||
n := p.portalProtocol.table.getNode(id)
|
||||
n := p.portalProtocol.table.GetNode(id)
|
||||
if n == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,30 +24,30 @@ import (
|
|||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
)
|
||||
|
||||
// lookup performs a network search for nodes close to the given target. It approaches the
|
||||
// Lookup performs a network search for nodes close to the given target. It approaches the
|
||||
// target by querying nodes that are closer to it on each iteration. The given target does
|
||||
// not need to be an actual node identifier.
|
||||
type lookup struct {
|
||||
type Lookup struct {
|
||||
tab *Table
|
||||
queryfunc queryFunc
|
||||
replyCh chan []*enode.Node
|
||||
cancelCh <-chan struct{}
|
||||
asked, seen map[enode.ID]bool
|
||||
result nodesByDistance
|
||||
result NodesByDistance
|
||||
replyBuffer []*enode.Node
|
||||
queries int
|
||||
}
|
||||
|
||||
type queryFunc func(*enode.Node) ([]*enode.Node, error)
|
||||
|
||||
func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *lookup {
|
||||
it := &lookup{
|
||||
func NewLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *Lookup {
|
||||
it := &Lookup{
|
||||
tab: tab,
|
||||
queryfunc: q,
|
||||
asked: make(map[enode.ID]bool),
|
||||
seen: make(map[enode.ID]bool),
|
||||
result: nodesByDistance{target: target},
|
||||
replyCh: make(chan []*enode.Node, alpha),
|
||||
result: NodesByDistance{Target: target},
|
||||
replyCh: make(chan []*enode.Node, Alpha),
|
||||
cancelCh: ctx.Done(),
|
||||
queries: -1,
|
||||
}
|
||||
|
|
@ -57,16 +57,16 @@ func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *l
|
|||
return it
|
||||
}
|
||||
|
||||
// run runs the lookup to completion and returns the closest nodes found.
|
||||
func (it *lookup) run() []*enode.Node {
|
||||
// Run runs the lookup to completion and returns the closest nodes found.
|
||||
func (it *Lookup) Run() []*enode.Node {
|
||||
for it.advance() {
|
||||
}
|
||||
return it.result.entries
|
||||
return it.result.Entries
|
||||
}
|
||||
|
||||
// advance advances the lookup until any new nodes have been found.
|
||||
// It returns false when the lookup has ended.
|
||||
func (it *lookup) advance() bool {
|
||||
func (it *Lookup) advance() bool {
|
||||
for it.startQueries() {
|
||||
select {
|
||||
case nodes := <-it.replyCh:
|
||||
|
|
@ -74,7 +74,7 @@ func (it *lookup) advance() bool {
|
|||
for _, n := range nodes {
|
||||
if n != nil && !it.seen[n.ID()] {
|
||||
it.seen[n.ID()] = true
|
||||
it.result.push(n, bucketSize)
|
||||
it.result.Push(n, BucketSize)
|
||||
it.replyBuffer = append(it.replyBuffer, n)
|
||||
}
|
||||
}
|
||||
|
|
@ -89,7 +89,7 @@ func (it *lookup) advance() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (it *lookup) shutdown() {
|
||||
func (it *Lookup) shutdown() {
|
||||
for it.queries > 0 {
|
||||
<-it.replyCh
|
||||
it.queries--
|
||||
|
|
@ -98,28 +98,28 @@ func (it *lookup) shutdown() {
|
|||
it.replyBuffer = nil
|
||||
}
|
||||
|
||||
func (it *lookup) startQueries() bool {
|
||||
func (it *Lookup) startQueries() bool {
|
||||
if it.queryfunc == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// The first query returns nodes from the local table.
|
||||
if it.queries == -1 {
|
||||
closest := it.tab.findnodeByID(it.result.target, bucketSize, false)
|
||||
closest := it.tab.FindnodeByID(it.result.Target, BucketSize, false)
|
||||
// Avoid finishing the lookup too quickly if table is empty. It'd be better to wait
|
||||
// for the table to fill in this case, but there is no good mechanism for that
|
||||
// yet.
|
||||
if len(closest.entries) == 0 {
|
||||
if len(closest.Entries) == 0 {
|
||||
it.slowdown()
|
||||
}
|
||||
it.queries = 1
|
||||
it.replyCh <- closest.entries
|
||||
it.replyCh <- closest.Entries
|
||||
return true
|
||||
}
|
||||
|
||||
// Ask the closest nodes that we haven't asked yet.
|
||||
for i := 0; i < len(it.result.entries) && it.queries < alpha; i++ {
|
||||
n := it.result.entries[i]
|
||||
for i := 0; i < len(it.result.Entries) && it.queries < Alpha; i++ {
|
||||
n := it.result.Entries[i]
|
||||
if !it.asked[n.ID()] {
|
||||
it.asked[n.ID()] = true
|
||||
it.queries++
|
||||
|
|
@ -130,7 +130,7 @@ func (it *lookup) startQueries() bool {
|
|||
return it.queries > 0
|
||||
}
|
||||
|
||||
func (it *lookup) slowdown() {
|
||||
func (it *Lookup) slowdown() {
|
||||
sleep := time.NewTimer(1 * time.Second)
|
||||
defer sleep.Stop()
|
||||
select {
|
||||
|
|
@ -139,9 +139,9 @@ func (it *lookup) slowdown() {
|
|||
}
|
||||
}
|
||||
|
||||
func (it *lookup) query(n *enode.Node, reply chan<- []*enode.Node) {
|
||||
func (it *Lookup) query(n *enode.Node, reply chan<- []*enode.Node) {
|
||||
r, err := it.queryfunc(n)
|
||||
if !errors.Is(err, errClosed) { // avoid recording failures on shutdown.
|
||||
if !errors.Is(err, ErrClosed) { // avoid recording failures on shutdown.
|
||||
success := len(r) > 0
|
||||
it.tab.trackRequest(n, success, r)
|
||||
if err != nil {
|
||||
|
|
@ -158,10 +158,10 @@ type lookupIterator struct {
|
|||
nextLookup lookupFunc
|
||||
ctx context.Context
|
||||
cancel func()
|
||||
lookup *lookup
|
||||
lookup *Lookup
|
||||
}
|
||||
|
||||
type lookupFunc func(ctx context.Context) *lookup
|
||||
type lookupFunc func(ctx context.Context) *Lookup
|
||||
|
||||
func newLookupIterator(ctx context.Context, next lookupFunc) *lookupIterator {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
|
|
|||
|
|
@ -54,27 +54,27 @@ func (n *tableNode) String() string {
|
|||
return n.Node.String()
|
||||
}
|
||||
|
||||
// nodesByDistance is a list of nodes, ordered by distance to target.
|
||||
type nodesByDistance struct {
|
||||
entries []*enode.Node
|
||||
target enode.ID
|
||||
// NodesByDistance is a list of nodes, ordered by distance to target.
|
||||
type NodesByDistance struct {
|
||||
Entries []*enode.Node
|
||||
Target enode.ID
|
||||
}
|
||||
|
||||
// push adds the given node to the list, keeping the total size below maxElems.
|
||||
func (h *nodesByDistance) push(n *enode.Node, maxElems int) {
|
||||
ix := sort.Search(len(h.entries), func(i int) bool {
|
||||
return enode.DistCmp(h.target, h.entries[i].ID(), n.ID()) > 0
|
||||
// Push adds the given node to the list, keeping the total size below maxElems.
|
||||
func (h *NodesByDistance) Push(n *enode.Node, maxElems int) {
|
||||
ix := sort.Search(len(h.Entries), func(i int) bool {
|
||||
return enode.DistCmp(h.Target, h.Entries[i].ID(), n.ID()) > 0
|
||||
})
|
||||
|
||||
end := len(h.entries)
|
||||
if len(h.entries) < maxElems {
|
||||
h.entries = append(h.entries, n)
|
||||
end := len(h.Entries)
|
||||
if len(h.Entries) < maxElems {
|
||||
h.Entries = append(h.Entries, n)
|
||||
}
|
||||
if ix < end {
|
||||
// Slide existing entries down to make room.
|
||||
// This will overwrite the entry we just appended.
|
||||
copy(h.entries[ix+1:], h.entries[ix:])
|
||||
h.entries[ix] = n
|
||||
copy(h.Entries[ix+1:], h.Entries[ix:])
|
||||
h.Entries[ix] = n
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ func (p *PortalProtocol) Start() error {
|
|||
return err
|
||||
}
|
||||
|
||||
go p.table.loop()
|
||||
go p.table.Loop()
|
||||
|
||||
for i := 0; i < concurrentOffers; i++ {
|
||||
go p.offerWorker()
|
||||
|
|
@ -269,7 +269,7 @@ func (p *PortalProtocol) Start() error {
|
|||
|
||||
func (p *PortalProtocol) Stop() {
|
||||
p.cancelCloseCtx()
|
||||
p.table.close()
|
||||
p.table.Close()
|
||||
p.DiscV5.Close()
|
||||
if p.Utp != nil {
|
||||
p.Utp.Stop()
|
||||
|
|
@ -335,7 +335,7 @@ func (p *PortalProtocol) setupDiscV5AndTable() error {
|
|||
Log: p.Log,
|
||||
}
|
||||
|
||||
p.table, err = newTable(p, p.localNode.Database(), cfg)
|
||||
p.table, err = NewTable(p, p.localNode.Database(), cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -343,7 +343,7 @@ func (p *PortalProtocol) setupDiscV5AndTable() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *PortalProtocol) ping(node *enode.Node) (uint64, error) {
|
||||
func (p *PortalProtocol) Ping(node *enode.Node) (uint64, error) {
|
||||
pong, err := p.pingInner(node)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
|
@ -515,7 +515,7 @@ func (p *PortalProtocol) processOffer(target *enode.Node, resp []byte, request *
|
|||
if metrics.Enabled {
|
||||
p.portalMetrics.messagesReceivedAccept.Mark(1)
|
||||
}
|
||||
isAdded := p.table.addFoundNode(target, true)
|
||||
isAdded := p.table.AddFoundNode(target, true)
|
||||
if isAdded {
|
||||
log.Debug("Node added to bucket", "protocol", p.protocolName, "node", target.IP(), "port", target.UDP())
|
||||
} else {
|
||||
|
|
@ -651,7 +651,7 @@ func (p *PortalProtocol) processContent(target *enode.Node, resp []byte) (byte,
|
|||
if metrics.Enabled {
|
||||
p.portalMetrics.messagesReceivedContent.Mark(1)
|
||||
}
|
||||
isAdded := p.table.addFoundNode(target, true)
|
||||
isAdded := p.table.AddFoundNode(target, true)
|
||||
if isAdded {
|
||||
log.Debug("Node added to bucket", "protocol", p.protocolName, "node", target.IP(), "port", target.UDP())
|
||||
} else {
|
||||
|
|
@ -669,7 +669,7 @@ func (p *PortalProtocol) processContent(target *enode.Node, resp []byte) (byte,
|
|||
if metrics.Enabled {
|
||||
p.portalMetrics.messagesReceivedContent.Mark(1)
|
||||
}
|
||||
isAdded := p.table.addFoundNode(target, true)
|
||||
isAdded := p.table.AddFoundNode(target, true)
|
||||
if isAdded {
|
||||
log.Debug("Node added to bucket", "protocol", p.protocolName, "node", target.IP(), "port", target.UDP())
|
||||
} else {
|
||||
|
|
@ -729,7 +729,7 @@ func (p *PortalProtocol) processContent(target *enode.Node, resp []byte) (byte,
|
|||
if metrics.Enabled {
|
||||
p.portalMetrics.messagesReceivedContent.Mark(1)
|
||||
}
|
||||
isAdded := p.table.addFoundNode(target, true)
|
||||
isAdded := p.table.AddFoundNode(target, true)
|
||||
if isAdded {
|
||||
log.Debug("Node added to bucket", "protocol", p.protocolName, "node", target.IP(), "port", target.UDP())
|
||||
} else {
|
||||
|
|
@ -757,7 +757,7 @@ func (p *PortalProtocol) processNodes(target *enode.Node, resp []byte, distances
|
|||
return nil, err
|
||||
}
|
||||
|
||||
isAdded := p.table.addFoundNode(target, true)
|
||||
isAdded := p.table.AddFoundNode(target, true)
|
||||
if isAdded {
|
||||
log.Debug("Node added to bucket", "protocol", p.protocolName, "node", target.IP(), "port", target.UDP())
|
||||
} else {
|
||||
|
|
@ -828,7 +828,7 @@ func (p *PortalProtocol) processPong(target *enode.Node, resp []byte) (*portalwi
|
|||
if metrics.Enabled {
|
||||
p.portalMetrics.messagesReceivedPong.Mark(1)
|
||||
}
|
||||
isAdded := p.table.addFoundNode(target, true)
|
||||
isAdded := p.table.AddFoundNode(target, true)
|
||||
if isAdded {
|
||||
log.Debug("Node added to bucket", "protocol", p.protocolName, "node", target.IP(), "port", target.UDP())
|
||||
} else {
|
||||
|
|
@ -840,8 +840,8 @@ func (p *PortalProtocol) processPong(target *enode.Node, resp []byte) (*portalwi
|
|||
}
|
||||
|
||||
func (p *PortalProtocol) handleTalkRequest(id enode.ID, addr *net.UDPAddr, msg []byte) []byte {
|
||||
if n := p.DiscV5.getNode(id); n != nil {
|
||||
p.table.addInboundNode(n)
|
||||
if n := p.DiscV5.GetNode(id); n != nil {
|
||||
p.table.AddInboundNode(n)
|
||||
}
|
||||
|
||||
msgCode := msg[0]
|
||||
|
|
@ -1377,7 +1377,7 @@ func (p *PortalProtocol) verifyResponseNode(sender *enode.Node, r *enr.Record, d
|
|||
return nil, errors.New("not contained in netrestrict list")
|
||||
}
|
||||
if n.UDP() <= 1024 {
|
||||
return nil, errLowPort
|
||||
return nil, ErrLowPort
|
||||
}
|
||||
if distances != nil {
|
||||
nd := enode.LogDist(sender.ID(), n.ID())
|
||||
|
|
@ -1394,24 +1394,24 @@ func (p *PortalProtocol) verifyResponseNode(sender *enode.Node, r *enr.Record, d
|
|||
|
||||
// lookupRandom looks up a random target.
|
||||
// This is needed to satisfy the transport interface.
|
||||
func (p *PortalProtocol) lookupRandom() []*enode.Node {
|
||||
return p.newRandomLookup(p.closeCtx).run()
|
||||
func (p *PortalProtocol) LookupRandom() []*enode.Node {
|
||||
return p.newRandomLookup(p.closeCtx).Run()
|
||||
}
|
||||
|
||||
// lookupSelf looks up our own node ID.
|
||||
// This is needed to satisfy the transport interface.
|
||||
func (p *PortalProtocol) lookupSelf() []*enode.Node {
|
||||
return p.newLookup(p.closeCtx, p.Self().ID()).run()
|
||||
func (p *PortalProtocol) LookupSelf() []*enode.Node {
|
||||
return p.newLookup(p.closeCtx, p.Self().ID()).Run()
|
||||
}
|
||||
|
||||
func (p *PortalProtocol) newRandomLookup(ctx context.Context) *lookup {
|
||||
func (p *PortalProtocol) newRandomLookup(ctx context.Context) *Lookup {
|
||||
var target enode.ID
|
||||
_, _ = crand.Read(target[:])
|
||||
return p.newLookup(ctx, target)
|
||||
}
|
||||
|
||||
func (p *PortalProtocol) newLookup(ctx context.Context, target enode.ID) *lookup {
|
||||
return newLookup(ctx, p.table, target, func(n *enode.Node) ([]*enode.Node, error) {
|
||||
func (p *PortalProtocol) newLookup(ctx context.Context, target enode.ID) *Lookup {
|
||||
return NewLookup(ctx, p.table, target, func(n *enode.Node) ([]*enode.Node, error) {
|
||||
return p.lookupWorker(n, target)
|
||||
})
|
||||
}
|
||||
|
|
@ -1419,28 +1419,28 @@ func (p *PortalProtocol) newLookup(ctx context.Context, target enode.ID) *lookup
|
|||
// lookupWorker performs FINDNODE calls against a single node during lookup.
|
||||
func (p *PortalProtocol) lookupWorker(destNode *enode.Node, target enode.ID) ([]*enode.Node, error) {
|
||||
var (
|
||||
dists = lookupDistances(target, destNode.ID())
|
||||
nodes = nodesByDistance{target: target}
|
||||
dists = LookupDistances(target, destNode.ID())
|
||||
nodes = NodesByDistance{Target: target}
|
||||
err error
|
||||
)
|
||||
var r []*enode.Node
|
||||
|
||||
r, err = p.findNodes(destNode, dists)
|
||||
if errors.Is(err, errClosed) {
|
||||
if errors.Is(err, ErrClosed) {
|
||||
return nil, err
|
||||
}
|
||||
for _, n := range r {
|
||||
if n.ID() != p.Self().ID() {
|
||||
isAdded := p.table.addFoundNode(n, false)
|
||||
isAdded := p.table.AddFoundNode(n, false)
|
||||
if isAdded {
|
||||
log.Debug("Node added to bucket", "protocol", p.protocolName, "node", n.IP(), "port", n.UDP())
|
||||
} else {
|
||||
log.Debug("Node added to replacements list", "protocol", p.protocolName, "node", n.IP(), "port", n.UDP())
|
||||
}
|
||||
nodes.push(n, portalFindnodesResultLimit)
|
||||
nodes.Push(n, portalFindnodesResultLimit)
|
||||
}
|
||||
}
|
||||
return nodes.entries, err
|
||||
return nodes.Entries, err
|
||||
}
|
||||
|
||||
func (p *PortalProtocol) offerWorker() {
|
||||
|
|
@ -1496,13 +1496,13 @@ func (p *PortalProtocol) findNodesCloseToContent(contentId []byte, limit int) []
|
|||
// Lookup performs a recursive lookup for the given target.
|
||||
// It returns the closest nodes to target.
|
||||
func (p *PortalProtocol) Lookup(target enode.ID) []*enode.Node {
|
||||
return p.newLookup(p.closeCtx, target).run()
|
||||
return p.newLookup(p.closeCtx, target).Run()
|
||||
}
|
||||
|
||||
// Resolve searches for a specific Node with the given ID and tries to get the most recent
|
||||
// version of the Node record for it. It returns n if the Node could not be resolved.
|
||||
func (p *PortalProtocol) Resolve(n *enode.Node) *enode.Node {
|
||||
if intable := p.table.getNode(n.ID()); intable != nil && intable.Seq() > n.Seq() {
|
||||
if intable := p.table.GetNode(n.ID()); intable != nil && intable.Seq() > n.Seq() {
|
||||
n = intable
|
||||
}
|
||||
// Try asking directly. This works if the Node is still responding on the endpoint we have.
|
||||
|
|
@ -1527,7 +1527,7 @@ func (p *PortalProtocol) ResolveNodeId(id enode.ID) *enode.Node {
|
|||
return p.Self()
|
||||
}
|
||||
|
||||
n := p.table.getNode(id)
|
||||
n := p.table.GetNode(id)
|
||||
if n != nil {
|
||||
p.Log.Debug("found Id in table and will request enr from the node", "id", id.String())
|
||||
// Try asking directly. This works if the Node is still responding on the endpoint we have.
|
||||
|
|
@ -1564,7 +1564,7 @@ func (p *PortalProtocol) collectTableNodes(rip net.IP, distances []uint, limit i
|
|||
processed[dist] = struct{}{}
|
||||
|
||||
checkLive := !p.table.cfg.NoFindnodeLivenessCheck
|
||||
for _, n := range p.table.appendBucketNodes(dist, bn[:0], checkLive) {
|
||||
for _, n := range p.table.AppendBucketNodes(dist, bn[:0], checkLive) {
|
||||
// Apply some pre-checks to avoid sending invalid nodes.
|
||||
// Note liveness is checked by appendLiveNodes.
|
||||
if netutil.CheckRelayIP(rip, n.IP()) != nil {
|
||||
|
|
@ -1582,7 +1582,7 @@ func (p *PortalProtocol) collectTableNodes(rip net.IP, distances []uint, limit i
|
|||
func (p *PortalProtocol) ContentLookup(contentKey, contentId []byte) ([]byte, bool, error) {
|
||||
lookupContext, cancel := context.WithCancel(context.Background())
|
||||
|
||||
resChan := make(chan *traceContentInfoResp, alpha)
|
||||
resChan := make(chan *traceContentInfoResp, Alpha)
|
||||
hasResult := int32(0)
|
||||
|
||||
result := ContentInfoResp{}
|
||||
|
|
@ -1600,9 +1600,9 @@ func (p *PortalProtocol) ContentLookup(contentKey, contentId []byte) ([]byte, bo
|
|||
}
|
||||
}()
|
||||
|
||||
newLookup(lookupContext, p.table, enode.ID(contentId), func(n *enode.Node) ([]*enode.Node, error) {
|
||||
NewLookup(lookupContext, p.table, enode.ID(contentId), func(n *enode.Node) ([]*enode.Node, error) {
|
||||
return p.contentLookupWorker(n, contentKey, resChan, cancel, &hasResult)
|
||||
}).run()
|
||||
}).Run()
|
||||
close(resChan)
|
||||
|
||||
wg.Wait()
|
||||
|
|
@ -1616,7 +1616,7 @@ func (p *PortalProtocol) ContentLookup(contentKey, contentId []byte) ([]byte, bo
|
|||
func (p *PortalProtocol) TraceContentLookup(contentKey, contentId []byte) (*TraceContentResult, error) {
|
||||
lookupContext, cancel := context.WithCancel(context.Background())
|
||||
// resp channel
|
||||
resChan := make(chan *traceContentInfoResp, alpha)
|
||||
resChan := make(chan *traceContentInfoResp, Alpha)
|
||||
|
||||
hasResult := int32(0)
|
||||
|
||||
|
|
@ -1633,10 +1633,10 @@ func (p *PortalProtocol) TraceContentLookup(contentKey, contentId []byte) (*Trac
|
|||
Cancelled: make([]string, 0),
|
||||
}
|
||||
|
||||
nodes := p.table.findnodeByID(enode.ID(contentId), bucketSize, false)
|
||||
nodes := p.table.FindnodeByID(enode.ID(contentId), BucketSize, false)
|
||||
|
||||
localResponse := make([]string, 0, len(nodes.entries))
|
||||
for _, node := range nodes.entries {
|
||||
localResponse := make([]string, 0, len(nodes.Entries))
|
||||
for _, node := range nodes.Entries {
|
||||
id := "0x" + node.ID().String()
|
||||
localResponse = append(localResponse, id)
|
||||
}
|
||||
|
|
@ -1698,10 +1698,10 @@ func (p *PortalProtocol) TraceContentLookup(contentKey, contentId []byte) (*Trac
|
|||
}
|
||||
}()
|
||||
|
||||
lookup := newLookup(lookupContext, p.table, enode.ID(contentId), func(n *enode.Node) ([]*enode.Node, error) {
|
||||
lookup := NewLookup(lookupContext, p.table, enode.ID(contentId), func(n *enode.Node) ([]*enode.Node, error) {
|
||||
return p.contentLookupWorker(n, contentKey, resChan, cancel, &hasResult)
|
||||
})
|
||||
lookup.run()
|
||||
lookup.Run()
|
||||
close(resChan)
|
||||
|
||||
wg.Wait()
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ func (p *PortalUtp) packetRouterFunc(buf []byte, id enode.ID, addr *net.UDPAddr)
|
|||
if n, ok := p.discV5.GetCachedNode(addr.String()); ok {
|
||||
//_, err := p.DiscV5.TalkRequestToID(id, addr, string(portalwire.UTPNetwork), buf)
|
||||
req := &v5wire.TalkRequest{Protocol: string(portalwire.Utp), Message: buf}
|
||||
p.discV5.sendFromAnotherThreadWithNode(n, netip.AddrPortFrom(netutil.IPToAddr(addr.IP), uint16(addr.Port)), req)
|
||||
p.discV5.SendFromAnotherThreadWithNode(n, netip.AddrPortFrom(netutil.IPToAddr(addr.IP), uint16(addr.Port)), req)
|
||||
|
||||
return len(buf), nil
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
alpha = 3 // Kademlia concurrency factor
|
||||
bucketSize = 16 // Kademlia bucket size
|
||||
Alpha = 3 // Kademlia concurrency factor
|
||||
BucketSize = 16 // Kademlia bucket size
|
||||
maxReplacements = 10 // Size of per-bucket replacement list
|
||||
|
||||
// We keep buckets for the upper 1/15 of distances because
|
||||
|
|
@ -92,9 +92,9 @@ type Table struct {
|
|||
type transport interface {
|
||||
Self() *enode.Node
|
||||
RequestENR(*enode.Node) (*enode.Node, error)
|
||||
lookupRandom() []*enode.Node
|
||||
lookupSelf() []*enode.Node
|
||||
ping(*enode.Node) (seq uint64, err error)
|
||||
LookupRandom() []*enode.Node
|
||||
LookupSelf() []*enode.Node
|
||||
Ping(*enode.Node) (seq uint64, err error)
|
||||
}
|
||||
|
||||
// bucket contains nodes, ordered by their last activity. the entry
|
||||
|
|
@ -118,7 +118,7 @@ type trackRequestOp struct {
|
|||
success bool
|
||||
}
|
||||
|
||||
func newTable(t transport, db *enode.DB, cfg Config) (*Table, error) {
|
||||
func NewTable(t transport, db *enode.DB, cfg Config) (*Table, error) {
|
||||
cfg = cfg.withDefaults()
|
||||
tab := &Table{
|
||||
net: t,
|
||||
|
|
@ -196,8 +196,8 @@ func (tab *Table) self() *enode.Node {
|
|||
return tab.net.Self()
|
||||
}
|
||||
|
||||
// getNode returns the node with the given ID or nil if it isn't in the table.
|
||||
func (tab *Table) getNode(id enode.ID) *enode.Node {
|
||||
// GetNode returns the node with the given ID or nil if it isn't in the table.
|
||||
func (tab *Table) GetNode(id enode.ID) *enode.Node {
|
||||
tab.mutex.Lock()
|
||||
defer tab.mutex.Unlock()
|
||||
|
||||
|
|
@ -210,8 +210,8 @@ func (tab *Table) getNode(id enode.ID) *enode.Node {
|
|||
return nil
|
||||
}
|
||||
|
||||
// close terminates the network listener and flushes the node database.
|
||||
func (tab *Table) close() {
|
||||
// Close terminates the network listener and flushes the node database.
|
||||
func (tab *Table) Close() {
|
||||
close(tab.closeReq)
|
||||
<-tab.closed
|
||||
}
|
||||
|
|
@ -255,40 +255,40 @@ func (tab *Table) refresh() <-chan struct{} {
|
|||
return done
|
||||
}
|
||||
|
||||
// findnodeByID returns the n nodes in the table that are closest to the given id.
|
||||
// FindnodeByID returns the n nodes in the table that are closest to the given id.
|
||||
// This is used by the FINDNODE/v4 handler.
|
||||
//
|
||||
// The preferLive parameter says whether the caller wants liveness-checked results. If
|
||||
// preferLive is true and the table contains any verified nodes, the result will not
|
||||
// contain unverified nodes. However, if there are no verified nodes at all, the result
|
||||
// will contain unverified nodes.
|
||||
func (tab *Table) findnodeByID(target enode.ID, nresults int, preferLive bool) *nodesByDistance {
|
||||
func (tab *Table) FindnodeByID(target enode.ID, nresults int, preferLive bool) *NodesByDistance {
|
||||
tab.mutex.Lock()
|
||||
defer tab.mutex.Unlock()
|
||||
|
||||
// Scan all buckets. There might be a better way to do this, but there aren't that many
|
||||
// buckets, so this solution should be fine. The worst-case complexity of this loop
|
||||
// is O(tab.len() * nresults).
|
||||
nodes := &nodesByDistance{target: target}
|
||||
liveNodes := &nodesByDistance{target: target}
|
||||
nodes := &NodesByDistance{Target: target}
|
||||
liveNodes := &NodesByDistance{Target: target}
|
||||
for _, b := range &tab.buckets {
|
||||
for _, n := range b.entries {
|
||||
nodes.push(n.Node, nresults)
|
||||
nodes.Push(n.Node, nresults)
|
||||
if preferLive && n.isValidatedLive {
|
||||
liveNodes.push(n.Node, nresults)
|
||||
liveNodes.Push(n.Node, nresults)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if preferLive && len(liveNodes.entries) > 0 {
|
||||
if preferLive && len(liveNodes.Entries) > 0 {
|
||||
return liveNodes
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
// appendBucketNodes adds nodes at the given distance to the result slice.
|
||||
// AppendBucketNodes adds nodes at the given distance to the result slice.
|
||||
// This is used by the FINDNODE/v5 handler.
|
||||
func (tab *Table) appendBucketNodes(dist uint, result []*enode.Node, checkLive bool) []*enode.Node {
|
||||
func (tab *Table) AppendBucketNodes(dist uint, result []*enode.Node, checkLive bool) []*enode.Node {
|
||||
if dist > 256 {
|
||||
return result
|
||||
}
|
||||
|
|
@ -322,12 +322,12 @@ func (tab *Table) len() (n int) {
|
|||
return n
|
||||
}
|
||||
|
||||
// addFoundNode adds a node which may not be live. If the bucket has space available,
|
||||
// AddFoundNode adds a node which may not be live. If the bucket has space available,
|
||||
// adding the node succeeds immediately. Otherwise, the node is added to the replacements
|
||||
// list.
|
||||
//
|
||||
// The caller must not hold tab.mutex.
|
||||
func (tab *Table) addFoundNode(n *enode.Node, forceSetLive bool) bool {
|
||||
func (tab *Table) AddFoundNode(n *enode.Node, forceSetLive bool) bool {
|
||||
op := addNodeOp{node: n, isInbound: false, forceSetLive: forceSetLive}
|
||||
select {
|
||||
case tab.addNodeCh <- op:
|
||||
|
|
@ -337,7 +337,7 @@ func (tab *Table) addFoundNode(n *enode.Node, forceSetLive bool) bool {
|
|||
}
|
||||
}
|
||||
|
||||
// addInboundNode adds a node from an inbound contact. If the bucket has no space, the
|
||||
// AddInboundNode adds a node from an inbound contact. If the bucket has no space, the
|
||||
// node is added to the replacements list.
|
||||
//
|
||||
// There is an additional safety measure: if the table is still initializing the node is
|
||||
|
|
@ -345,7 +345,7 @@ func (tab *Table) addFoundNode(n *enode.Node, forceSetLive bool) bool {
|
|||
// repeatedly.
|
||||
//
|
||||
// The caller must not hold tab.mutex.
|
||||
func (tab *Table) addInboundNode(n *enode.Node) bool {
|
||||
func (tab *Table) AddInboundNode(n *enode.Node) bool {
|
||||
op := addNodeOp{node: n, isInbound: true}
|
||||
select {
|
||||
case tab.addNodeCh <- op:
|
||||
|
|
@ -363,8 +363,8 @@ func (tab *Table) trackRequest(n *enode.Node, success bool, foundNodes []*enode.
|
|||
}
|
||||
}
|
||||
|
||||
// loop is the main loop of Table.
|
||||
func (tab *Table) loop() {
|
||||
// Loop is the main loop of Table.
|
||||
func (tab *Table) Loop() {
|
||||
var (
|
||||
refresh = time.NewTimer(tab.nextRefreshTime())
|
||||
refreshDone = make(chan struct{}) // where doRefresh reports completion
|
||||
|
|
@ -447,7 +447,7 @@ func (tab *Table) doRefresh(done chan struct{}) {
|
|||
tab.loadSeedNodes()
|
||||
|
||||
// Run self lookup to discover new neighbor nodes.
|
||||
tab.net.lookupSelf()
|
||||
tab.net.LookupSelf()
|
||||
|
||||
// The Kademlia paper specifies that the bucket refresh should
|
||||
// perform a lookup in the least recently used bucket. We cannot
|
||||
|
|
@ -456,7 +456,7 @@ func (tab *Table) doRefresh(done chan struct{}) {
|
|||
// sha3 preimage that falls into a chosen bucket.
|
||||
// We perform a few lookups with a random target instead.
|
||||
for i := 0; i < 3; i++ {
|
||||
tab.net.lookupRandom()
|
||||
tab.net.LookupRandom()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -542,7 +542,7 @@ func (tab *Table) handleAddNode(req addNodeOp) bool {
|
|||
tab.log.Debug("the node is already in table", "id", req.node.ID())
|
||||
return false
|
||||
}
|
||||
if len(b.entries) >= bucketSize {
|
||||
if len(b.entries) >= BucketSize {
|
||||
// Bucket full, maybe add as replacement.
|
||||
tab.log.Debug("the bucket is full and will add in replacement", "id", req.node.ID())
|
||||
tab.addReplacement(b, req.node)
|
||||
|
|
@ -697,7 +697,7 @@ func (tab *Table) handleTrackRequest(op trackRequestOp) {
|
|||
// many times, but only if there are enough other nodes in the bucket. This latter
|
||||
// condition specifically exists to make bootstrapping in smaller test networks more
|
||||
// reliable.
|
||||
if fails >= maxFindnodeFailures && len(b.entries) >= bucketSize/4 {
|
||||
if fails >= maxFindnodeFailures && len(b.entries) >= BucketSize/4 {
|
||||
tab.deleteInBucket(b, op.node.ID())
|
||||
}
|
||||
|
||||
|
|
@ -717,3 +717,32 @@ func pushNode(list []*tableNode, n *tableNode, max int) ([]*tableNode, *tableNod
|
|||
list[0] = n
|
||||
return list, removed
|
||||
}
|
||||
|
||||
func (tab *Table) WaitInit() {
|
||||
<-tab.initDone
|
||||
}
|
||||
|
||||
func (tab *Table) NodeIds() [][]string {
|
||||
tab.mutex.Lock()
|
||||
defer tab.mutex.Unlock()
|
||||
nodes := make([][]string, 0)
|
||||
for _, b := range &tab.buckets {
|
||||
bucketNodes := make([]string, 0)
|
||||
for _, n := range b.entries {
|
||||
bucketNodes = append(bucketNodes, "0x"+n.ID().String())
|
||||
}
|
||||
nodes = append(nodes, bucketNodes)
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
func (tab *Table) Config() Config {
|
||||
return tab.cfg
|
||||
}
|
||||
|
||||
func (tab *Table) DeleteNode(n *enode.Node) {
|
||||
tab.mutex.Lock()
|
||||
defer tab.mutex.Unlock()
|
||||
b := tab.bucket(n.ID())
|
||||
tab.deleteInBucket(b, n.ID())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ func (tr *tableRevalidation) startRequest(tab *Table, n *tableNode) {
|
|||
|
||||
func (tab *Table) doRevalidate(resp revalidationResponse, node *enode.Node) {
|
||||
// Ping the selected node and wait for a pong response.
|
||||
remoteSeq, err := tab.net.ping(node)
|
||||
remoteSeq, err := tab.net.Ping(node)
|
||||
resp.didRespond = err == nil
|
||||
|
||||
// Also fetch record if the node replied and returned a higher sequence number.
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ func TestRevalidation_nodeRemoved(t *testing.T) {
|
|||
tr.handleResponse(tab, resp)
|
||||
|
||||
// Ensure the node was not re-added to the table.
|
||||
if tab.getNode(node.ID()) != nil {
|
||||
if tab.GetNode(node.ID()) != nil {
|
||||
t.Fatal("node was re-added to Table")
|
||||
}
|
||||
if tr.fast.contains(node.ID()) || tr.slow.contains(node.ID()) {
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding
|
|||
Log: testlog.Logger(t, log.LevelTrace),
|
||||
})
|
||||
defer db.Close()
|
||||
defer tab.close()
|
||||
defer tab.Close()
|
||||
|
||||
<-tab.initDone
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding
|
|||
transport.dead[replacementNode.ID()] = !newNodeIsResponding
|
||||
|
||||
// Add replacement node to table.
|
||||
tab.addFoundNode(replacementNode, false)
|
||||
tab.AddFoundNode(replacementNode, false)
|
||||
|
||||
t.Log("last:", last.ID())
|
||||
t.Log("replacement:", replacementNode.ID())
|
||||
|
|
@ -108,7 +108,7 @@ func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding
|
|||
// Check bucket content.
|
||||
tab.mutex.Lock()
|
||||
defer tab.mutex.Unlock()
|
||||
wantSize := bucketSize
|
||||
wantSize := BucketSize
|
||||
if !lastInBucketIsResponding && !newNodeIsResponding {
|
||||
wantSize--
|
||||
}
|
||||
|
|
@ -150,11 +150,11 @@ func TestTable_IPLimit(t *testing.T) {
|
|||
transport := newPingRecorder()
|
||||
tab, db := newTestTable(transport, Config{})
|
||||
defer db.Close()
|
||||
defer tab.close()
|
||||
defer tab.Close()
|
||||
|
||||
for i := 0; i < tableIPLimit+1; i++ {
|
||||
n := nodeAtDistance(tab.self().ID(), i, net.IP{172, 0, 1, byte(i)})
|
||||
tab.addFoundNode(n, false)
|
||||
tab.AddFoundNode(n, false)
|
||||
}
|
||||
if tab.len() > tableIPLimit {
|
||||
t.Errorf("too many nodes in table")
|
||||
|
|
@ -167,12 +167,12 @@ func TestTable_BucketIPLimit(t *testing.T) {
|
|||
transport := newPingRecorder()
|
||||
tab, db := newTestTable(transport, Config{})
|
||||
defer db.Close()
|
||||
defer tab.close()
|
||||
defer tab.Close()
|
||||
|
||||
d := 3
|
||||
for i := 0; i < bucketIPLimit+1; i++ {
|
||||
n := nodeAtDistance(tab.self().ID(), d, net.IP{172, 0, 1, byte(i)})
|
||||
tab.addFoundNode(n, false)
|
||||
tab.AddFoundNode(n, false)
|
||||
}
|
||||
if tab.len() > bucketIPLimit {
|
||||
t.Errorf("too many nodes in table")
|
||||
|
|
@ -204,11 +204,11 @@ func TestTable_findnodeByID(t *testing.T) {
|
|||
transport := newPingRecorder()
|
||||
tab, db := newTestTable(transport, Config{})
|
||||
defer db.Close()
|
||||
defer tab.close()
|
||||
defer tab.Close()
|
||||
fillTable(tab, test.All, true)
|
||||
|
||||
// check that closest(Target, N) returns nodes
|
||||
result := tab.findnodeByID(test.Target, test.N, false).entries
|
||||
result := tab.FindnodeByID(test.Target, test.N, false).Entries
|
||||
if hasDuplicates(result) {
|
||||
t.Errorf("result contains duplicates")
|
||||
return false
|
||||
|
|
@ -264,7 +264,7 @@ func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value {
|
|||
t := &closeTest{
|
||||
Self: gen(enode.ID{}, rand).(enode.ID),
|
||||
Target: gen(enode.ID{}, rand).(enode.ID),
|
||||
N: rand.Intn(bucketSize),
|
||||
N: rand.Intn(BucketSize),
|
||||
}
|
||||
for _, id := range gen([]enode.ID{}, rand).([]enode.ID) {
|
||||
r := new(enr.Record)
|
||||
|
|
@ -279,20 +279,20 @@ func TestTable_addInboundNode(t *testing.T) {
|
|||
tab, db := newTestTable(newPingRecorder(), Config{})
|
||||
<-tab.initDone
|
||||
defer db.Close()
|
||||
defer tab.close()
|
||||
defer tab.Close()
|
||||
|
||||
// Insert two nodes.
|
||||
n1 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 1})
|
||||
n2 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 2})
|
||||
tab.addFoundNode(n1, false)
|
||||
tab.addFoundNode(n2, false)
|
||||
tab.AddFoundNode(n1, false)
|
||||
tab.AddFoundNode(n2, false)
|
||||
checkBucketContent(t, tab, []*enode.Node{n1, n2})
|
||||
|
||||
// Add a changed version of n2. The bucket should be updated.
|
||||
newrec := n2.Record()
|
||||
newrec.Set(enr.IP{99, 99, 99, 99})
|
||||
n2v2 := enode.SignNull(newrec, n2.ID())
|
||||
tab.addInboundNode(n2v2)
|
||||
tab.AddInboundNode(n2v2)
|
||||
checkBucketContent(t, tab, []*enode.Node{n1, n2v2})
|
||||
|
||||
// Try updating n2 without sequence number change. The update is accepted
|
||||
|
|
@ -301,7 +301,7 @@ func TestTable_addInboundNode(t *testing.T) {
|
|||
newrec.Set(enr.IP{100, 100, 100, 100})
|
||||
newrec.SetSeq(n2.Seq())
|
||||
n2v3 := enode.SignNull(newrec, n2.ID())
|
||||
tab.addInboundNode(n2v3)
|
||||
tab.AddInboundNode(n2v3)
|
||||
checkBucketContent(t, tab, []*enode.Node{n1, n2v3})
|
||||
}
|
||||
|
||||
|
|
@ -309,20 +309,20 @@ func TestTable_addFoundNode(t *testing.T) {
|
|||
tab, db := newTestTable(newPingRecorder(), Config{})
|
||||
<-tab.initDone
|
||||
defer db.Close()
|
||||
defer tab.close()
|
||||
defer tab.Close()
|
||||
|
||||
// Insert two nodes.
|
||||
n1 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 1})
|
||||
n2 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 2})
|
||||
tab.addFoundNode(n1, false)
|
||||
tab.addFoundNode(n2, false)
|
||||
tab.AddFoundNode(n1, false)
|
||||
tab.AddFoundNode(n2, false)
|
||||
checkBucketContent(t, tab, []*enode.Node{n1, n2})
|
||||
|
||||
// Add a changed version of n2. The bucket should be updated.
|
||||
newrec := n2.Record()
|
||||
newrec.Set(enr.IP{99, 99, 99, 99})
|
||||
n2v2 := enode.SignNull(newrec, n2.ID())
|
||||
tab.addFoundNode(n2v2, false)
|
||||
tab.AddFoundNode(n2v2, false)
|
||||
checkBucketContent(t, tab, []*enode.Node{n1, n2v2})
|
||||
|
||||
// Try updating n2 without a sequence number change.
|
||||
|
|
@ -331,7 +331,7 @@ func TestTable_addFoundNode(t *testing.T) {
|
|||
newrec.Set(enr.IP{100, 100, 100, 100})
|
||||
newrec.SetSeq(n2.Seq())
|
||||
n2v3 := enode.SignNull(newrec, n2.ID())
|
||||
tab.addFoundNode(n2v3, false)
|
||||
tab.AddFoundNode(n2v3, false)
|
||||
checkBucketContent(t, tab, []*enode.Node{n1, n2v2})
|
||||
}
|
||||
|
||||
|
|
@ -340,18 +340,18 @@ func TestTable_addInboundNodeUpdateV4Accept(t *testing.T) {
|
|||
tab, db := newTestTable(newPingRecorder(), Config{})
|
||||
<-tab.initDone
|
||||
defer db.Close()
|
||||
defer tab.close()
|
||||
defer tab.Close()
|
||||
|
||||
// Add a v4 node.
|
||||
key, _ := crypto.HexToECDSA("dd3757a8075e88d0f2b1431e7d3c5b1562e1c0aab9643707e8cbfcc8dae5cfe3")
|
||||
n1 := enode.NewV4(&key.PublicKey, net.IP{88, 77, 66, 1}, 9000, 9000)
|
||||
tab.addInboundNode(n1)
|
||||
tab.AddInboundNode(n1)
|
||||
checkBucketContent(t, tab, []*enode.Node{n1})
|
||||
|
||||
// Add an updated version with changed IP.
|
||||
// The update will be accepted because it is inbound.
|
||||
n1v2 := enode.NewV4(&key.PublicKey, net.IP{99, 99, 99, 99}, 9000, 9000)
|
||||
tab.addInboundNode(n1v2)
|
||||
tab.AddInboundNode(n1v2)
|
||||
checkBucketContent(t, tab, []*enode.Node{n1v2})
|
||||
}
|
||||
|
||||
|
|
@ -361,18 +361,18 @@ func TestTable_addFoundNodeV4UpdateReject(t *testing.T) {
|
|||
tab, db := newTestTable(newPingRecorder(), Config{})
|
||||
<-tab.initDone
|
||||
defer db.Close()
|
||||
defer tab.close()
|
||||
defer tab.Close()
|
||||
|
||||
// Add a v4 node.
|
||||
key, _ := crypto.HexToECDSA("dd3757a8075e88d0f2b1431e7d3c5b1562e1c0aab9643707e8cbfcc8dae5cfe3")
|
||||
n1 := enode.NewV4(&key.PublicKey, net.IP{88, 77, 66, 1}, 9000, 9000)
|
||||
tab.addFoundNode(n1, false)
|
||||
tab.AddFoundNode(n1, false)
|
||||
checkBucketContent(t, tab, []*enode.Node{n1})
|
||||
|
||||
// Add an updated version with changed IP.
|
||||
// The update won't be accepted because it isn't inbound.
|
||||
n1v2 := enode.NewV4(&key.PublicKey, net.IP{99, 99, 99, 99}, 9000, 9000)
|
||||
tab.addFoundNode(n1v2, false)
|
||||
tab.AddFoundNode(n1v2, false)
|
||||
checkBucketContent(t, tab, []*enode.Node{n1})
|
||||
}
|
||||
|
||||
|
|
@ -407,14 +407,14 @@ func TestTable_revalidateSyncRecord(t *testing.T) {
|
|||
})
|
||||
<-tab.initDone
|
||||
defer db.Close()
|
||||
defer tab.close()
|
||||
defer tab.Close()
|
||||
|
||||
// Insert a node.
|
||||
var r enr.Record
|
||||
r.Set(enr.IP(net.IP{127, 0, 0, 1}))
|
||||
id := enode.ID{1}
|
||||
n1 := enode.SignNull(&r, id)
|
||||
tab.addFoundNode(n1, false)
|
||||
tab.AddFoundNode(n1, false)
|
||||
|
||||
// Update the node record.
|
||||
r.Set(enr.WithEntry("foo", "bar"))
|
||||
|
|
@ -426,7 +426,7 @@ func TestTable_revalidateSyncRecord(t *testing.T) {
|
|||
waitForRevalidationPing(t, transport, tab, n2.ID())
|
||||
waitForRevalidationPing(t, transport, tab, n2.ID())
|
||||
|
||||
intable := tab.getNode(id)
|
||||
intable := tab.GetNode(id)
|
||||
if !reflect.DeepEqual(intable, n2) {
|
||||
t.Fatalf("table contains old record with seq %d, want seq %d", intable.Seq(), n2.Seq())
|
||||
}
|
||||
|
|
@ -448,22 +448,22 @@ func TestNodesPush(t *testing.T) {
|
|||
|
||||
// Insert all permutations into lists with size limit 3.
|
||||
for _, nodes := range perm {
|
||||
list := nodesByDistance{target: target}
|
||||
list := NodesByDistance{Target: target}
|
||||
for _, n := range nodes {
|
||||
list.push(n, 3)
|
||||
list.Push(n, 3)
|
||||
}
|
||||
if !slices.EqualFunc(list.entries, perm[0], nodeIDEqual) {
|
||||
if !slices.EqualFunc(list.Entries, perm[0], nodeIDEqual) {
|
||||
t.Fatal("not equal")
|
||||
}
|
||||
}
|
||||
|
||||
// Insert all permutations into lists with size limit 2.
|
||||
for _, nodes := range perm {
|
||||
list := nodesByDistance{target: target}
|
||||
list := NodesByDistance{Target: target}
|
||||
for _, n := range nodes {
|
||||
list.push(n, 2)
|
||||
list.Push(n, 2)
|
||||
}
|
||||
if !slices.EqualFunc(list.entries, perm[0][:2], nodeIDEqual) {
|
||||
if !slices.EqualFunc(list.Entries, perm[0][:2], nodeIDEqual) {
|
||||
t.Fatal("not equal")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,14 +45,14 @@ func init() {
|
|||
|
||||
func newTestTable(t transport, cfg Config) (*Table, *enode.DB) {
|
||||
tab, db := newInactiveTestTable(t, cfg)
|
||||
go tab.loop()
|
||||
go tab.Loop()
|
||||
return tab, db
|
||||
}
|
||||
|
||||
// newInactiveTestTable creates a Table without running the main loop.
|
||||
func newInactiveTestTable(t transport, cfg Config) (*Table, *enode.DB) {
|
||||
db, _ := enode.OpenDB("")
|
||||
tab, _ := newTable(t, db, cfg)
|
||||
tab, _ := NewTable(t, db, cfg)
|
||||
return tab, db
|
||||
}
|
||||
|
||||
|
|
@ -110,20 +110,20 @@ func intIP(i int) net.IP {
|
|||
func fillBucket(tab *Table, id enode.ID) (last *tableNode) {
|
||||
ld := enode.LogDist(tab.self().ID(), id)
|
||||
b := tab.bucket(id)
|
||||
for len(b.entries) < bucketSize {
|
||||
for len(b.entries) < BucketSize {
|
||||
node := nodeAtDistance(tab.self().ID(), ld, intIP(ld))
|
||||
if !tab.addFoundNode(node, false) {
|
||||
if !tab.AddFoundNode(node, false) {
|
||||
panic("node not added")
|
||||
}
|
||||
}
|
||||
return b.entries[bucketSize-1]
|
||||
return b.entries[BucketSize-1]
|
||||
}
|
||||
|
||||
// fillTable adds nodes the table to the end of their corresponding bucket
|
||||
// if the bucket is not full. The caller must not hold tab.mutex.
|
||||
func fillTable(tab *Table, nodes []*enode.Node, setLive bool) {
|
||||
for _, n := range nodes {
|
||||
tab.addFoundNode(n, setLive)
|
||||
tab.AddFoundNode(n, setLive)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -160,8 +160,8 @@ func (t *pingRecorder) updateRecord(n *enode.Node) {
|
|||
|
||||
// Stubs to satisfy the transport interface.
|
||||
func (t *pingRecorder) Self() *enode.Node { return nullNode }
|
||||
func (t *pingRecorder) lookupSelf() []*enode.Node { return nil }
|
||||
func (t *pingRecorder) lookupRandom() []*enode.Node { return nil }
|
||||
func (t *pingRecorder) LookupSelf() []*enode.Node { return nil }
|
||||
func (t *pingRecorder) LookupRandom() []*enode.Node { return nil }
|
||||
|
||||
func (t *pingRecorder) waitPing(timeout time.Duration) *enode.Node {
|
||||
t.mu.Lock()
|
||||
|
|
@ -190,7 +190,7 @@ func (t *pingRecorder) waitPing(timeout time.Duration) *enode.Node {
|
|||
}
|
||||
|
||||
// ping simulates a ping request.
|
||||
func (t *pingRecorder) ping(n *enode.Node) (seq uint64, err error) {
|
||||
func (t *pingRecorder) Ping(n *enode.Node) (seq uint64, err error) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@ func TestUDPv4_Lookup(t *testing.T) {
|
|||
for _, e := range results {
|
||||
t.Logf(" ld=%d, %x", enode.LogDist(lookupTestnet.target.ID(), e.ID()), e.ID().Bytes())
|
||||
}
|
||||
if len(results) != bucketSize {
|
||||
t.Errorf("wrong number of results: got %d, want %d", len(results), bucketSize)
|
||||
if len(results) != BucketSize {
|
||||
t.Errorf("wrong number of results: got %d, want %d", len(results), BucketSize)
|
||||
}
|
||||
checkLookupResults(t, lookupTestnet, results)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ var (
|
|||
errUnknownNode = errors.New("unknown node")
|
||||
errTimeout = errors.New("RPC timeout")
|
||||
errClockWarp = errors.New("reply deadline too far in the future")
|
||||
errClosed = errors.New("socket closed")
|
||||
errLowPort = errors.New("low port")
|
||||
ErrClosed = errors.New("socket closed")
|
||||
ErrLowPort = errors.New("low port")
|
||||
errNoUDPEndpoint = errors.New("node has no UDP endpoint")
|
||||
)
|
||||
|
||||
|
|
@ -143,12 +143,12 @@ func ListenV4(c UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv4, error) {
|
|||
log: cfg.Log,
|
||||
}
|
||||
|
||||
tab, err := newTable(t, ln.Database(), cfg)
|
||||
tab, err := NewTable(t, ln.Database(), cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.tab = tab
|
||||
go tab.loop()
|
||||
go tab.Loop()
|
||||
|
||||
t.wg.Add(2)
|
||||
go t.loop()
|
||||
|
|
@ -167,7 +167,7 @@ func (t *UDPv4) Close() {
|
|||
t.cancelCloseCtx()
|
||||
t.conn.Close()
|
||||
t.wg.Wait()
|
||||
t.tab.close()
|
||||
t.tab.Close()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -179,7 +179,7 @@ func (t *UDPv4) Resolve(n *enode.Node) *enode.Node {
|
|||
return rn
|
||||
}
|
||||
// Check table for the ID, we might have a newer version there.
|
||||
if intable := t.tab.getNode(n.ID()); intable != nil && intable.Seq() > n.Seq() {
|
||||
if intable := t.tab.GetNode(n.ID()); intable != nil && intable.Seq() > n.Seq() {
|
||||
n = intable
|
||||
if rn, err := t.RequestENR(n); err == nil {
|
||||
return rn
|
||||
|
|
@ -210,14 +210,14 @@ func (t *UDPv4) ourEndpoint() v4wire.Endpoint {
|
|||
return v4wire.NewEndpoint(addr, uint16(node.TCP()))
|
||||
}
|
||||
|
||||
// Ping sends a ping message to the given node.
|
||||
func (t *UDPv4) Ping(n *enode.Node) error {
|
||||
_, err := t.ping(n)
|
||||
// PingWithoutResp sends a ping message to the given node.
|
||||
func (t *UDPv4) PingWithoutResp(n *enode.Node) error {
|
||||
_, err := t.Ping(n)
|
||||
return err
|
||||
}
|
||||
|
||||
// ping sends a ping message to the given node and waits for a reply.
|
||||
func (t *UDPv4) ping(n *enode.Node) (seq uint64, err error) {
|
||||
func (t *UDPv4) Ping(n *enode.Node) (seq uint64, err error) {
|
||||
addr, ok := n.UDPEndpoint()
|
||||
if !ok {
|
||||
return 0, errNoUDPEndpoint
|
||||
|
|
@ -271,7 +271,7 @@ func (t *UDPv4) LookupPubkey(key *ecdsa.PublicKey) []*enode.Node {
|
|||
// case and run the bootstrapping logic.
|
||||
<-t.tab.refresh()
|
||||
}
|
||||
return t.newLookup(t.closeCtx, v4wire.EncodePubkey(key)).run()
|
||||
return t.newLookup(t.closeCtx, v4wire.EncodePubkey(key)).Run()
|
||||
}
|
||||
|
||||
// RandomNodes is an iterator yielding nodes from a random walk of the DHT.
|
||||
|
|
@ -280,25 +280,25 @@ func (t *UDPv4) RandomNodes() enode.Iterator {
|
|||
}
|
||||
|
||||
// lookupRandom implements transport.
|
||||
func (t *UDPv4) lookupRandom() []*enode.Node {
|
||||
return t.newRandomLookup(t.closeCtx).run()
|
||||
func (t *UDPv4) LookupRandom() []*enode.Node {
|
||||
return t.newRandomLookup(t.closeCtx).Run()
|
||||
}
|
||||
|
||||
// lookupSelf implements transport.
|
||||
func (t *UDPv4) lookupSelf() []*enode.Node {
|
||||
func (t *UDPv4) LookupSelf() []*enode.Node {
|
||||
pubkey := v4wire.EncodePubkey(&t.priv.PublicKey)
|
||||
return t.newLookup(t.closeCtx, pubkey).run()
|
||||
return t.newLookup(t.closeCtx, pubkey).Run()
|
||||
}
|
||||
|
||||
func (t *UDPv4) newRandomLookup(ctx context.Context) *lookup {
|
||||
func (t *UDPv4) newRandomLookup(ctx context.Context) *Lookup {
|
||||
var target v4wire.Pubkey
|
||||
crand.Read(target[:])
|
||||
return t.newLookup(ctx, target)
|
||||
}
|
||||
|
||||
func (t *UDPv4) newLookup(ctx context.Context, targetKey v4wire.Pubkey) *lookup {
|
||||
func (t *UDPv4) newLookup(ctx context.Context, targetKey v4wire.Pubkey) *Lookup {
|
||||
target := enode.ID(crypto.Keccak256Hash(targetKey[:]))
|
||||
it := newLookup(ctx, t.tab, target, func(n *enode.Node) ([]*enode.Node, error) {
|
||||
it := NewLookup(ctx, t.tab, target, func(n *enode.Node) ([]*enode.Node, error) {
|
||||
addr, ok := n.UDPEndpoint()
|
||||
if !ok {
|
||||
return nil, errNoUDPEndpoint
|
||||
|
|
@ -315,7 +315,7 @@ func (t *UDPv4) findnode(toid enode.ID, toAddrPort netip.AddrPort, target v4wire
|
|||
|
||||
// Add a matcher for 'neighbours' replies to the pending reply queue. The matcher is
|
||||
// active until enough nodes have been received.
|
||||
nodes := make([]*enode.Node, 0, bucketSize)
|
||||
nodes := make([]*enode.Node, 0, BucketSize)
|
||||
nreceived := 0
|
||||
rm := t.pending(toid, toAddrPort.Addr(), v4wire.NeighborsPacket, func(r v4wire.Packet) (matched bool, requestDone bool) {
|
||||
reply := r.(*v4wire.Neighbors)
|
||||
|
|
@ -328,7 +328,7 @@ func (t *UDPv4) findnode(toid enode.ID, toAddrPort netip.AddrPort, target v4wire
|
|||
}
|
||||
nodes = append(nodes, n)
|
||||
}
|
||||
return true, nreceived >= bucketSize
|
||||
return true, nreceived >= BucketSize
|
||||
})
|
||||
t.send(toAddrPort, toid, &v4wire.Findnode{
|
||||
Target: target,
|
||||
|
|
@ -400,7 +400,7 @@ func (t *UDPv4) pending(id enode.ID, ip netip.Addr, ptype byte, callback replyMa
|
|||
case t.addReplyMatcher <- p:
|
||||
// loop will handle it
|
||||
case <-t.closeCtx.Done():
|
||||
ch <- errClosed
|
||||
ch <- ErrClosed
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
|
@ -461,7 +461,7 @@ func (t *UDPv4) loop() {
|
|||
select {
|
||||
case <-t.closeCtx.Done():
|
||||
for el := plist.Front(); el != nil; el = el.Next() {
|
||||
el.Value.(*replyMatcher).errc <- errClosed
|
||||
el.Value.(*replyMatcher).errc <- ErrClosed
|
||||
}
|
||||
return
|
||||
|
||||
|
|
@ -599,7 +599,7 @@ func (t *UDPv4) ensureBond(toid enode.ID, toaddr netip.AddrPort) {
|
|||
|
||||
func (t *UDPv4) nodeFromRPC(sender netip.AddrPort, rn v4wire.Node) (*enode.Node, error) {
|
||||
if rn.UDP <= 1024 {
|
||||
return nil, errLowPort
|
||||
return nil, ErrLowPort
|
||||
}
|
||||
if err := netutil.CheckRelayIP(sender.Addr().AsSlice(), rn.IP); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -692,10 +692,10 @@ func (t *UDPv4) handlePing(h *packetHandlerV4, from netip.AddrPort, fromID enode
|
|||
n := enode.NewV4(h.senderKey, fromIP, int(req.From.TCP), int(from.Port()))
|
||||
if time.Since(t.db.LastPongReceived(n.ID(), from.Addr())) > bondExpiration {
|
||||
t.sendPing(fromID, from, func() {
|
||||
t.tab.addInboundNode(n)
|
||||
t.tab.AddInboundNode(n)
|
||||
})
|
||||
} else {
|
||||
t.tab.addInboundNode(n)
|
||||
t.tab.AddInboundNode(n)
|
||||
}
|
||||
|
||||
// Update node database and endpoint predictor.
|
||||
|
|
@ -747,7 +747,7 @@ func (t *UDPv4) handleFindnode(h *packetHandlerV4, from netip.AddrPort, fromID e
|
|||
// Determine closest nodes.
|
||||
target := enode.ID(crypto.Keccak256Hash(req.Target[:]))
|
||||
preferLive := !t.tab.cfg.NoFindnodeLivenessCheck
|
||||
closest := t.tab.findnodeByID(target, bucketSize, preferLive).entries
|
||||
closest := t.tab.FindnodeByID(target, BucketSize, preferLive).Entries
|
||||
|
||||
// Send neighbors in chunks with at most maxNeighbors per packet
|
||||
// to stay below the packet size limit.
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ func (test *udpTest) waitPacketOut(validate interface{}) (closed bool) {
|
|||
test.t.Helper()
|
||||
|
||||
dgram, err := test.pipe.receive()
|
||||
if err == errClosed {
|
||||
if err == ErrClosed {
|
||||
return true
|
||||
} else if err != nil {
|
||||
test.t.Error("packet receive error:", err)
|
||||
|
|
@ -151,7 +151,7 @@ func TestUDPv4_pingTimeout(t *testing.T) {
|
|||
key := newkey()
|
||||
toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222}
|
||||
node := enode.NewV4(&key.PublicKey, toaddr.IP, 0, toaddr.Port)
|
||||
if _, err := test.udp.ping(node); err != errTimeout {
|
||||
if _, err := test.udp.Ping(node); err != errTimeout {
|
||||
t.Error("expected timeout error, got", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -256,9 +256,9 @@ func TestUDPv4_findnode(t *testing.T) {
|
|||
// put a few nodes into the table. their exact
|
||||
// distribution shouldn't matter much, although we need to
|
||||
// take care not to overflow any bucket.
|
||||
nodes := &nodesByDistance{target: testTarget.ID()}
|
||||
nodes := &NodesByDistance{Target: testTarget.ID()}
|
||||
live := make(map[enode.ID]bool)
|
||||
numCandidates := 2 * bucketSize
|
||||
numCandidates := 2 * BucketSize
|
||||
for i := 0; i < numCandidates; i++ {
|
||||
key := newkey()
|
||||
ip := net.IP{10, 13, 0, byte(i)}
|
||||
|
|
@ -267,8 +267,8 @@ func TestUDPv4_findnode(t *testing.T) {
|
|||
if i > numCandidates/2 {
|
||||
live[n.ID()] = true
|
||||
}
|
||||
test.table.addFoundNode(n, live[n.ID()])
|
||||
nodes.push(n, numCandidates)
|
||||
test.table.AddFoundNode(n, live[n.ID()])
|
||||
nodes.Push(n, numCandidates)
|
||||
}
|
||||
|
||||
// ensure there's a bond with the test node,
|
||||
|
|
@ -277,7 +277,7 @@ func TestUDPv4_findnode(t *testing.T) {
|
|||
test.table.db.UpdateLastPongReceived(remoteID, test.remoteaddr.Addr(), time.Now())
|
||||
|
||||
// check that closest neighbors are returned.
|
||||
expected := test.table.findnodeByID(testTarget.ID(), bucketSize, true)
|
||||
expected := test.table.FindnodeByID(testTarget.ID(), BucketSize, true)
|
||||
test.packetIn(nil, &v4wire.Findnode{Target: testTarget, Expiration: futureExp})
|
||||
waitNeighbors := func(want []*enode.Node) {
|
||||
test.waitPacketOut(func(p *v4wire.Neighbors, to netip.AddrPort, hash []byte) {
|
||||
|
|
@ -287,7 +287,7 @@ func TestUDPv4_findnode(t *testing.T) {
|
|||
}
|
||||
for i, n := range p.Nodes {
|
||||
if n.ID.ID() != want[i].ID() {
|
||||
t.Errorf("result mismatch at %d:\n got: %v\n want: %v", i, n, expected.entries[i])
|
||||
t.Errorf("result mismatch at %d:\n got: %v\n want: %v", i, n, expected.Entries[i])
|
||||
}
|
||||
if !live[n.ID.ID()] {
|
||||
t.Errorf("result includes dead node %v", n.ID.ID())
|
||||
|
|
@ -296,7 +296,7 @@ func TestUDPv4_findnode(t *testing.T) {
|
|||
})
|
||||
}
|
||||
// Receive replies.
|
||||
want := expected.entries
|
||||
want := expected.Entries
|
||||
if len(want) > v4wire.MaxNeighbors {
|
||||
waitNeighbors(want[:v4wire.MaxNeighbors])
|
||||
want = want[v4wire.MaxNeighbors:]
|
||||
|
|
@ -644,7 +644,7 @@ func (c *dgramPipe) receive() (dgram, error) {
|
|||
c.cond.Wait()
|
||||
}
|
||||
if c.closed {
|
||||
return dgram{}, errClosed
|
||||
return dgram{}, ErrClosed
|
||||
}
|
||||
if timedOut {
|
||||
return dgram{}, errTimeout
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ func ListenV5(conn UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv5, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go t.tab.loop()
|
||||
go t.tab.Loop()
|
||||
t.wg.Add(2)
|
||||
go t.readLoop()
|
||||
go t.dispatch()
|
||||
|
|
@ -180,7 +180,7 @@ func newUDPv5(conn UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv5, error) {
|
|||
cancelCloseCtx: cancelCloseCtx,
|
||||
}
|
||||
t.talk = newTalkSystem(t)
|
||||
tab, err := newTable(t, t.db, cfg)
|
||||
tab, err := NewTable(t, t.db, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -200,20 +200,20 @@ func (t *UDPv5) Close() {
|
|||
t.conn.Close()
|
||||
t.talk.wait()
|
||||
t.wg.Wait()
|
||||
t.tab.close()
|
||||
t.tab.Close()
|
||||
})
|
||||
}
|
||||
|
||||
// Ping sends a ping message to the given node.
|
||||
func (t *UDPv5) Ping(n *enode.Node) error {
|
||||
_, err := t.ping(n)
|
||||
// PingWithoutResp sends a ping message to the given node.
|
||||
func (t *UDPv5) PingWithoutResp(n *enode.Node) error {
|
||||
_, err := t.Ping(n)
|
||||
return err
|
||||
}
|
||||
|
||||
// Resolve searches for a specific node with the given ID and tries to get the most recent
|
||||
// version of the node record for it. It returns n if the node could not be resolved.
|
||||
func (t *UDPv5) Resolve(n *enode.Node) *enode.Node {
|
||||
if intable := t.tab.getNode(n.ID()); intable != nil && intable.Seq() > n.Seq() {
|
||||
if intable := t.tab.GetNode(n.ID()); intable != nil && intable.Seq() > n.Seq() {
|
||||
n = intable
|
||||
}
|
||||
// Try asking directly. This works if the node is still responding on the endpoint we have.
|
||||
|
|
@ -237,7 +237,7 @@ func (t *UDPv5) ResolveNodeId(id enode.ID) *enode.Node {
|
|||
return t.Self()
|
||||
}
|
||||
|
||||
n := t.tab.getNode(id)
|
||||
n := t.tab.GetNode(id)
|
||||
if n != nil {
|
||||
// Try asking directly. This works if the Node is still responding on the endpoint we have.
|
||||
if resp, err := t.RequestENR(n); err == nil {
|
||||
|
|
@ -341,29 +341,29 @@ func (t *UDPv5) RandomNodes() enode.Iterator {
|
|||
// Lookup performs a recursive lookup for the given target.
|
||||
// It returns the closest nodes to target.
|
||||
func (t *UDPv5) Lookup(target enode.ID) []*enode.Node {
|
||||
return t.newLookup(t.closeCtx, target).run()
|
||||
return t.newLookup(t.closeCtx, target).Run()
|
||||
}
|
||||
|
||||
// lookupRandom looks up a random target.
|
||||
// This is needed to satisfy the transport interface.
|
||||
func (t *UDPv5) lookupRandom() []*enode.Node {
|
||||
return t.newRandomLookup(t.closeCtx).run()
|
||||
func (t *UDPv5) LookupRandom() []*enode.Node {
|
||||
return t.newRandomLookup(t.closeCtx).Run()
|
||||
}
|
||||
|
||||
// lookupSelf looks up our own node ID.
|
||||
// This is needed to satisfy the transport interface.
|
||||
func (t *UDPv5) lookupSelf() []*enode.Node {
|
||||
return t.newLookup(t.closeCtx, t.Self().ID()).run()
|
||||
func (t *UDPv5) LookupSelf() []*enode.Node {
|
||||
return t.newLookup(t.closeCtx, t.Self().ID()).Run()
|
||||
}
|
||||
|
||||
func (t *UDPv5) newRandomLookup(ctx context.Context) *lookup {
|
||||
func (t *UDPv5) newRandomLookup(ctx context.Context) *Lookup {
|
||||
var target enode.ID
|
||||
crand.Read(target[:])
|
||||
return t.newLookup(ctx, target)
|
||||
}
|
||||
|
||||
func (t *UDPv5) newLookup(ctx context.Context, target enode.ID) *lookup {
|
||||
return newLookup(ctx, t.tab, target, func(n *enode.Node) ([]*enode.Node, error) {
|
||||
func (t *UDPv5) newLookup(ctx context.Context, target enode.ID) *Lookup {
|
||||
return NewLookup(ctx, t.tab, target, func(n *enode.Node) ([]*enode.Node, error) {
|
||||
return t.lookupWorker(n, target)
|
||||
})
|
||||
}
|
||||
|
|
@ -371,27 +371,27 @@ func (t *UDPv5) newLookup(ctx context.Context, target enode.ID) *lookup {
|
|||
// lookupWorker performs FINDNODE calls against a single node during lookup.
|
||||
func (t *UDPv5) lookupWorker(destNode *enode.Node, target enode.ID) ([]*enode.Node, error) {
|
||||
var (
|
||||
dists = lookupDistances(target, destNode.ID())
|
||||
nodes = nodesByDistance{target: target}
|
||||
dists = LookupDistances(target, destNode.ID())
|
||||
nodes = NodesByDistance{Target: target}
|
||||
err error
|
||||
)
|
||||
var r []*enode.Node
|
||||
r, err = t.findnode(destNode, dists)
|
||||
if errors.Is(err, errClosed) {
|
||||
r, err = t.Findnode(destNode, dists)
|
||||
if errors.Is(err, ErrClosed) {
|
||||
return nil, err
|
||||
}
|
||||
for _, n := range r {
|
||||
if n.ID() != t.Self().ID() {
|
||||
nodes.push(n, findnodeResultLimit)
|
||||
nodes.Push(n, findnodeResultLimit)
|
||||
}
|
||||
}
|
||||
return nodes.entries, err
|
||||
return nodes.Entries, err
|
||||
}
|
||||
|
||||
// lookupDistances computes the distance parameter for FINDNODE calls to dest.
|
||||
// LookupDistances computes the distance parameter for FINDNODE calls to dest.
|
||||
// It chooses distances adjacent to logdist(target, dest), e.g. for a target
|
||||
// with logdist(target, dest) = 255 the result is [255, 256, 254].
|
||||
func lookupDistances(target, dest enode.ID) (dists []uint) {
|
||||
func LookupDistances(target, dest enode.ID) (dists []uint) {
|
||||
td := enode.LogDist(target, dest)
|
||||
dists = append(dists, uint(td))
|
||||
for i := 1; len(dists) < lookupRequestLimit; i++ {
|
||||
|
|
@ -406,8 +406,8 @@ func lookupDistances(target, dest enode.ID) (dists []uint) {
|
|||
}
|
||||
|
||||
// ping calls PING on a node and waits for a PONG response.
|
||||
func (t *UDPv5) ping(n *enode.Node) (uint64, error) {
|
||||
pong, err := t.pingInner(n)
|
||||
func (t *UDPv5) Ping(n *enode.Node) (uint64, error) {
|
||||
pong, err := t.PingWithResp(n)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
@ -415,8 +415,8 @@ func (t *UDPv5) ping(n *enode.Node) (uint64, error) {
|
|||
return pong.ENRSeq, nil
|
||||
}
|
||||
|
||||
// pingInner calls PING on a node and waits for a PONG response.
|
||||
func (t *UDPv5) pingInner(n *enode.Node) (*v5wire.Pong, error) {
|
||||
// PingWithResp calls PING on a node and waits for a PONG response.
|
||||
func (t *UDPv5) PingWithResp(n *enode.Node) (*v5wire.Pong, error) {
|
||||
req := &v5wire.Ping{ENRSeq: t.localNode.Node().Seq()}
|
||||
resp := t.callToNode(n, v5wire.PongMsg, req)
|
||||
defer t.callDone(resp)
|
||||
|
|
@ -431,7 +431,7 @@ func (t *UDPv5) pingInner(n *enode.Node) (*v5wire.Pong, error) {
|
|||
|
||||
// RequestENR requests n's record.
|
||||
func (t *UDPv5) RequestENR(n *enode.Node) (*enode.Node, error) {
|
||||
nodes, err := t.findnode(n, []uint{0})
|
||||
nodes, err := t.Findnode(n, []uint{0})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -441,8 +441,8 @@ func (t *UDPv5) RequestENR(n *enode.Node) (*enode.Node, error) {
|
|||
return nodes[0], nil
|
||||
}
|
||||
|
||||
// findnode calls FINDNODE on a node and waits for responses.
|
||||
func (t *UDPv5) findnode(n *enode.Node, distances []uint) ([]*enode.Node, error) {
|
||||
// Findnode calls FINDNODE on a node and waits for responses.
|
||||
func (t *UDPv5) Findnode(n *enode.Node, distances []uint) ([]*enode.Node, error) {
|
||||
resp := t.callToNode(n, v5wire.NodesMsg, &v5wire.Findnode{Distances: distances})
|
||||
return t.waitForNodes(resp, distances)
|
||||
}
|
||||
|
|
@ -493,7 +493,7 @@ func (t *UDPv5) verifyResponseNode(c *callV5, r *enr.Record, distances []uint, s
|
|||
return nil, errors.New("not contained in netrestrict list")
|
||||
}
|
||||
if node.UDP() <= 1024 {
|
||||
return nil, errLowPort
|
||||
return nil, ErrLowPort
|
||||
}
|
||||
if distances != nil {
|
||||
nd := enode.LogDist(c.id, node.ID())
|
||||
|
|
@ -537,7 +537,7 @@ func (t *UDPv5) initCall(c *callV5, responseType byte, packet v5wire.Packet) {
|
|||
select {
|
||||
case t.callCh <- c:
|
||||
case <-t.closeCtx.Done():
|
||||
c.err <- errClosed
|
||||
c.err <- ErrClosed
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -630,12 +630,12 @@ func (t *UDPv5) dispatch() {
|
|||
close(t.readNextCh)
|
||||
for id, queue := range t.callQueue {
|
||||
for _, c := range queue {
|
||||
c.err <- errClosed
|
||||
c.err <- ErrClosed
|
||||
}
|
||||
delete(t.callQueue, id)
|
||||
}
|
||||
for id, c := range t.activeCallByNode {
|
||||
c.err <- errClosed
|
||||
c.err <- ErrClosed
|
||||
delete(t.activeCallByNode, id)
|
||||
delete(t.activeCallByAuth, c.nonce)
|
||||
}
|
||||
|
|
@ -709,7 +709,7 @@ func (t *UDPv5) sendFromAnotherThread(toID enode.ID, toAddr netip.AddrPort, pack
|
|||
}
|
||||
}
|
||||
|
||||
func (t *UDPv5) sendFromAnotherThreadWithNode(node *enode.Node, toAddr netip.AddrPort, packet v5wire.Packet) {
|
||||
func (t *UDPv5) SendFromAnotherThreadWithNode(node *enode.Node, toAddr netip.AddrPort, packet v5wire.Packet) {
|
||||
select {
|
||||
case t.sendCh <- sendRequest{node.ID(), node, toAddr, packet}:
|
||||
case <-t.closeCtx.Done():
|
||||
|
|
@ -792,7 +792,7 @@ func (t *UDPv5) handlePacket(rawpacket []byte, fromAddr netip.AddrPort) error {
|
|||
}
|
||||
if fromNode != nil {
|
||||
// Handshake succeeded, add to table.
|
||||
t.tab.addInboundNode(fromNode)
|
||||
t.tab.AddInboundNode(fromNode)
|
||||
t.putCache(fromAddr.String(), fromNode)
|
||||
}
|
||||
if packet.Kind() != v5wire.WhoareyouPacket {
|
||||
|
|
@ -825,9 +825,9 @@ func (t *UDPv5) handleCallResponse(fromID enode.ID, fromAddr netip.AddrPort, p v
|
|||
return true
|
||||
}
|
||||
|
||||
// getNode looks for a node record in table and database.
|
||||
func (t *UDPv5) getNode(id enode.ID) *enode.Node {
|
||||
if n := t.tab.getNode(id); n != nil {
|
||||
// GetNode looks for a node record in table and database.
|
||||
func (t *UDPv5) GetNode(id enode.ID) *enode.Node {
|
||||
if n := t.tab.GetNode(id); n != nil {
|
||||
return n
|
||||
}
|
||||
if n := t.localNode.Database().Node(id); n != nil {
|
||||
|
|
@ -865,7 +865,7 @@ func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr netip.AddrPort
|
|||
func (t *UDPv5) handleUnknown(p *v5wire.Unknown, fromID enode.ID, fromAddr netip.AddrPort) {
|
||||
challenge := &v5wire.Whoareyou{Nonce: p.Nonce}
|
||||
crand.Read(challenge.IDNonce[:])
|
||||
if n := t.getNode(fromID); n != nil {
|
||||
if n := t.GetNode(fromID); n != nil {
|
||||
challenge.Node = n
|
||||
challenge.RecordSeq = n.Seq()
|
||||
}
|
||||
|
|
@ -952,7 +952,7 @@ func (t *UDPv5) collectTableNodes(rip netip.Addr, distances []uint, limit int) [
|
|||
processed[dist] = struct{}{}
|
||||
|
||||
checkLive := !t.tab.cfg.NoFindnodeLivenessCheck
|
||||
for _, n := range t.tab.appendBucketNodes(dist, bn[:0], checkLive) {
|
||||
for _, n := range t.tab.AppendBucketNodes(dist, bn[:0], checkLive) {
|
||||
// Apply some pre-checks to avoid sending invalid nodes.
|
||||
// Note liveness is checked by appendLiveNodes.
|
||||
if netutil.CheckRelayAddr(rip, n.IPAddr()) != nil {
|
||||
|
|
@ -1014,3 +1014,7 @@ func (t *UDPv5) GetCachedNode(addr string) (*enode.Node, bool) {
|
|||
n, ok := t.cachedAddrNode[addr]
|
||||
return n, ok
|
||||
}
|
||||
|
||||
func (t *UDPv5) Table() *Table {
|
||||
return t.tab
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ func TestUDPv5_unknownPacket(t *testing.T) {
|
|||
|
||||
// Make Node known.
|
||||
n := test.getNode(test.remotekey, test.remoteaddr).Node()
|
||||
test.table.addFoundNode(n, false)
|
||||
test.table.AddFoundNode(n, false)
|
||||
|
||||
test.packetIn(&v5wire.Unknown{Nonce: nonce})
|
||||
test.waitPacketOut(func(p *v5wire.Whoareyou, addr netip.AddrPort, _ v5wire.Nonce) {
|
||||
|
|
@ -237,7 +237,7 @@ func TestUDPv5_pingCall(t *testing.T) {
|
|||
|
||||
// This ping times out.
|
||||
go func() {
|
||||
_, err := test.udp.ping(remote)
|
||||
_, err := test.udp.Ping(remote)
|
||||
done <- err
|
||||
}()
|
||||
test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, _ v5wire.Nonce) {})
|
||||
|
|
@ -247,7 +247,7 @@ func TestUDPv5_pingCall(t *testing.T) {
|
|||
|
||||
// This ping works.
|
||||
go func() {
|
||||
_, err := test.udp.ping(remote)
|
||||
_, err := test.udp.Ping(remote)
|
||||
done <- err
|
||||
}()
|
||||
test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, _ v5wire.Nonce) {
|
||||
|
|
@ -259,7 +259,7 @@ func TestUDPv5_pingCall(t *testing.T) {
|
|||
|
||||
// This ping gets a reply from the wrong endpoint.
|
||||
go func() {
|
||||
_, err := test.udp.ping(remote)
|
||||
_, err := test.udp.Ping(remote)
|
||||
done <- err
|
||||
}()
|
||||
test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, _ v5wire.Nonce) {
|
||||
|
|
@ -288,7 +288,7 @@ func TestUDPv5_findnodeCall(t *testing.T) {
|
|||
)
|
||||
go func() {
|
||||
var err error
|
||||
response, err = test.udp.findnode(remote, distances)
|
||||
response, err = test.udp.Findnode(remote, distances)
|
||||
done <- err
|
||||
}()
|
||||
|
||||
|
|
@ -330,11 +330,11 @@ func TestUDPv5_callResend(t *testing.T) {
|
|||
remote := test.getNode(test.remotekey, test.remoteaddr).Node()
|
||||
done := make(chan error, 2)
|
||||
go func() {
|
||||
_, err := test.udp.ping(remote)
|
||||
_, err := test.udp.Ping(remote)
|
||||
done <- err
|
||||
}()
|
||||
go func() {
|
||||
_, err := test.udp.ping(remote)
|
||||
_, err := test.udp.Ping(remote)
|
||||
done <- err
|
||||
}()
|
||||
|
||||
|
|
@ -367,7 +367,7 @@ func TestUDPv5_multipleHandshakeRounds(t *testing.T) {
|
|||
remote := test.getNode(test.remotekey, test.remoteaddr).Node()
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
_, err := test.udp.ping(remote)
|
||||
_, err := test.udp.Ping(remote)
|
||||
done <- err
|
||||
}()
|
||||
|
||||
|
|
@ -398,7 +398,7 @@ func TestUDPv5_callTimeoutReset(t *testing.T) {
|
|||
done = make(chan error, 1)
|
||||
)
|
||||
go func() {
|
||||
_, err := test.udp.findnode(remote, []uint{distance})
|
||||
_, err := test.udp.Findnode(remote, []uint{distance})
|
||||
done <- err
|
||||
}()
|
||||
|
||||
|
|
@ -535,38 +535,38 @@ func TestUDPv5_talkRequest(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// This test checks that lookupDistances works.
|
||||
// This test checks that LookupDistances works.
|
||||
func TestUDPv5_lookupDistances(t *testing.T) {
|
||||
test := newUDPV5Test(t)
|
||||
lnID := test.table.self().ID()
|
||||
|
||||
t.Run("target distance of 1", func(t *testing.T) {
|
||||
node := nodeAtDistance(lnID, 1, intIP(0))
|
||||
dists := lookupDistances(lnID, node.ID())
|
||||
dists := LookupDistances(lnID, node.ID())
|
||||
require.Equal(t, []uint{1, 2, 3}, dists)
|
||||
})
|
||||
|
||||
t.Run("target distance of 2", func(t *testing.T) {
|
||||
node := nodeAtDistance(lnID, 2, intIP(0))
|
||||
dists := lookupDistances(lnID, node.ID())
|
||||
dists := LookupDistances(lnID, node.ID())
|
||||
require.Equal(t, []uint{2, 3, 1}, dists)
|
||||
})
|
||||
|
||||
t.Run("target distance of 128", func(t *testing.T) {
|
||||
node := nodeAtDistance(lnID, 128, intIP(0))
|
||||
dists := lookupDistances(lnID, node.ID())
|
||||
dists := LookupDistances(lnID, node.ID())
|
||||
require.Equal(t, []uint{128, 129, 127}, dists)
|
||||
})
|
||||
|
||||
t.Run("target distance of 255", func(t *testing.T) {
|
||||
node := nodeAtDistance(lnID, 255, intIP(0))
|
||||
dists := lookupDistances(lnID, node.ID())
|
||||
dists := LookupDistances(lnID, node.ID())
|
||||
require.Equal(t, []uint{255, 256, 254}, dists)
|
||||
})
|
||||
|
||||
t.Run("target distance of 256", func(t *testing.T) {
|
||||
node := nodeAtDistance(lnID, 256, intIP(0))
|
||||
dists := lookupDistances(lnID, node.ID())
|
||||
dists := LookupDistances(lnID, node.ID())
|
||||
require.Equal(t, []uint{256, 255, 254}, dists)
|
||||
})
|
||||
}
|
||||
|
|
@ -817,7 +817,7 @@ func (test *udpV5Test) waitPacketOut(validate interface{}) (closed bool) {
|
|||
exptype := fn.Type().In(0)
|
||||
|
||||
dgram, err := test.pipe.receive()
|
||||
if err == errClosed {
|
||||
if err == ErrClosed {
|
||||
return true
|
||||
}
|
||||
if err == errTimeout {
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ const (
|
|||
// Should reject packets smaller than minPacketSize.
|
||||
minPacketSize = 63
|
||||
|
||||
maxPacketSize = 1280
|
||||
MaxPacketSize = 1280
|
||||
|
||||
minMessageSize = 48 // this refers to data after static headers
|
||||
randomPacketMsgSize = 20
|
||||
|
|
@ -169,7 +169,7 @@ func NewCodec(ln *enode.LocalNode, key *ecdsa.PrivateKey, clock mclock.Clock, pr
|
|||
privkey: key,
|
||||
sc: NewSessionCache(1024, clock),
|
||||
protocolID: DefaultProtocolID,
|
||||
decbuf: make([]byte, maxPacketSize),
|
||||
decbuf: make([]byte, MaxPacketSize),
|
||||
}
|
||||
if protocolID != nil {
|
||||
c.protocolID = *protocolID
|
||||
|
|
|
|||
543
portalnetwork/api.go
Normal file
543
portalnetwork/api.go
Normal file
|
|
@ -0,0 +1,543 @@
|
|||
package portalnetwork
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/portalnetwork/portalwire"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// DiscV5API json-rpc spec
|
||||
// https://playground.open-rpc.org/?schemaUrl=https://raw.githubusercontent.com/ethereum/portal-network-specs/assembled-spec/jsonrpc/openrpc.json&uiSchema%5BappBar%5D%5Bui:splitView%5D=false&uiSchema%5BappBar%5D%5Bui:input%5D=false&uiSchema%5BappBar%5D%5Bui:examplesDropdown%5D=false
|
||||
type DiscV5API struct {
|
||||
DiscV5 *discover.UDPv5
|
||||
}
|
||||
|
||||
func NewDiscV5API(discV5 *discover.UDPv5) *DiscV5API {
|
||||
return &DiscV5API{discV5}
|
||||
}
|
||||
|
||||
type NodeInfo struct {
|
||||
NodeId string `json:"nodeId"`
|
||||
Enr string `json:"enr"`
|
||||
Ip string `json:"ip"`
|
||||
}
|
||||
|
||||
type RoutingTableInfo struct {
|
||||
Buckets [][]string `json:"buckets"`
|
||||
LocalNodeId string `json:"localNodeId"`
|
||||
}
|
||||
|
||||
type DiscV5PongResp struct {
|
||||
EnrSeq uint64 `json:"enrSeq"`
|
||||
RecipientIP string `json:"recipientIP"`
|
||||
RecipientPort uint16 `json:"recipientPort"`
|
||||
}
|
||||
|
||||
type PortalPongResp struct {
|
||||
EnrSeq uint32 `json:"enrSeq"`
|
||||
DataRadius string `json:"dataRadius"`
|
||||
}
|
||||
|
||||
type ContentInfo struct {
|
||||
Content string `json:"content"`
|
||||
UtpTransfer bool `json:"utpTransfer"`
|
||||
}
|
||||
|
||||
type TraceContentResult struct {
|
||||
Content string `json:"content"`
|
||||
UtpTransfer bool `json:"utpTransfer"`
|
||||
Trace Trace `json:"trace"`
|
||||
}
|
||||
|
||||
type Trace struct {
|
||||
Origin string `json:"origin"` // local node id
|
||||
TargetId string `json:"targetId"` // target content id
|
||||
ReceivedFrom string `json:"receivedFrom"` // the node id of which content from
|
||||
Responses map[string]RespByNode `json:"responses"` // the node id and there response nodeIds
|
||||
Metadata map[string]*NodeMetadata `json:"metadata"` // node id and there metadata object
|
||||
StartedAtMs int `json:"startedAtMs"` // timestamp of the beginning of this request in milliseconds
|
||||
Cancelled []string `json:"cancelled"` // the node ids which are send but cancelled
|
||||
}
|
||||
|
||||
type NodeMetadata struct {
|
||||
Enr string `json:"enr"`
|
||||
Distance string `json:"distance"`
|
||||
}
|
||||
|
||||
type RespByNode struct {
|
||||
DurationMs int32 `json:"durationMs"`
|
||||
RespondedWith []string `json:"respondedWith"`
|
||||
}
|
||||
|
||||
type Enrs struct {
|
||||
Enrs []string `json:"enrs"`
|
||||
}
|
||||
|
||||
func (d *DiscV5API) NodeInfo() *NodeInfo {
|
||||
n := d.DiscV5.LocalNode().Node()
|
||||
|
||||
return &NodeInfo{
|
||||
NodeId: "0x" + n.ID().String(),
|
||||
Enr: n.String(),
|
||||
Ip: n.IP().String(),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DiscV5API) RoutingTableInfo() *RoutingTableInfo {
|
||||
n := d.DiscV5.LocalNode().Node()
|
||||
bucketNodes := d.DiscV5.RoutingTableInfo()
|
||||
|
||||
return &RoutingTableInfo{
|
||||
Buckets: bucketNodes,
|
||||
LocalNodeId: "0x" + n.ID().String(),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DiscV5API) AddEnr(enr string) (bool, error) {
|
||||
n, err := enode.Parse(enode.ValidSchemes, enr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// immediately add the node to the routing table
|
||||
d.DiscV5.Table().AddInboundNode(n)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (d *DiscV5API) GetEnr(nodeId string) (bool, error) {
|
||||
id, err := enode.ParseID(nodeId)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
n := d.DiscV5.Table().GetNode(id)
|
||||
if n == nil {
|
||||
return false, errors.New("record not in local routing table")
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (d *DiscV5API) DeleteEnr(nodeId string) (bool, error) {
|
||||
id, err := enode.ParseID(nodeId)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
n := d.DiscV5.Table().GetNode(id)
|
||||
if n == nil {
|
||||
return false, errors.New("record not in local routing table")
|
||||
}
|
||||
|
||||
d.DiscV5.Table().DeleteNode(n)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (d *DiscV5API) LookupEnr(nodeId string) (string, error) {
|
||||
id, err := enode.ParseID(nodeId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
enr := d.DiscV5.ResolveNodeId(id)
|
||||
|
||||
if enr == nil {
|
||||
return "", errors.New("record not found in DHT lookup")
|
||||
}
|
||||
|
||||
return enr.String(), nil
|
||||
}
|
||||
|
||||
func (d *DiscV5API) Ping(enr string) (*DiscV5PongResp, error) {
|
||||
n, err := enode.Parse(enode.ValidSchemes, enr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pong, err := d.DiscV5.PingWithResp(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &DiscV5PongResp{
|
||||
EnrSeq: pong.ENRSeq,
|
||||
RecipientIP: pong.ToIP.String(),
|
||||
RecipientPort: pong.ToPort,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DiscV5API) FindNodes(enr string, distances []uint) ([]string, error) {
|
||||
n, err := enode.Parse(enode.ValidSchemes, enr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
findNodes, err := d.DiscV5.Findnode(n, distances)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
enrs := make([]string, 0, len(findNodes))
|
||||
for _, r := range findNodes {
|
||||
enrs = append(enrs, r.String())
|
||||
}
|
||||
|
||||
return enrs, nil
|
||||
}
|
||||
|
||||
func (d *DiscV5API) TalkReq(enr string, protocol string, payload string) (string, error) {
|
||||
n, err := enode.Parse(enode.ValidSchemes, enr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
req, err := hexutil.Decode(payload)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
talkResp, err := d.DiscV5.TalkRequest(n, protocol, req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hexutil.Encode(talkResp), nil
|
||||
}
|
||||
|
||||
func (d *DiscV5API) RecursiveFindNodes(nodeId string) ([]string, error) {
|
||||
findNodes := d.DiscV5.Lookup(enode.HexID(nodeId))
|
||||
|
||||
enrs := make([]string, 0, len(findNodes))
|
||||
for _, r := range findNodes {
|
||||
enrs = append(enrs, r.String())
|
||||
}
|
||||
|
||||
return enrs, nil
|
||||
}
|
||||
|
||||
type PortalProtocolAPI struct {
|
||||
portalProtocol *PortalProtocol
|
||||
}
|
||||
|
||||
func NewPortalAPI(portalProtocol *PortalProtocol) *PortalProtocolAPI {
|
||||
return &PortalProtocolAPI{
|
||||
portalProtocol: portalProtocol,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PortalProtocolAPI) NodeInfo() *NodeInfo {
|
||||
n := p.portalProtocol.localNode.Node()
|
||||
|
||||
return &NodeInfo{
|
||||
NodeId: n.ID().String(),
|
||||
Enr: n.String(),
|
||||
Ip: n.IP().String(),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PortalProtocolAPI) RoutingTableInfo() *RoutingTableInfo {
|
||||
n := p.portalProtocol.localNode.Node()
|
||||
bucketNodes := p.portalProtocol.RoutingTableInfo()
|
||||
|
||||
return &RoutingTableInfo{
|
||||
Buckets: bucketNodes,
|
||||
LocalNodeId: "0x" + n.ID().String(),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PortalProtocolAPI) AddEnr(enr string) (bool, error) {
|
||||
p.portalProtocol.Log.Debug("serving AddEnr", "enr", enr)
|
||||
n, err := enode.ParseForAddEnr(enode.ValidSchemes, enr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
p.portalProtocol.AddEnr(n)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (p *PortalProtocolAPI) AddEnrs(enrs []string) bool {
|
||||
// Note: unspecified RPC, but useful for our local testnet test
|
||||
for _, enr := range enrs {
|
||||
n, err := enode.ParseForAddEnr(enode.ValidSchemes, enr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
p.portalProtocol.AddEnr(n)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *PortalProtocolAPI) GetEnr(nodeId string) (string, error) {
|
||||
id, err := enode.ParseID(nodeId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if id == p.portalProtocol.localNode.Node().ID() {
|
||||
return p.portalProtocol.localNode.Node().String(), nil
|
||||
}
|
||||
|
||||
n := p.portalProtocol.table.GetNode(id)
|
||||
if n == nil {
|
||||
return "", errors.New("record not in local routing table")
|
||||
}
|
||||
|
||||
return n.String(), nil
|
||||
}
|
||||
|
||||
func (p *PortalProtocolAPI) DeleteEnr(nodeId string) (bool, error) {
|
||||
id, err := enode.ParseID(nodeId)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
n := p.portalProtocol.table.GetNode(id)
|
||||
if n == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
p.portalProtocol.table.DeleteNode(n)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (p *PortalProtocolAPI) LookupEnr(nodeId string) (string, error) {
|
||||
id, err := enode.ParseID(nodeId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
enr := p.portalProtocol.ResolveNodeId(id)
|
||||
|
||||
if enr == nil {
|
||||
return "", errors.New("record not found in DHT lookup")
|
||||
}
|
||||
|
||||
return enr.String(), nil
|
||||
}
|
||||
|
||||
func (p *PortalProtocolAPI) Ping(enr string) (*PortalPongResp, error) {
|
||||
n, err := enode.Parse(enode.ValidSchemes, enr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pong, err := p.portalProtocol.pingInner(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
customPayload := &portalwire.PingPongCustomData{}
|
||||
err = customPayload.UnmarshalSSZ(pong.CustomPayload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodeRadius := new(uint256.Int)
|
||||
err = nodeRadius.UnmarshalSSZ(customPayload.Radius)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &PortalPongResp{
|
||||
EnrSeq: uint32(pong.EnrSeq),
|
||||
DataRadius: nodeRadius.Hex(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *PortalProtocolAPI) FindNodes(enr string, distances []uint) ([]string, error) {
|
||||
n, err := enode.Parse(enode.ValidSchemes, enr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
findNodes, err := p.portalProtocol.findNodes(n, distances)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
enrs := make([]string, 0, len(findNodes))
|
||||
for _, r := range findNodes {
|
||||
enrs = append(enrs, r.String())
|
||||
}
|
||||
|
||||
return enrs, nil
|
||||
}
|
||||
|
||||
func (p *PortalProtocolAPI) FindContent(enr string, contentKey string) (interface{}, error) {
|
||||
n, err := enode.Parse(enode.ValidSchemes, enr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contentKeyBytes, err := hexutil.Decode(contentKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
flag, findContent, err := p.portalProtocol.findContent(n, contentKeyBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch flag {
|
||||
case portalwire.ContentRawSelector:
|
||||
contentInfo := &ContentInfo{
|
||||
Content: hexutil.Encode(findContent.([]byte)),
|
||||
UtpTransfer: false,
|
||||
}
|
||||
p.portalProtocol.Log.Trace("FindContent", "contentInfo", contentInfo)
|
||||
return contentInfo, nil
|
||||
case portalwire.ContentConnIdSelector:
|
||||
contentInfo := &ContentInfo{
|
||||
Content: hexutil.Encode(findContent.([]byte)),
|
||||
UtpTransfer: true,
|
||||
}
|
||||
p.portalProtocol.Log.Trace("FindContent", "contentInfo", contentInfo)
|
||||
return contentInfo, nil
|
||||
default:
|
||||
enrs := make([]string, 0)
|
||||
for _, r := range findContent.([]*enode.Node) {
|
||||
enrs = append(enrs, r.String())
|
||||
}
|
||||
|
||||
p.portalProtocol.Log.Trace("FindContent", "enrs", enrs)
|
||||
return &Enrs{
|
||||
Enrs: enrs,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PortalProtocolAPI) Offer(enr string, contentItems [][2]string) (string, error) {
|
||||
n, err := enode.Parse(enode.ValidSchemes, enr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
entries := make([]*ContentEntry, 0, len(contentItems))
|
||||
for _, contentItem := range contentItems {
|
||||
contentKey, err := hexutil.Decode(contentItem[0])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
contentValue, err := hexutil.Decode(contentItem[1])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
contentEntry := &ContentEntry{
|
||||
ContentKey: contentKey,
|
||||
Content: contentValue,
|
||||
}
|
||||
entries = append(entries, contentEntry)
|
||||
}
|
||||
|
||||
transientOfferRequest := &TransientOfferRequest{
|
||||
Contents: entries,
|
||||
}
|
||||
|
||||
offerReq := &OfferRequest{
|
||||
Kind: TransientOfferRequestKind,
|
||||
Request: transientOfferRequest,
|
||||
}
|
||||
accept, err := p.portalProtocol.offer(n, offerReq)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return hexutil.Encode(accept), nil
|
||||
}
|
||||
|
||||
func (p *PortalProtocolAPI) RecursiveFindNodes(nodeId string) ([]string, error) {
|
||||
findNodes := p.portalProtocol.Lookup(enode.HexID(nodeId))
|
||||
|
||||
enrs := make([]string, 0, len(findNodes))
|
||||
for _, r := range findNodes {
|
||||
enrs = append(enrs, r.String())
|
||||
}
|
||||
|
||||
return enrs, nil
|
||||
}
|
||||
|
||||
func (p *PortalProtocolAPI) RecursiveFindContent(contentKeyHex string) (*ContentInfo, error) {
|
||||
contentKey, err := hexutil.Decode(contentKeyHex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contentId := p.portalProtocol.toContentId(contentKey)
|
||||
|
||||
data, err := p.portalProtocol.Get(contentKey, contentId)
|
||||
if err == nil {
|
||||
return &ContentInfo{
|
||||
Content: hexutil.Encode(data),
|
||||
UtpTransfer: false,
|
||||
}, err
|
||||
}
|
||||
p.portalProtocol.Log.Warn("find content err", "contextKey", hexutil.Encode(contentKey), "err", err)
|
||||
|
||||
content, utpTransfer, err := p.portalProtocol.ContentLookup(contentKey, contentId)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ContentInfo{
|
||||
Content: hexutil.Encode(content),
|
||||
UtpTransfer: utpTransfer,
|
||||
}, err
|
||||
}
|
||||
|
||||
func (p *PortalProtocolAPI) LocalContent(contentKeyHex string) (string, error) {
|
||||
contentKey, err := hexutil.Decode(contentKeyHex)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
contentId := p.portalProtocol.ToContentId(contentKey)
|
||||
content, err := p.portalProtocol.Get(contentKey, contentId)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hexutil.Encode(content), nil
|
||||
}
|
||||
|
||||
func (p *PortalProtocolAPI) Store(contentKeyHex string, contextHex string) (bool, error) {
|
||||
contentKey, err := hexutil.Decode(contentKeyHex)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
contentId := p.portalProtocol.ToContentId(contentKey)
|
||||
if !p.portalProtocol.InRange(contentId) {
|
||||
return false, nil
|
||||
}
|
||||
content, err := hexutil.Decode(contextHex)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
err = p.portalProtocol.Put(contentKey, contentId, content)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (p *PortalProtocolAPI) Gossip(contentKeyHex, contentHex string) (int, error) {
|
||||
contentKey, err := hexutil.Decode(contentKeyHex)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
content, err := hexutil.Decode(contentHex)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
id := p.portalProtocol.Self().ID()
|
||||
return p.portalProtocol.Gossip(&id, [][]byte{contentKey}, [][]byte{content})
|
||||
}
|
||||
|
||||
func (p *PortalProtocolAPI) TraceRecursiveFindContent(contentKeyHex string) (*TraceContentResult, error) {
|
||||
contentKey, err := hexutil.Decode(contentKeyHex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contentId := p.portalProtocol.toContentId(contentKey)
|
||||
return p.portalProtocol.TraceContentLookup(contentKey, contentId)
|
||||
}
|
||||
172
portalnetwork/nat.go
Normal file
172
portalnetwork/nat.go
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
package portalnetwork
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/mclock"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/enr"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
)
|
||||
|
||||
const (
|
||||
portMapDuration = 10 * time.Minute
|
||||
portMapRefreshInterval = 8 * time.Minute
|
||||
portMapRetryInterval = 5 * time.Minute
|
||||
extipRetryInterval = 2 * time.Minute
|
||||
)
|
||||
|
||||
type portMapping struct {
|
||||
protocol string
|
||||
name string
|
||||
port int
|
||||
|
||||
// for use by the portMappingLoop goroutine:
|
||||
extPort int // the mapped port returned by the NAT interface
|
||||
nextTime mclock.AbsTime
|
||||
}
|
||||
|
||||
// setupPortMapping starts the port mapping loop if necessary.
|
||||
// Note: this needs to be called after the LocalNode instance has been set on the server.
|
||||
func (p *PortalProtocol) setupPortMapping() {
|
||||
// portMappingRegister will receive up to two values: one for the TCP port if
|
||||
// listening is enabled, and one more for enabling UDP port mapping if discovery is
|
||||
// enabled. We make it buffered to avoid blocking setup while a mapping request is in
|
||||
// progress.
|
||||
p.portMappingRegister = make(chan *portMapping, 2)
|
||||
|
||||
switch p.NAT.(type) {
|
||||
case nil:
|
||||
// No NAT interface configured.
|
||||
go p.consumePortMappingRequests()
|
||||
|
||||
case nat.ExtIP:
|
||||
// ExtIP doesn't block, set the IP right away.
|
||||
ip, _ := p.NAT.ExternalIP()
|
||||
p.localNode.SetStaticIP(ip)
|
||||
go p.consumePortMappingRequests()
|
||||
|
||||
case nat.STUN:
|
||||
// STUN doesn't block, set the IP right away.
|
||||
ip, _ := p.NAT.ExternalIP()
|
||||
p.localNode.SetStaticIP(ip)
|
||||
go p.consumePortMappingRequests()
|
||||
|
||||
default:
|
||||
go p.portMappingLoop()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PortalProtocol) consumePortMappingRequests() {
|
||||
for {
|
||||
select {
|
||||
case <-p.closeCtx.Done():
|
||||
return
|
||||
case <-p.portMappingRegister:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// portMappingLoop manages port mappings for UDP and TCP.
|
||||
func (p *PortalProtocol) portMappingLoop() {
|
||||
newLogger := func(proto string, e int, i int) log.Logger {
|
||||
return log.New("proto", proto, "extport", e, "intport", i, "interface", p.NAT)
|
||||
}
|
||||
|
||||
var (
|
||||
mappings = make(map[string]*portMapping, 2)
|
||||
refresh = mclock.NewAlarm(p.clock)
|
||||
extip = mclock.NewAlarm(p.clock)
|
||||
lastExtIP net.IP
|
||||
)
|
||||
extip.Schedule(p.clock.Now())
|
||||
defer func() {
|
||||
refresh.Stop()
|
||||
extip.Stop()
|
||||
for _, m := range mappings {
|
||||
if m.extPort != 0 {
|
||||
log := newLogger(m.protocol, m.extPort, m.port)
|
||||
log.Debug("Deleting port mapping")
|
||||
p.NAT.DeleteMapping(m.protocol, m.extPort, m.port)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
// Schedule refresh of existing mappings.
|
||||
for _, m := range mappings {
|
||||
refresh.Schedule(m.nextTime)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-p.closeCtx.Done():
|
||||
return
|
||||
|
||||
case <-extip.C():
|
||||
extip.Schedule(p.clock.Now().Add(extipRetryInterval))
|
||||
ip, err := p.NAT.ExternalIP()
|
||||
if err != nil {
|
||||
log.Debug("Couldn't get external IP", "err", err, "interface", p.NAT)
|
||||
} else if !ip.Equal(lastExtIP) {
|
||||
log.Debug("External IP changed", "ip", extip, "interface", p.NAT)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
// Here, we either failed to get the external IP, or it has changed.
|
||||
lastExtIP = ip
|
||||
p.localNode.SetStaticIP(ip)
|
||||
p.Log.Debug("set static ip in nat", "ip", p.localNode.Node().IP().String())
|
||||
// Ensure port mappings are refreshed in case we have moved to a new network.
|
||||
for _, m := range mappings {
|
||||
m.nextTime = p.clock.Now()
|
||||
}
|
||||
|
||||
case m := <-p.portMappingRegister:
|
||||
if m.protocol != "TCP" && m.protocol != "UDP" {
|
||||
panic("unknown NAT protocol name: " + m.protocol)
|
||||
}
|
||||
mappings[m.protocol] = m
|
||||
m.nextTime = p.clock.Now()
|
||||
|
||||
case <-refresh.C():
|
||||
for _, m := range mappings {
|
||||
if p.clock.Now() < m.nextTime {
|
||||
continue
|
||||
}
|
||||
|
||||
external := m.port
|
||||
if m.extPort != 0 {
|
||||
external = m.extPort
|
||||
}
|
||||
log := newLogger(m.protocol, external, m.port)
|
||||
|
||||
log.Trace("Attempting port mapping")
|
||||
port, err := p.NAT.AddMapping(m.protocol, external, m.port, m.name, portMapDuration)
|
||||
if err != nil {
|
||||
log.Debug("Couldn't add port mapping", "err", err)
|
||||
m.extPort = 0
|
||||
m.nextTime = p.clock.Now().Add(portMapRetryInterval)
|
||||
continue
|
||||
}
|
||||
// It was mapped!
|
||||
m.extPort = int(port)
|
||||
m.nextTime = p.clock.Now().Add(portMapRefreshInterval)
|
||||
if external != m.extPort {
|
||||
log = newLogger(m.protocol, m.extPort, m.port)
|
||||
log.Info("NAT mapped alternative port")
|
||||
} else {
|
||||
log.Info("NAT mapped port")
|
||||
}
|
||||
|
||||
// Update port in local ENR.
|
||||
switch m.protocol {
|
||||
case "TCP":
|
||||
p.localNode.Set(enr.TCP(m.extPort))
|
||||
case "UDP":
|
||||
p.localNode.SetFallbackUDP(m.extPort)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1918
portalnetwork/portal_protocol.go
Normal file
1918
portalnetwork/portal_protocol.go
Normal file
File diff suppressed because it is too large
Load diff
67
portalnetwork/portal_protocol_metrics.go
Normal file
67
portalnetwork/portal_protocol_metrics.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package portalnetwork
|
||||
|
||||
import "github.com/ethereum/go-ethereum/metrics"
|
||||
|
||||
type portalMetrics struct {
|
||||
messagesReceivedAccept metrics.Meter
|
||||
messagesReceivedNodes metrics.Meter
|
||||
messagesReceivedFindNodes metrics.Meter
|
||||
messagesReceivedFindContent metrics.Meter
|
||||
messagesReceivedContent metrics.Meter
|
||||
messagesReceivedOffer metrics.Meter
|
||||
messagesReceivedPing metrics.Meter
|
||||
messagesReceivedPong metrics.Meter
|
||||
|
||||
messagesSentAccept metrics.Meter
|
||||
messagesSentNodes metrics.Meter
|
||||
messagesSentFindNodes metrics.Meter
|
||||
messagesSentFindContent metrics.Meter
|
||||
messagesSentContent metrics.Meter
|
||||
messagesSentOffer metrics.Meter
|
||||
messagesSentPing metrics.Meter
|
||||
messagesSentPong metrics.Meter
|
||||
|
||||
utpInFailConn metrics.Counter
|
||||
utpInFailRead metrics.Counter
|
||||
utpInFailDeadline metrics.Counter
|
||||
utpInSuccess metrics.Counter
|
||||
|
||||
utpOutFailConn metrics.Counter
|
||||
utpOutFailWrite metrics.Counter
|
||||
utpOutFailDeadline metrics.Counter
|
||||
utpOutSuccess metrics.Counter
|
||||
|
||||
contentDecodedTrue metrics.Counter
|
||||
contentDecodedFalse metrics.Counter
|
||||
}
|
||||
|
||||
func newPortalMetrics(protocolName string) *portalMetrics {
|
||||
return &portalMetrics{
|
||||
messagesReceivedAccept: metrics.NewRegisteredMeter("portal/"+protocolName+"/received/accept", nil),
|
||||
messagesReceivedNodes: metrics.NewRegisteredMeter("portal/"+protocolName+"/received/nodes", nil),
|
||||
messagesReceivedFindNodes: metrics.NewRegisteredMeter("portal/"+protocolName+"/received/find_nodes", nil),
|
||||
messagesReceivedFindContent: metrics.NewRegisteredMeter("portal/"+protocolName+"/received/find_content", nil),
|
||||
messagesReceivedContent: metrics.NewRegisteredMeter("portal/"+protocolName+"/received/content", nil),
|
||||
messagesReceivedOffer: metrics.NewRegisteredMeter("portal/"+protocolName+"/received/offer", nil),
|
||||
messagesReceivedPing: metrics.NewRegisteredMeter("portal/"+protocolName+"/received/ping", nil),
|
||||
messagesReceivedPong: metrics.NewRegisteredMeter("portal/"+protocolName+"/received/pong", nil),
|
||||
messagesSentAccept: metrics.NewRegisteredMeter("portal/"+protocolName+"/sent/accept", nil),
|
||||
messagesSentNodes: metrics.NewRegisteredMeter("portal/"+protocolName+"/sent/nodes", nil),
|
||||
messagesSentFindNodes: metrics.NewRegisteredMeter("portal/"+protocolName+"/sent/find_nodes", nil),
|
||||
messagesSentFindContent: metrics.NewRegisteredMeter("portal/"+protocolName+"/sent/find_content", nil),
|
||||
messagesSentContent: metrics.NewRegisteredMeter("portal/"+protocolName+"/sent/content", nil),
|
||||
messagesSentOffer: metrics.NewRegisteredMeter("portal/"+protocolName+"/sent/offer", nil),
|
||||
messagesSentPing: metrics.NewRegisteredMeter("portal/"+protocolName+"/sent/ping", nil),
|
||||
messagesSentPong: metrics.NewRegisteredMeter("portal/"+protocolName+"/sent/pong", nil),
|
||||
utpInFailConn: metrics.NewRegisteredCounter("portal/"+protocolName+"/utp/inbound/fail_conn", nil),
|
||||
utpInFailRead: metrics.NewRegisteredCounter("portal/"+protocolName+"/utp/inbound/fail_read", nil),
|
||||
utpInFailDeadline: metrics.NewRegisteredCounter("portal/"+protocolName+"/utp/inbound/fail_deadline", nil),
|
||||
utpInSuccess: metrics.NewRegisteredCounter("portal/"+protocolName+"/utp/inbound/success", nil),
|
||||
utpOutFailConn: metrics.NewRegisteredCounter("portal/"+protocolName+"/utp/outbound/fail_conn", nil),
|
||||
utpOutFailWrite: metrics.NewRegisteredCounter("portal/"+protocolName+"/utp/outbound/fail_write", nil),
|
||||
utpOutFailDeadline: metrics.NewRegisteredCounter("portal/"+protocolName+"/utp/outbound/fail_deadline", nil),
|
||||
utpOutSuccess: metrics.NewRegisteredCounter("portal/"+protocolName+"/utp/outbound/success", nil),
|
||||
contentDecodedTrue: metrics.NewRegisteredCounter("portal/"+protocolName+"/content/decoded/true", nil),
|
||||
contentDecodedFalse: metrics.NewRegisteredCounter("portal/"+protocolName+"/content/decoded/false", nil),
|
||||
}
|
||||
}
|
||||
503
portalnetwork/portal_protocol_test.go
Normal file
503
portalnetwork/portal_protocol_test.go
Normal file
|
|
@ -0,0 +1,503 @@
|
|||
package portalnetwork
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/portalnetwork/storage"
|
||||
"github.com/optimism-java/utp-go"
|
||||
"github.com/optimism-java/utp-go/libutp"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/internal/testlog"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover/portalwire"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
assert "github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupLocalPortalNode(addr string, bootNodes []*enode.Node) (*PortalProtocol, error) {
|
||||
conf := DefaultPortalProtocolConfig()
|
||||
conf.NAT = nil
|
||||
if addr != "" {
|
||||
conf.ListenAddr = addr
|
||||
}
|
||||
if bootNodes != nil {
|
||||
conf.BootstrapNodes = bootNodes
|
||||
}
|
||||
|
||||
addr1, err := net.ResolveUDPAddr("udp", conf.ListenAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn, err := net.ListenUDP("udp", addr1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
privKey := newkey()
|
||||
|
||||
discCfg := Config{
|
||||
PrivateKey: privKey,
|
||||
NetRestrict: conf.NetRestrict,
|
||||
Bootnodes: conf.BootstrapNodes,
|
||||
}
|
||||
|
||||
nodeDB, err := enode.OpenDB(conf.NodeDBPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
localNode := enode.NewLocalNode(nodeDB, privKey)
|
||||
localNode.SetFallbackIP(net.IP{127, 0, 0, 1})
|
||||
localNode.Set(Tag)
|
||||
|
||||
if conf.NAT == nil {
|
||||
var addrs []net.Addr
|
||||
addrs, err = net.InterfaceAddrs()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, address := range addrs {
|
||||
// check ip addr is loopback addr
|
||||
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||
if ipnet.IP.To4() != nil {
|
||||
localNode.SetStaticIP(ipnet.IP)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
discV5, err := ListenV5(conn, localNode, discCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
utpSocket := NewPortalUtp(context.Background(), conf, discV5, conn)
|
||||
|
||||
contentQueue := make(chan *ContentElement, 50)
|
||||
portalProtocol, err := NewPortalProtocol(
|
||||
conf,
|
||||
portalwire.History,
|
||||
privKey,
|
||||
conn,
|
||||
localNode,
|
||||
discV5,
|
||||
utpSocket,
|
||||
&storage.MockStorage{Db: make(map[string][]byte)},
|
||||
contentQueue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return portalProtocol, nil
|
||||
}
|
||||
|
||||
func TestPortalWireProtocolUdp(t *testing.T) {
|
||||
node1, err := setupLocalPortalNode(":8777", nil)
|
||||
assert.NoError(t, err)
|
||||
node1.Log = testlog.Logger(t, log.LvlTrace)
|
||||
err = node1.Start()
|
||||
assert.NoError(t, err)
|
||||
|
||||
node2, err := setupLocalPortalNode(":8778", []*enode.Node{node1.localNode.Node()})
|
||||
assert.NoError(t, err)
|
||||
node2.Log = testlog.Logger(t, log.LvlTrace)
|
||||
err = node2.Start()
|
||||
assert.NoError(t, err)
|
||||
time.Sleep(12 * time.Second)
|
||||
|
||||
node3, err := setupLocalPortalNode(":8779", []*enode.Node{node1.localNode.Node()})
|
||||
assert.NoError(t, err)
|
||||
node3.Log = testlog.Logger(t, log.LvlTrace)
|
||||
err = node3.Start()
|
||||
assert.NoError(t, err)
|
||||
time.Sleep(12 * time.Second)
|
||||
|
||||
cid1 := libutp.ReceConnId(12)
|
||||
cid2 := libutp.ReceConnId(116)
|
||||
cliSendMsgWithCid1 := "there are connection id : 12!"
|
||||
cliSendMsgWithCid2 := "there are connection id: 116!"
|
||||
|
||||
serverEchoWithCid := "accept connection sends back msg: echo"
|
||||
|
||||
largeTestContent := make([]byte, 1199)
|
||||
_, err = rand.Read(largeTestContent)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var workGroup sync.WaitGroup
|
||||
var acceptGroup sync.WaitGroup
|
||||
workGroup.Add(4)
|
||||
acceptGroup.Add(1)
|
||||
go func() {
|
||||
var acceptConn *utp.Conn
|
||||
defer func() {
|
||||
workGroup.Done()
|
||||
_ = acceptConn.Close()
|
||||
}()
|
||||
acceptConn, err := node1.Utp.AcceptWithCid(context.Background(), node2.localNode.ID(), cid1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
acceptGroup.Done()
|
||||
buf := make([]byte, 100)
|
||||
n, err := acceptConn.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
panic(err)
|
||||
}
|
||||
assert.Equal(t, cliSendMsgWithCid1, string(buf[:n]))
|
||||
_, err = acceptConn.Write([]byte(serverEchoWithCid))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
var connId2Conn net.Conn
|
||||
defer func() {
|
||||
workGroup.Done()
|
||||
_ = connId2Conn.Close()
|
||||
}()
|
||||
connId2Conn, err := node1.Utp.AcceptWithCid(context.Background(), node2.localNode.ID(), cid2)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
buf := make([]byte, 100)
|
||||
n, err := connId2Conn.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
panic(err)
|
||||
}
|
||||
assert.Equal(t, cliSendMsgWithCid2, string(buf[:n]))
|
||||
|
||||
_, err = connId2Conn.Write(largeTestContent)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
var connWithConnId net.Conn
|
||||
defer func() {
|
||||
workGroup.Done()
|
||||
if connWithConnId != nil {
|
||||
_ = connWithConnId.Close()
|
||||
}
|
||||
}()
|
||||
connWithConnId, err = node2.Utp.DialWithCid(context.Background(), node1.localNode.Node(), cid1.SendId())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = connWithConnId.Write([]byte(cliSendMsgWithCid1))
|
||||
if err != nil && err != io.EOF {
|
||||
panic(err)
|
||||
}
|
||||
buf := make([]byte, 100)
|
||||
n, err := connWithConnId.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
panic(err)
|
||||
}
|
||||
assert.Equal(t, serverEchoWithCid, string(buf[:n]))
|
||||
}()
|
||||
go func() {
|
||||
var ConnId2Conn net.Conn
|
||||
defer func() {
|
||||
workGroup.Done()
|
||||
if ConnId2Conn != nil {
|
||||
_ = ConnId2Conn.Close()
|
||||
}
|
||||
}()
|
||||
ConnId2Conn, err = node2.Utp.DialWithCid(context.Background(), node1.localNode.Node(), cid2.SendId())
|
||||
if err != nil && err != io.EOF {
|
||||
panic(err)
|
||||
}
|
||||
_, err = ConnId2Conn.Write([]byte(cliSendMsgWithCid2))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
data := make([]byte, 0)
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
var n int
|
||||
n, err = ConnId2Conn.Read(buf)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
}
|
||||
data = append(data, buf[:n]...)
|
||||
}
|
||||
assert.Equal(t, largeTestContent, data)
|
||||
}()
|
||||
workGroup.Wait()
|
||||
node1.Stop()
|
||||
node2.Stop()
|
||||
node3.Stop()
|
||||
}
|
||||
|
||||
func TestPortalWireProtocol(t *testing.T) {
|
||||
node1, err := setupLocalPortalNode(":7777", nil)
|
||||
assert.NoError(t, err)
|
||||
node1.Log = testlog.Logger(t, log.LevelDebug)
|
||||
err = node1.Start()
|
||||
assert.NoError(t, err)
|
||||
|
||||
node2, err := setupLocalPortalNode(":7778", []*enode.Node{node1.localNode.Node()})
|
||||
assert.NoError(t, err)
|
||||
node2.Log = testlog.Logger(t, log.LevelDebug)
|
||||
err = node2.Start()
|
||||
assert.NoError(t, err)
|
||||
|
||||
time.Sleep(12 * time.Second)
|
||||
|
||||
node3, err := setupLocalPortalNode(":7779", []*enode.Node{node1.localNode.Node()})
|
||||
assert.NoError(t, err)
|
||||
node3.Log = testlog.Logger(t, log.LevelDebug)
|
||||
err = node3.Start()
|
||||
assert.NoError(t, err)
|
||||
|
||||
time.Sleep(12 * time.Second)
|
||||
|
||||
slices.ContainsFunc(node1.table.NodeList(), func(n *enode.Node) bool {
|
||||
return n.ID() == node2.localNode.Node().ID()
|
||||
})
|
||||
slices.ContainsFunc(node1.table.NodeList(), func(n *enode.Node) bool {
|
||||
return n.ID() == node3.localNode.Node().ID()
|
||||
})
|
||||
|
||||
slices.ContainsFunc(node2.table.NodeList(), func(n *enode.Node) bool {
|
||||
return n.ID() == node1.localNode.Node().ID()
|
||||
})
|
||||
slices.ContainsFunc(node2.table.NodeList(), func(n *enode.Node) bool {
|
||||
return n.ID() == node3.localNode.Node().ID()
|
||||
})
|
||||
|
||||
slices.ContainsFunc(node3.table.NodeList(), func(n *enode.Node) bool {
|
||||
return n.ID() == node1.localNode.Node().ID()
|
||||
})
|
||||
slices.ContainsFunc(node3.table.NodeList(), func(n *enode.Node) bool {
|
||||
return n.ID() == node2.localNode.Node().ID()
|
||||
})
|
||||
|
||||
err = node1.storage.Put(nil, node1.toContentId([]byte("test_key")), []byte("test_value"))
|
||||
assert.NoError(t, err)
|
||||
|
||||
flag, content, err := node2.findContent(node1.localNode.Node(), []byte("test_key"))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, portalwire.ContentRawSelector, flag)
|
||||
assert.Equal(t, []byte("test_value"), content)
|
||||
|
||||
flag, content, err = node2.findContent(node3.localNode.Node(), []byte("test_key"))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, portalwire.ContentEnrsSelector, flag)
|
||||
assert.Equal(t, 1, len(content.([]*enode.Node)))
|
||||
assert.Equal(t, node1.localNode.Node().ID(), content.([]*enode.Node)[0].ID())
|
||||
|
||||
// create a byte slice of length 1199 and fill it with random data
|
||||
// this will be used as a test content
|
||||
largeTestContent := make([]byte, 2000)
|
||||
_, err = rand.Read(largeTestContent)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = node1.storage.Put(nil, node1.toContentId([]byte("large_test_key")), largeTestContent)
|
||||
assert.NoError(t, err)
|
||||
|
||||
flag, content, err = node2.findContent(node1.localNode.Node(), []byte("large_test_key"))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, largeTestContent, content)
|
||||
assert.Equal(t, portalwire.ContentConnIdSelector, flag)
|
||||
|
||||
testEntry1 := &ContentEntry{
|
||||
ContentKey: []byte("test_entry1"),
|
||||
Content: []byte("test_entry1_content"),
|
||||
}
|
||||
|
||||
testEntry2 := &ContentEntry{
|
||||
ContentKey: []byte("test_entry2"),
|
||||
Content: []byte("test_entry2_content"),
|
||||
}
|
||||
|
||||
testTransientOfferRequest := &TransientOfferRequest{
|
||||
Contents: []*ContentEntry{testEntry1, testEntry2},
|
||||
}
|
||||
|
||||
offerRequest := &OfferRequest{
|
||||
Kind: TransientOfferRequestKind,
|
||||
Request: testTransientOfferRequest,
|
||||
}
|
||||
|
||||
contentKeys, err := node1.offer(node3.localNode.Node(), offerRequest)
|
||||
assert.Equal(t, uint64(2), bitfield.Bitlist(contentKeys).Count())
|
||||
assert.NoError(t, err)
|
||||
|
||||
contentElement := <-node3.contentQueue
|
||||
assert.Equal(t, node1.localNode.Node().ID(), contentElement.Node)
|
||||
assert.Equal(t, testEntry1.ContentKey, contentElement.ContentKeys[0])
|
||||
assert.Equal(t, testEntry1.Content, contentElement.Contents[0])
|
||||
assert.Equal(t, testEntry2.ContentKey, contentElement.ContentKeys[1])
|
||||
assert.Equal(t, testEntry2.Content, contentElement.Contents[1])
|
||||
|
||||
testGossipContentKeys := [][]byte{[]byte("test_gossip_content_keys"), []byte("test_gossip_content_keys2")}
|
||||
testGossipContent := [][]byte{[]byte("test_gossip_content"), []byte("test_gossip_content2")}
|
||||
id := node1.Self().ID()
|
||||
gossip, err := node1.Gossip(&id, testGossipContentKeys, testGossipContent)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, gossip)
|
||||
|
||||
contentElement = <-node2.contentQueue
|
||||
assert.Equal(t, node1.localNode.Node().ID(), contentElement.Node)
|
||||
assert.Equal(t, testGossipContentKeys[0], contentElement.ContentKeys[0])
|
||||
assert.Equal(t, testGossipContent[0], contentElement.Contents[0])
|
||||
assert.Equal(t, testGossipContentKeys[1], contentElement.ContentKeys[1])
|
||||
assert.Equal(t, testGossipContent[1], contentElement.Contents[1])
|
||||
|
||||
contentElement = <-node3.contentQueue
|
||||
assert.Equal(t, node1.localNode.Node().ID(), contentElement.Node)
|
||||
assert.Equal(t, testGossipContentKeys[0], contentElement.ContentKeys[0])
|
||||
assert.Equal(t, testGossipContent[0], contentElement.Contents[0])
|
||||
assert.Equal(t, testGossipContentKeys[1], contentElement.ContentKeys[1])
|
||||
assert.Equal(t, testGossipContent[1], contentElement.Contents[1])
|
||||
|
||||
node1.Stop()
|
||||
node2.Stop()
|
||||
node3.Stop()
|
||||
}
|
||||
|
||||
func TestCancel(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
go func(ctx context.Context) {
|
||||
defer func() {
|
||||
t.Log("goroutine cancel")
|
||||
}()
|
||||
|
||||
time.Sleep(time.Second * 5)
|
||||
}(ctx)
|
||||
|
||||
cancel()
|
||||
t.Log("after main cancel")
|
||||
|
||||
time.Sleep(time.Second * 3)
|
||||
}
|
||||
|
||||
func TestContentLookup(t *testing.T) {
|
||||
node1, err := setupLocalPortalNode(":17777", nil)
|
||||
assert.NoError(t, err)
|
||||
node1.Log = testlog.Logger(t, log.LvlTrace)
|
||||
err = node1.Start()
|
||||
assert.NoError(t, err)
|
||||
|
||||
node2, err := setupLocalPortalNode(":17778", []*enode.Node{node1.localNode.Node()})
|
||||
assert.NoError(t, err)
|
||||
node2.Log = testlog.Logger(t, log.LvlTrace)
|
||||
err = node2.Start()
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(node2.localNode.Node().String())
|
||||
|
||||
node3, err := setupLocalPortalNode(":17779", []*enode.Node{node1.localNode.Node()})
|
||||
assert.NoError(t, err)
|
||||
node3.Log = testlog.Logger(t, log.LvlTrace)
|
||||
err = node3.Start()
|
||||
assert.NoError(t, err)
|
||||
|
||||
defer func() {
|
||||
node1.Stop()
|
||||
node2.Stop()
|
||||
node3.Stop()
|
||||
}()
|
||||
|
||||
contentKey := []byte{0x3, 0x4}
|
||||
content := []byte{0x1, 0x2}
|
||||
contentId := node1.toContentId(contentKey)
|
||||
|
||||
err = node3.storage.Put(nil, contentId, content)
|
||||
assert.NoError(t, err)
|
||||
|
||||
res, _, err := node1.ContentLookup(contentKey, contentId)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, res, content)
|
||||
|
||||
nonExist := []byte{0x2, 0x4}
|
||||
res, _, err = node1.ContentLookup(nonExist, node1.toContentId(nonExist))
|
||||
assert.Equal(t, ContentNotFound, err)
|
||||
assert.Nil(t, res)
|
||||
}
|
||||
|
||||
func TestTraceContentLookup(t *testing.T) {
|
||||
node1, err := setupLocalPortalNode(":17787", nil)
|
||||
assert.NoError(t, err)
|
||||
node1.Log = testlog.Logger(t, log.LvlTrace)
|
||||
err = node1.Start()
|
||||
assert.NoError(t, err)
|
||||
|
||||
node2, err := setupLocalPortalNode(":17788", []*enode.Node{node1.localNode.Node()})
|
||||
assert.NoError(t, err)
|
||||
node2.Log = testlog.Logger(t, log.LvlTrace)
|
||||
err = node2.Start()
|
||||
assert.NoError(t, err)
|
||||
|
||||
node3, err := setupLocalPortalNode(":17789", []*enode.Node{node2.localNode.Node()})
|
||||
assert.NoError(t, err)
|
||||
node3.Log = testlog.Logger(t, log.LvlTrace)
|
||||
err = node3.Start()
|
||||
assert.NoError(t, err)
|
||||
|
||||
defer node1.Stop()
|
||||
defer node2.Stop()
|
||||
defer node3.Stop()
|
||||
|
||||
contentKey := []byte{0x3, 0x4}
|
||||
content := []byte{0x1, 0x2}
|
||||
contentId := node1.toContentId(contentKey)
|
||||
|
||||
err = node1.storage.Put(nil, contentId, content)
|
||||
assert.NoError(t, err)
|
||||
|
||||
node1Id := hexutil.Encode(node1.Self().ID().Bytes())
|
||||
node2Id := hexutil.Encode(node2.Self().ID().Bytes())
|
||||
node3Id := hexutil.Encode(node3.Self().ID().Bytes())
|
||||
|
||||
res, err := node3.TraceContentLookup(contentKey, contentId)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, res.Content, hexutil.Encode(content))
|
||||
assert.Equal(t, res.UtpTransfer, false)
|
||||
assert.Equal(t, res.Trace.Origin, node3Id)
|
||||
assert.Equal(t, res.Trace.TargetId, hexutil.Encode(contentId))
|
||||
assert.Equal(t, res.Trace.ReceivedFrom, node1Id)
|
||||
|
||||
// check nodeMeta
|
||||
node1Meta := res.Trace.Metadata[node1Id]
|
||||
assert.Equal(t, node1Meta.Enr, node1.Self().String())
|
||||
dis := node1.Distance(node1.Self().ID(), enode.ID(contentId))
|
||||
assert.Equal(t, node1Meta.Distance, hexutil.Encode(dis[:]))
|
||||
|
||||
node2Meta := res.Trace.Metadata[node2Id]
|
||||
assert.Equal(t, node2Meta.Enr, node2.Self().String())
|
||||
dis = node2.Distance(node2.Self().ID(), enode.ID(contentId))
|
||||
assert.Equal(t, node2Meta.Distance, hexutil.Encode(dis[:]))
|
||||
|
||||
node3Meta := res.Trace.Metadata[node3Id]
|
||||
assert.Equal(t, node3Meta.Enr, node3.Self().String())
|
||||
dis = node3.Distance(node3.Self().ID(), enode.ID(contentId))
|
||||
assert.Equal(t, node3Meta.Distance, hexutil.Encode(dis[:]))
|
||||
|
||||
// check response
|
||||
node3Response := res.Trace.Responses[node3Id]
|
||||
assert.Equal(t, node3Response.RespondedWith, []string{node2Id})
|
||||
|
||||
node2Response := res.Trace.Responses[node2Id]
|
||||
assert.Equal(t, node2Response.RespondedWith, []string{node1Id})
|
||||
|
||||
node1Response := res.Trace.Responses[node1Id]
|
||||
assert.Equal(t, node1Response.RespondedWith, ([]string)(nil))
|
||||
}
|
||||
139
portalnetwork/portal_utp.go
Normal file
139
portalnetwork/portal_utp.go
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
package portalnetwork
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover/v5wire"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
"github.com/ethereum/go-ethereum/portalnetwork/portalwire"
|
||||
"github.com/optimism-java/utp-go"
|
||||
"github.com/optimism-java/utp-go/libutp"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type PortalUtp struct {
|
||||
ctx context.Context
|
||||
log log.Logger
|
||||
discV5 *discover.UDPv5
|
||||
conn discover.UDPConn
|
||||
ListenAddr string
|
||||
listener *utp.Listener
|
||||
utpSm *utp.SocketManager
|
||||
packetRouter *utp.PacketRouter
|
||||
lAddr *utp.Addr
|
||||
|
||||
startOnce sync.Once
|
||||
}
|
||||
|
||||
func NewPortalUtp(ctx context.Context, config *PortalProtocolConfig, discV5 *discover.UDPv5, conn discover.UDPConn) *PortalUtp {
|
||||
return &PortalUtp{
|
||||
ctx: ctx,
|
||||
log: log.New("protocol", "utp", "local", conn.LocalAddr().String()),
|
||||
discV5: discV5,
|
||||
conn: conn,
|
||||
ListenAddr: config.ListenAddr,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PortalUtp) Start() error {
|
||||
var err error
|
||||
go p.startOnce.Do(func() {
|
||||
var logger *zap.Logger
|
||||
if p.log.Enabled(p.ctx, log.LevelDebug) || p.log.Enabled(p.ctx, log.LevelTrace) {
|
||||
logger, err = zap.NewDevelopmentConfig().Build()
|
||||
} else {
|
||||
logger, err = zap.NewProductionConfig().Build()
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
laddr := p.getLocalAddr()
|
||||
p.packetRouter = utp.NewPacketRouter(p.packetRouterFunc)
|
||||
p.utpSm, err = utp.NewSocketManagerWithOptions(
|
||||
"utp",
|
||||
laddr,
|
||||
utp.WithContext(p.ctx),
|
||||
utp.WithLogger(logger.Named(p.ListenAddr)),
|
||||
utp.WithPacketRouter(p.packetRouter),
|
||||
utp.WithMaxPacketSize(1145))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p.listener, err = utp.ListenUTPOptions("utp", (*utp.Addr)(laddr), utp.WithSocketManager(p.utpSm))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p.lAddr = p.listener.Addr().(*utp.Addr)
|
||||
|
||||
// register discv5 listener
|
||||
p.discV5.RegisterTalkHandler(string(portalwire.Utp), p.handleUtpTalkRequest)
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *PortalUtp) Stop() {
|
||||
err := p.listener.Close()
|
||||
if err != nil {
|
||||
p.log.Error("close utp listener has error", "error", err)
|
||||
}
|
||||
p.discV5.Close()
|
||||
}
|
||||
|
||||
func (p *PortalUtp) DialWithCid(ctx context.Context, dest *enode.Node, connId uint16) (net.Conn, error) {
|
||||
raddr := &utp.Addr{IP: dest.IP(), Port: dest.UDP()}
|
||||
p.log.Debug("will connect to: ", "nodeId", dest.ID().String(), "connId", connId)
|
||||
conn, err := utp.DialUTPOptions("utp", p.lAddr, raddr, utp.WithContext(ctx), utp.WithSocketManager(p.utpSm), utp.WithConnId(connId))
|
||||
return conn, err
|
||||
}
|
||||
|
||||
func (p *PortalUtp) Dial(ctx context.Context, dest *enode.Node) (net.Conn, error) {
|
||||
raddr := &utp.Addr{IP: dest.IP(), Port: dest.UDP()}
|
||||
p.log.Info("will connect to: ", "addr", raddr.String())
|
||||
conn, err := utp.DialUTPOptions("utp", p.lAddr, raddr, utp.WithContext(ctx), utp.WithSocketManager(p.utpSm))
|
||||
return conn, err
|
||||
}
|
||||
|
||||
func (p *PortalUtp) AcceptWithCid(ctx context.Context, nodeId enode.ID, cid *libutp.ConnId) (*utp.Conn, error) {
|
||||
p.log.Debug("will accept from: ", "nodeId", nodeId.String(), "sendId", cid.SendId(), "recvId", cid.RecvId())
|
||||
return p.listener.AcceptUTPContext(ctx, nodeId, cid)
|
||||
}
|
||||
|
||||
func (p *PortalUtp) Accept(ctx context.Context) (*utp.Conn, error) {
|
||||
return p.listener.AcceptUTPContext(ctx, enode.ID{}, nil)
|
||||
}
|
||||
|
||||
func (p *PortalUtp) getLocalAddr() *net.UDPAddr {
|
||||
laddr := p.conn.LocalAddr().(*net.UDPAddr)
|
||||
p.log.Debug("UDP listener up", "addr", laddr)
|
||||
return laddr
|
||||
}
|
||||
|
||||
func (p *PortalUtp) packetRouterFunc(buf []byte, id enode.ID, addr *net.UDPAddr) (int, error) {
|
||||
p.log.Info("will send to target data", "nodeId", id.String(), "ip", addr.IP.To4().String(), "port", addr.Port, "bufLength", len(buf))
|
||||
|
||||
if n, ok := p.discV5.GetCachedNode(addr.String()); ok {
|
||||
//_, err := p.DiscV5.TalkRequestToID(id, addr, string(portalwire.UTPNetwork), buf)
|
||||
req := &v5wire.TalkRequest{Protocol: string(portalwire.Utp), Message: buf}
|
||||
p.discV5.SendFromAnotherThreadWithNode(n, netip.AddrPortFrom(netutil.IPToAddr(addr.IP), uint16(addr.Port)), req)
|
||||
|
||||
return len(buf), nil
|
||||
} else {
|
||||
p.log.Warn("not found target node info", "ip", addr.IP.To4().String(), "port", addr.Port, "bufLength", len(buf))
|
||||
return 0, fmt.Errorf("not found target node id")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PortalUtp) handleUtpTalkRequest(id enode.ID, addr *net.UDPAddr, msg []byte) []byte {
|
||||
p.log.Trace("receive utp data", "nodeId", id.String(), "addr", addr, "msg-length", len(msg))
|
||||
p.packetRouter.ReceiveMessage(msg, &utp.NodeInfo{Id: id, Addr: addr})
|
||||
return []byte("")
|
||||
}
|
||||
336
portalnetwork/portalwire/messages.go
Normal file
336
portalnetwork/portalwire/messages.go
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
package portalwire
|
||||
|
||||
import (
|
||||
ssz "github.com/ferranbt/fastssz"
|
||||
)
|
||||
|
||||
// note: We changed the generated file since fastssz issues which can't be passed by the CI, so we commented the go:generate line
|
||||
///go:generate sszgen --path messages.go --exclude-objs Content,Enrs,ContentKV
|
||||
|
||||
// Message codes for the portal protocol.
|
||||
const (
|
||||
PING byte = 0x00
|
||||
PONG byte = 0x01
|
||||
FINDNODES byte = 0x02
|
||||
NODES byte = 0x03
|
||||
FINDCONTENT byte = 0x04
|
||||
CONTENT byte = 0x05
|
||||
OFFER byte = 0x06
|
||||
ACCEPT byte = 0x07
|
||||
)
|
||||
|
||||
// Content selectors for the portal protocol.
|
||||
const (
|
||||
ContentConnIdSelector byte = 0x00
|
||||
ContentRawSelector byte = 0x01
|
||||
ContentEnrsSelector byte = 0x02
|
||||
)
|
||||
|
||||
const (
|
||||
ContentKeysLimit = 64
|
||||
// OfferMessageOverhead overhead of content message is a result of 1byte for kind enum, and
|
||||
// 4 bytes for offset in ssz serialization
|
||||
OfferMessageOverhead = 5
|
||||
|
||||
// PerContentKeyOverhead each key in ContentKeysList has uint32 offset which results in 4 bytes per
|
||||
// key overhead when serialized
|
||||
PerContentKeyOverhead = 4
|
||||
)
|
||||
|
||||
// Protocol IDs for the portal protocol.
|
||||
// var (
|
||||
// StateNetwork = []byte{0x50, 0x0a}
|
||||
// HistoryNetwork = []byte{0x50, 0x0b}
|
||||
// TxGossipNetwork = []byte{0x50, 0x0c}
|
||||
// HeaderGossipNetwork = []byte{0x50, 0x0d}
|
||||
// CanonicalIndicesNetwork = []byte{0x50, 0x0e}
|
||||
// BeaconLightClientNetwork = []byte{0x50, 0x1a}
|
||||
// UTPNetwork = []byte{0x75, 0x74, 0x70}
|
||||
// Rendezvous = []byte{0x72, 0x65, 0x6e}
|
||||
// )
|
||||
|
||||
type ProtocolId []byte
|
||||
|
||||
var (
|
||||
State ProtocolId = []byte{0x50, 0x0A}
|
||||
History ProtocolId = []byte{0x50, 0x0B}
|
||||
Beacon ProtocolId = []byte{0x50, 0x0C}
|
||||
CanonicalIndices ProtocolId = []byte{0x50, 0x0D}
|
||||
VerkleState ProtocolId = []byte{0x50, 0x0E}
|
||||
TransactionGossip ProtocolId = []byte{0x50, 0x0F}
|
||||
Utp ProtocolId = []byte{0x75, 0x74, 0x70}
|
||||
)
|
||||
|
||||
var protocalName = map[string]string{
|
||||
string(State): "state",
|
||||
string(History): "history",
|
||||
string(Beacon): "beacon",
|
||||
string(CanonicalIndices): "canonical indices",
|
||||
string(VerkleState): "verkle state",
|
||||
string(TransactionGossip): "transaction gossip",
|
||||
}
|
||||
|
||||
func (p ProtocolId) Name() string {
|
||||
return protocalName[string(p)]
|
||||
}
|
||||
|
||||
// const (
|
||||
// HistoryNetworkName = "history"
|
||||
// BeaconNetworkName = "beacon"
|
||||
// StateNetworkName = "state"
|
||||
// )
|
||||
|
||||
// var NetworkNameMap = map[string]string{
|
||||
// string(StateNetwork): StateNetworkName,
|
||||
// string(HistoryNetwork): HistoryNetworkName,
|
||||
// string(BeaconLightClientNetwork): BeaconNetworkName,
|
||||
// }
|
||||
|
||||
type ContentKV struct {
|
||||
ContentKey []byte
|
||||
Content []byte
|
||||
}
|
||||
|
||||
// Request messages for the portal protocol.
|
||||
type (
|
||||
PingPongCustomData struct {
|
||||
Radius []byte `ssz-size:"32"`
|
||||
}
|
||||
|
||||
Ping struct {
|
||||
EnrSeq uint64
|
||||
CustomPayload []byte `ssz-max:"2048"`
|
||||
}
|
||||
|
||||
FindNodes struct {
|
||||
Distances [][2]byte `ssz-max:"256,2" ssz-size:"?,2"`
|
||||
}
|
||||
|
||||
FindContent struct {
|
||||
ContentKey []byte `ssz-max:"2048"`
|
||||
}
|
||||
|
||||
Offer struct {
|
||||
ContentKeys [][]byte `ssz-max:"64,2048"`
|
||||
}
|
||||
)
|
||||
|
||||
// Response messages for the portal protocol.
|
||||
type (
|
||||
Pong struct {
|
||||
EnrSeq uint64
|
||||
CustomPayload []byte `ssz-max:"2048"`
|
||||
}
|
||||
|
||||
Nodes struct {
|
||||
Total uint8
|
||||
Enrs [][]byte `ssz-max:"32,2048"`
|
||||
}
|
||||
|
||||
ConnectionId struct {
|
||||
Id []byte `ssz-size:"2"`
|
||||
}
|
||||
|
||||
Content struct {
|
||||
Content []byte `ssz-max:"2048"`
|
||||
}
|
||||
|
||||
Enrs struct {
|
||||
Enrs [][]byte `ssz-max:"32,2048"`
|
||||
}
|
||||
|
||||
Accept struct {
|
||||
ConnectionId []byte `ssz-size:"2"`
|
||||
ContentKeys []byte `ssz:"bitlist" ssz-max:"64"`
|
||||
}
|
||||
)
|
||||
|
||||
// MarshalSSZ ssz marshals the Content object
|
||||
func (c *Content) MarshalSSZ() ([]byte, error) {
|
||||
return ssz.MarshalSSZ(c)
|
||||
}
|
||||
|
||||
// MarshalSSZTo ssz marshals the Content object to a target array
|
||||
func (c *Content) MarshalSSZTo(buf []byte) (dst []byte, err error) {
|
||||
dst = buf
|
||||
|
||||
// Field (0) 'Content'
|
||||
if size := len(c.Content); size > 2048 {
|
||||
err = ssz.ErrBytesLengthFn("Content.Content", size, 2048)
|
||||
return
|
||||
}
|
||||
dst = append(dst, c.Content...)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalSSZ ssz unmarshals the Content object
|
||||
func (c *Content) UnmarshalSSZ(buf []byte) error {
|
||||
var err error
|
||||
tail := buf
|
||||
|
||||
// Field (0) 'Content'
|
||||
{
|
||||
buf = tail[:]
|
||||
if len(buf) > 2048 {
|
||||
return ssz.ErrBytesLength
|
||||
}
|
||||
if cap(c.Content) == 0 {
|
||||
c.Content = make([]byte, 0, len(buf))
|
||||
}
|
||||
c.Content = append(c.Content, buf...)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SizeSSZ returns the ssz encoded size in bytes for the Content object
|
||||
func (c *Content) SizeSSZ() (size int) {
|
||||
// Field (0) 'Content'
|
||||
return len(c.Content)
|
||||
}
|
||||
|
||||
// HashTreeRoot ssz hashes the Content object
|
||||
func (c *Content) HashTreeRoot() ([32]byte, error) {
|
||||
return ssz.HashWithDefaultHasher(c)
|
||||
}
|
||||
|
||||
// HashTreeRootWith ssz hashes the Content object with a hasher
|
||||
func (c *Content) HashTreeRootWith(hh ssz.HashWalker) (err error) {
|
||||
indx := hh.Index()
|
||||
|
||||
// Field (0) 'Content'
|
||||
{
|
||||
elemIndx := hh.Index()
|
||||
byteLen := uint64(len(c.Content))
|
||||
if byteLen > 2048 {
|
||||
err = ssz.ErrIncorrectListSize
|
||||
return
|
||||
}
|
||||
hh.Append(c.Content)
|
||||
hh.MerkleizeWithMixin(elemIndx, byteLen, (2048+31)/32)
|
||||
}
|
||||
|
||||
hh.Merkleize(indx)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTree ssz hashes the Content object
|
||||
func (c *Content) GetTree() (*ssz.Node, error) {
|
||||
return ssz.ProofTree(c)
|
||||
}
|
||||
|
||||
// MarshalSSZ ssz marshals the Enrs object
|
||||
func (e *Enrs) MarshalSSZ() ([]byte, error) {
|
||||
return ssz.MarshalSSZ(e)
|
||||
}
|
||||
|
||||
// MarshalSSZTo ssz marshals the Enrs object to a target array
|
||||
func (e *Enrs) MarshalSSZTo(buf []byte) (dst []byte, err error) {
|
||||
dst = buf
|
||||
offset := int(0)
|
||||
|
||||
// Field (0) 'Enrs'
|
||||
if size := len(e.Enrs); size > 32 {
|
||||
err = ssz.ErrListTooBigFn("Enrs.Enrs", size, 32)
|
||||
return
|
||||
}
|
||||
{
|
||||
offset = 4 * len(e.Enrs)
|
||||
for ii := 0; ii < len(e.Enrs); ii++ {
|
||||
dst = ssz.WriteOffset(dst, offset)
|
||||
offset += len(e.Enrs[ii])
|
||||
}
|
||||
}
|
||||
for ii := 0; ii < len(e.Enrs); ii++ {
|
||||
if size := len(e.Enrs[ii]); size > 2048 {
|
||||
err = ssz.ErrBytesLengthFn("Enrs.Enrs[ii]", size, 2048)
|
||||
return
|
||||
}
|
||||
dst = append(dst, e.Enrs[ii]...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalSSZ ssz unmarshals the Enrs object
|
||||
func (e *Enrs) UnmarshalSSZ(buf []byte) error {
|
||||
var err error
|
||||
tail := buf
|
||||
// Field (0) 'Enrs'
|
||||
{
|
||||
buf = tail[:]
|
||||
num, err := ssz.DecodeDynamicLength(buf, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.Enrs = make([][]byte, num)
|
||||
err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) {
|
||||
if len(buf) > 2048 {
|
||||
return ssz.ErrBytesLength
|
||||
}
|
||||
if cap(e.Enrs[indx]) == 0 {
|
||||
e.Enrs[indx] = make([]byte, 0, len(buf))
|
||||
}
|
||||
e.Enrs[indx] = append(e.Enrs[indx], buf...)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SizeSSZ returns the ssz encoded size in bytes for the Enrs object
|
||||
func (e *Enrs) SizeSSZ() (size int) {
|
||||
size = 0
|
||||
|
||||
// Field (0) 'Enrs'
|
||||
for ii := 0; ii < len(e.Enrs); ii++ {
|
||||
size += 4
|
||||
size += len(e.Enrs[ii])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// HashTreeRoot ssz hashes the Enrs object
|
||||
func (e *Enrs) HashTreeRoot() ([32]byte, error) {
|
||||
return ssz.HashWithDefaultHasher(e)
|
||||
}
|
||||
|
||||
// HashTreeRootWith ssz hashes the Enrs object with a hasher
|
||||
func (e *Enrs) HashTreeRootWith(hh ssz.HashWalker) (err error) {
|
||||
indx := hh.Index()
|
||||
|
||||
// Field (0) 'Enrs'
|
||||
{
|
||||
subIndx := hh.Index()
|
||||
num := uint64(len(e.Enrs))
|
||||
if num > 32 {
|
||||
err = ssz.ErrIncorrectListSize
|
||||
return
|
||||
}
|
||||
for _, elem := range e.Enrs {
|
||||
{
|
||||
elemIndx := hh.Index()
|
||||
byteLen := uint64(len(elem))
|
||||
if byteLen > 2048 {
|
||||
err = ssz.ErrIncorrectListSize
|
||||
return
|
||||
}
|
||||
hh.AppendBytes32(elem)
|
||||
hh.MerkleizeWithMixin(elemIndx, byteLen, (2048+31)/32)
|
||||
}
|
||||
}
|
||||
hh.MerkleizeWithMixin(subIndx, num, 32)
|
||||
}
|
||||
|
||||
hh.Merkleize(indx)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTree ssz hashes the Enrs object
|
||||
func (e *Enrs) GetTree() (*ssz.Node, error) {
|
||||
return ssz.ProofTree(e)
|
||||
}
|
||||
957
portalnetwork/portalwire/messages_encoding.go
Normal file
957
portalnetwork/portalwire/messages_encoding.go
Normal file
|
|
@ -0,0 +1,957 @@
|
|||
// Code generated by fastssz. DO NOT EDIT.
|
||||
// Hash: 26a61b12807ff78c64a029acdd5bcb580dfe35b7bfbf8bf04ceebae1a3d5cac1
|
||||
// Version: 0.1.3
|
||||
package portalwire
|
||||
|
||||
import (
|
||||
ssz "github.com/ferranbt/fastssz"
|
||||
)
|
||||
|
||||
// MarshalSSZ ssz marshals the PingPongCustomData object
|
||||
func (p *PingPongCustomData) MarshalSSZ() ([]byte, error) {
|
||||
return ssz.MarshalSSZ(p)
|
||||
}
|
||||
|
||||
// MarshalSSZTo ssz marshals the PingPongCustomData object to a target array
|
||||
func (p *PingPongCustomData) MarshalSSZTo(buf []byte) (dst []byte, err error) {
|
||||
dst = buf
|
||||
|
||||
// Field (0) 'Radius'
|
||||
if size := len(p.Radius); size != 32 {
|
||||
err = ssz.ErrBytesLengthFn("PingPongCustomData.Radius", size, 32)
|
||||
return
|
||||
}
|
||||
dst = append(dst, p.Radius...)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalSSZ ssz unmarshals the PingPongCustomData object
|
||||
func (p *PingPongCustomData) UnmarshalSSZ(buf []byte) error {
|
||||
var err error
|
||||
size := uint64(len(buf))
|
||||
if size != 32 {
|
||||
return ssz.ErrSize
|
||||
}
|
||||
|
||||
// Field (0) 'Radius'
|
||||
if cap(p.Radius) == 0 {
|
||||
p.Radius = make([]byte, 0, len(buf[0:32]))
|
||||
}
|
||||
p.Radius = append(p.Radius, buf[0:32]...)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// SizeSSZ returns the ssz encoded size in bytes for the PingPongCustomData object
|
||||
func (p *PingPongCustomData) SizeSSZ() (size int) {
|
||||
size = 32
|
||||
return
|
||||
}
|
||||
|
||||
// HashTreeRoot ssz hashes the PingPongCustomData object
|
||||
func (p *PingPongCustomData) HashTreeRoot() ([32]byte, error) {
|
||||
return ssz.HashWithDefaultHasher(p)
|
||||
}
|
||||
|
||||
// HashTreeRootWith ssz hashes the PingPongCustomData object with a hasher
|
||||
func (p *PingPongCustomData) HashTreeRootWith(hh ssz.HashWalker) (err error) {
|
||||
indx := hh.Index()
|
||||
|
||||
// Field (0) 'Radius'
|
||||
if size := len(p.Radius); size != 32 {
|
||||
err = ssz.ErrBytesLengthFn("PingPongCustomData.Radius", size, 32)
|
||||
return
|
||||
}
|
||||
hh.PutBytes(p.Radius)
|
||||
|
||||
hh.Merkleize(indx)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTree ssz hashes the PingPongCustomData object
|
||||
func (p *PingPongCustomData) GetTree() (*ssz.Node, error) {
|
||||
return ssz.ProofTree(p)
|
||||
}
|
||||
|
||||
// MarshalSSZ ssz marshals the Ping object
|
||||
func (p *Ping) MarshalSSZ() ([]byte, error) {
|
||||
return ssz.MarshalSSZ(p)
|
||||
}
|
||||
|
||||
// MarshalSSZTo ssz marshals the Ping object to a target array
|
||||
func (p *Ping) MarshalSSZTo(buf []byte) (dst []byte, err error) {
|
||||
dst = buf
|
||||
offset := int(12)
|
||||
|
||||
// Field (0) 'EnrSeq'
|
||||
dst = ssz.MarshalUint64(dst, p.EnrSeq)
|
||||
|
||||
// Offset (1) 'CustomPayload'
|
||||
dst = ssz.WriteOffset(dst, offset)
|
||||
offset += len(p.CustomPayload)
|
||||
|
||||
// Field (1) 'CustomPayload'
|
||||
if size := len(p.CustomPayload); size > 2048 {
|
||||
err = ssz.ErrBytesLengthFn("Ping.CustomPayload", size, 2048)
|
||||
return
|
||||
}
|
||||
dst = append(dst, p.CustomPayload...)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalSSZ ssz unmarshals the Ping object
|
||||
func (p *Ping) UnmarshalSSZ(buf []byte) error {
|
||||
var err error
|
||||
size := uint64(len(buf))
|
||||
if size < 12 {
|
||||
return ssz.ErrSize
|
||||
}
|
||||
|
||||
tail := buf
|
||||
var o1 uint64
|
||||
|
||||
// Field (0) 'EnrSeq'
|
||||
p.EnrSeq = ssz.UnmarshallUint64(buf[0:8])
|
||||
|
||||
// Offset (1) 'CustomPayload'
|
||||
if o1 = ssz.ReadOffset(buf[8:12]); o1 > size {
|
||||
return ssz.ErrOffset
|
||||
}
|
||||
|
||||
if o1 < 12 {
|
||||
return ssz.ErrInvalidVariableOffset
|
||||
}
|
||||
|
||||
// Field (1) 'CustomPayload'
|
||||
{
|
||||
buf = tail[o1:]
|
||||
if len(buf) > 2048 {
|
||||
return ssz.ErrBytesLength
|
||||
}
|
||||
if cap(p.CustomPayload) == 0 {
|
||||
p.CustomPayload = make([]byte, 0, len(buf))
|
||||
}
|
||||
p.CustomPayload = append(p.CustomPayload, buf...)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SizeSSZ returns the ssz encoded size in bytes for the Ping object
|
||||
func (p *Ping) SizeSSZ() (size int) {
|
||||
size = 12
|
||||
|
||||
// Field (1) 'CustomPayload'
|
||||
size += len(p.CustomPayload)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// HashTreeRoot ssz hashes the Ping object
|
||||
func (p *Ping) HashTreeRoot() ([32]byte, error) {
|
||||
return ssz.HashWithDefaultHasher(p)
|
||||
}
|
||||
|
||||
// HashTreeRootWith ssz hashes the Ping object with a hasher
|
||||
func (p *Ping) HashTreeRootWith(hh ssz.HashWalker) (err error) {
|
||||
indx := hh.Index()
|
||||
|
||||
// Field (0) 'EnrSeq'
|
||||
hh.PutUint64(p.EnrSeq)
|
||||
|
||||
// Field (1) 'CustomPayload'
|
||||
{
|
||||
elemIndx := hh.Index()
|
||||
byteLen := uint64(len(p.CustomPayload))
|
||||
if byteLen > 2048 {
|
||||
err = ssz.ErrIncorrectListSize
|
||||
return
|
||||
}
|
||||
hh.Append(p.CustomPayload)
|
||||
hh.MerkleizeWithMixin(elemIndx, byteLen, (2048+31)/32)
|
||||
}
|
||||
|
||||
hh.Merkleize(indx)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTree ssz hashes the Ping object
|
||||
func (p *Ping) GetTree() (*ssz.Node, error) {
|
||||
return ssz.ProofTree(p)
|
||||
}
|
||||
|
||||
// MarshalSSZ ssz marshals the FindNodes object
|
||||
func (f *FindNodes) MarshalSSZ() ([]byte, error) {
|
||||
return ssz.MarshalSSZ(f)
|
||||
}
|
||||
|
||||
// MarshalSSZTo ssz marshals the FindNodes object to a target array
|
||||
func (f *FindNodes) MarshalSSZTo(buf []byte) (dst []byte, err error) {
|
||||
dst = buf
|
||||
offset := int(4)
|
||||
|
||||
// Offset (0) 'Distances'
|
||||
dst = ssz.WriteOffset(dst, offset)
|
||||
offset += len(f.Distances) * 2
|
||||
|
||||
// Field (0) 'Distances'
|
||||
if size := len(f.Distances); size > 256 {
|
||||
err = ssz.ErrListTooBigFn("FindNodes.Distances", size, 256)
|
||||
return
|
||||
}
|
||||
for ii := 0; ii < len(f.Distances); ii++ {
|
||||
dst = append(dst, f.Distances[ii][:]...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalSSZ ssz unmarshals the FindNodes object
|
||||
func (f *FindNodes) UnmarshalSSZ(buf []byte) error {
|
||||
var err error
|
||||
size := uint64(len(buf))
|
||||
if size < 4 {
|
||||
return ssz.ErrSize
|
||||
}
|
||||
|
||||
tail := buf
|
||||
var o0 uint64
|
||||
|
||||
// Offset (0) 'Distances'
|
||||
if o0 = ssz.ReadOffset(buf[0:4]); o0 > size {
|
||||
return ssz.ErrOffset
|
||||
}
|
||||
|
||||
if o0 < 4 {
|
||||
return ssz.ErrInvalidVariableOffset
|
||||
}
|
||||
|
||||
// Field (0) 'Distances'
|
||||
{
|
||||
buf = tail[o0:]
|
||||
num, err := ssz.DivideInt2(len(buf), 2, 256)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Distances = make([][2]byte, num)
|
||||
for ii := 0; ii < num; ii++ {
|
||||
copy(f.Distances[ii][:], buf[ii*2:(ii+1)*2])
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SizeSSZ returns the ssz encoded size in bytes for the FindNodes object
|
||||
func (f *FindNodes) SizeSSZ() (size int) {
|
||||
size = 4
|
||||
|
||||
// Field (0) 'Distances'
|
||||
size += len(f.Distances) * 2
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// HashTreeRoot ssz hashes the FindNodes object
|
||||
func (f *FindNodes) HashTreeRoot() ([32]byte, error) {
|
||||
return ssz.HashWithDefaultHasher(f)
|
||||
}
|
||||
|
||||
// HashTreeRootWith ssz hashes the FindNodes object with a hasher
|
||||
func (f *FindNodes) HashTreeRootWith(hh ssz.HashWalker) (err error) {
|
||||
indx := hh.Index()
|
||||
|
||||
// Field (0) 'Distances'
|
||||
{
|
||||
if size := len(f.Distances); size > 256 {
|
||||
err = ssz.ErrListTooBigFn("FindNodes.Distances", size, 256)
|
||||
return
|
||||
}
|
||||
subIndx := hh.Index()
|
||||
for _, i := range f.Distances {
|
||||
hh.PutBytes(i[:])
|
||||
}
|
||||
numItems := uint64(len(f.Distances))
|
||||
hh.MerkleizeWithMixin(subIndx, numItems, 256)
|
||||
}
|
||||
|
||||
hh.Merkleize(indx)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTree ssz hashes the FindNodes object
|
||||
func (f *FindNodes) GetTree() (*ssz.Node, error) {
|
||||
return ssz.ProofTree(f)
|
||||
}
|
||||
|
||||
// MarshalSSZ ssz marshals the FindContent object
|
||||
func (f *FindContent) MarshalSSZ() ([]byte, error) {
|
||||
return ssz.MarshalSSZ(f)
|
||||
}
|
||||
|
||||
// MarshalSSZTo ssz marshals the FindContent object to a target array
|
||||
func (f *FindContent) MarshalSSZTo(buf []byte) (dst []byte, err error) {
|
||||
dst = buf
|
||||
offset := int(4)
|
||||
|
||||
// Offset (0) 'ContentKey'
|
||||
dst = ssz.WriteOffset(dst, offset)
|
||||
offset += len(f.ContentKey)
|
||||
|
||||
// Field (0) 'ContentKey'
|
||||
if size := len(f.ContentKey); size > 2048 {
|
||||
err = ssz.ErrBytesLengthFn("FindContent.ContentKey", size, 2048)
|
||||
return
|
||||
}
|
||||
dst = append(dst, f.ContentKey...)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalSSZ ssz unmarshals the FindContent object
|
||||
func (f *FindContent) UnmarshalSSZ(buf []byte) error {
|
||||
var err error
|
||||
size := uint64(len(buf))
|
||||
if size < 4 {
|
||||
return ssz.ErrSize
|
||||
}
|
||||
|
||||
tail := buf
|
||||
var o0 uint64
|
||||
|
||||
// Offset (0) 'ContentKey'
|
||||
if o0 = ssz.ReadOffset(buf[0:4]); o0 > size {
|
||||
return ssz.ErrOffset
|
||||
}
|
||||
|
||||
if o0 < 4 {
|
||||
return ssz.ErrInvalidVariableOffset
|
||||
}
|
||||
|
||||
// Field (0) 'ContentKey'
|
||||
{
|
||||
buf = tail[o0:]
|
||||
if len(buf) > 2048 {
|
||||
return ssz.ErrBytesLength
|
||||
}
|
||||
if cap(f.ContentKey) == 0 {
|
||||
f.ContentKey = make([]byte, 0, len(buf))
|
||||
}
|
||||
f.ContentKey = append(f.ContentKey, buf...)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SizeSSZ returns the ssz encoded size in bytes for the FindContent object
|
||||
func (f *FindContent) SizeSSZ() (size int) {
|
||||
size = 4
|
||||
|
||||
// Field (0) 'ContentKey'
|
||||
size += len(f.ContentKey)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// HashTreeRoot ssz hashes the FindContent object
|
||||
func (f *FindContent) HashTreeRoot() ([32]byte, error) {
|
||||
return ssz.HashWithDefaultHasher(f)
|
||||
}
|
||||
|
||||
// HashTreeRootWith ssz hashes the FindContent object with a hasher
|
||||
func (f *FindContent) HashTreeRootWith(hh ssz.HashWalker) (err error) {
|
||||
indx := hh.Index()
|
||||
|
||||
// Field (0) 'ContentKey'
|
||||
{
|
||||
elemIndx := hh.Index()
|
||||
byteLen := uint64(len(f.ContentKey))
|
||||
if byteLen > 2048 {
|
||||
err = ssz.ErrIncorrectListSize
|
||||
return
|
||||
}
|
||||
hh.Append(f.ContentKey)
|
||||
hh.MerkleizeWithMixin(elemIndx, byteLen, (2048+31)/32)
|
||||
}
|
||||
|
||||
hh.Merkleize(indx)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTree ssz hashes the FindContent object
|
||||
func (f *FindContent) GetTree() (*ssz.Node, error) {
|
||||
return ssz.ProofTree(f)
|
||||
}
|
||||
|
||||
// MarshalSSZ ssz marshals the Offer object
|
||||
func (o *Offer) MarshalSSZ() ([]byte, error) {
|
||||
return ssz.MarshalSSZ(o)
|
||||
}
|
||||
|
||||
// MarshalSSZTo ssz marshals the Offer object to a target array
|
||||
func (o *Offer) MarshalSSZTo(buf []byte) (dst []byte, err error) {
|
||||
dst = buf
|
||||
offset := int(4)
|
||||
|
||||
// Offset (0) 'ContentKeys'
|
||||
dst = ssz.WriteOffset(dst, offset)
|
||||
for ii := 0; ii < len(o.ContentKeys); ii++ {
|
||||
offset += 4
|
||||
offset += len(o.ContentKeys[ii])
|
||||
}
|
||||
|
||||
// Field (0) 'ContentKeys'
|
||||
if size := len(o.ContentKeys); size > 64 {
|
||||
err = ssz.ErrListTooBigFn("Offer.ContentKeys", size, 64)
|
||||
return
|
||||
}
|
||||
{
|
||||
offset = 4 * len(o.ContentKeys)
|
||||
for ii := 0; ii < len(o.ContentKeys); ii++ {
|
||||
dst = ssz.WriteOffset(dst, offset)
|
||||
offset += len(o.ContentKeys[ii])
|
||||
}
|
||||
}
|
||||
for ii := 0; ii < len(o.ContentKeys); ii++ {
|
||||
if size := len(o.ContentKeys[ii]); size > 2048 {
|
||||
err = ssz.ErrBytesLengthFn("Offer.ContentKeys[ii]", size, 2048)
|
||||
return
|
||||
}
|
||||
dst = append(dst, o.ContentKeys[ii]...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalSSZ ssz unmarshals the Offer object
|
||||
func (o *Offer) UnmarshalSSZ(buf []byte) error {
|
||||
var err error
|
||||
size := uint64(len(buf))
|
||||
if size < 4 {
|
||||
return ssz.ErrSize
|
||||
}
|
||||
|
||||
tail := buf
|
||||
var o0 uint64
|
||||
|
||||
// Offset (0) 'ContentKeys'
|
||||
if o0 = ssz.ReadOffset(buf[0:4]); o0 > size {
|
||||
return ssz.ErrOffset
|
||||
}
|
||||
|
||||
if o0 < 4 {
|
||||
return ssz.ErrInvalidVariableOffset
|
||||
}
|
||||
|
||||
// Field (0) 'ContentKeys'
|
||||
{
|
||||
buf = tail[o0:]
|
||||
num, err := ssz.DecodeDynamicLength(buf, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.ContentKeys = make([][]byte, num)
|
||||
err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) {
|
||||
if len(buf) > 2048 {
|
||||
return ssz.ErrBytesLength
|
||||
}
|
||||
if cap(o.ContentKeys[indx]) == 0 {
|
||||
o.ContentKeys[indx] = make([]byte, 0, len(buf))
|
||||
}
|
||||
o.ContentKeys[indx] = append(o.ContentKeys[indx], buf...)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SizeSSZ returns the ssz encoded size in bytes for the Offer object
|
||||
func (o *Offer) SizeSSZ() (size int) {
|
||||
size = 4
|
||||
|
||||
// Field (0) 'ContentKeys'
|
||||
for ii := 0; ii < len(o.ContentKeys); ii++ {
|
||||
size += 4
|
||||
size += len(o.ContentKeys[ii])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// HashTreeRoot ssz hashes the Offer object
|
||||
func (o *Offer) HashTreeRoot() ([32]byte, error) {
|
||||
return ssz.HashWithDefaultHasher(o)
|
||||
}
|
||||
|
||||
// HashTreeRootWith ssz hashes the Offer object with a hasher
|
||||
func (o *Offer) HashTreeRootWith(hh ssz.HashWalker) (err error) {
|
||||
indx := hh.Index()
|
||||
|
||||
// Field (0) 'ContentKeys'
|
||||
{
|
||||
subIndx := hh.Index()
|
||||
num := uint64(len(o.ContentKeys))
|
||||
if num > 64 {
|
||||
err = ssz.ErrIncorrectListSize
|
||||
return
|
||||
}
|
||||
for _, elem := range o.ContentKeys {
|
||||
{
|
||||
elemIndx := hh.Index()
|
||||
byteLen := uint64(len(elem))
|
||||
if byteLen > 2048 {
|
||||
err = ssz.ErrIncorrectListSize
|
||||
return
|
||||
}
|
||||
hh.AppendBytes32(elem)
|
||||
hh.MerkleizeWithMixin(elemIndx, byteLen, (2048+31)/32)
|
||||
}
|
||||
}
|
||||
hh.MerkleizeWithMixin(subIndx, num, 64)
|
||||
}
|
||||
|
||||
hh.Merkleize(indx)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTree ssz hashes the Offer object
|
||||
func (o *Offer) GetTree() (*ssz.Node, error) {
|
||||
return ssz.ProofTree(o)
|
||||
}
|
||||
|
||||
// MarshalSSZ ssz marshals the Pong object
|
||||
func (p *Pong) MarshalSSZ() ([]byte, error) {
|
||||
return ssz.MarshalSSZ(p)
|
||||
}
|
||||
|
||||
// MarshalSSZTo ssz marshals the Pong object to a target array
|
||||
func (p *Pong) MarshalSSZTo(buf []byte) (dst []byte, err error) {
|
||||
dst = buf
|
||||
offset := int(12)
|
||||
|
||||
// Field (0) 'EnrSeq'
|
||||
dst = ssz.MarshalUint64(dst, p.EnrSeq)
|
||||
|
||||
// Offset (1) 'CustomPayload'
|
||||
dst = ssz.WriteOffset(dst, offset)
|
||||
offset += len(p.CustomPayload)
|
||||
|
||||
// Field (1) 'CustomPayload'
|
||||
if size := len(p.CustomPayload); size > 2048 {
|
||||
err = ssz.ErrBytesLengthFn("Pong.CustomPayload", size, 2048)
|
||||
return
|
||||
}
|
||||
dst = append(dst, p.CustomPayload...)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalSSZ ssz unmarshals the Pong object
|
||||
func (p *Pong) UnmarshalSSZ(buf []byte) error {
|
||||
var err error
|
||||
size := uint64(len(buf))
|
||||
if size < 12 {
|
||||
return ssz.ErrSize
|
||||
}
|
||||
|
||||
tail := buf
|
||||
var o1 uint64
|
||||
|
||||
// Field (0) 'EnrSeq'
|
||||
p.EnrSeq = ssz.UnmarshallUint64(buf[0:8])
|
||||
|
||||
// Offset (1) 'CustomPayload'
|
||||
if o1 = ssz.ReadOffset(buf[8:12]); o1 > size {
|
||||
return ssz.ErrOffset
|
||||
}
|
||||
|
||||
if o1 < 12 {
|
||||
return ssz.ErrInvalidVariableOffset
|
||||
}
|
||||
|
||||
// Field (1) 'CustomPayload'
|
||||
{
|
||||
buf = tail[o1:]
|
||||
if len(buf) > 2048 {
|
||||
return ssz.ErrBytesLength
|
||||
}
|
||||
if cap(p.CustomPayload) == 0 {
|
||||
p.CustomPayload = make([]byte, 0, len(buf))
|
||||
}
|
||||
p.CustomPayload = append(p.CustomPayload, buf...)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SizeSSZ returns the ssz encoded size in bytes for the Pong object
|
||||
func (p *Pong) SizeSSZ() (size int) {
|
||||
size = 12
|
||||
|
||||
// Field (1) 'CustomPayload'
|
||||
size += len(p.CustomPayload)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// HashTreeRoot ssz hashes the Pong object
|
||||
func (p *Pong) HashTreeRoot() ([32]byte, error) {
|
||||
return ssz.HashWithDefaultHasher(p)
|
||||
}
|
||||
|
||||
// HashTreeRootWith ssz hashes the Pong object with a hasher
|
||||
func (p *Pong) HashTreeRootWith(hh ssz.HashWalker) (err error) {
|
||||
indx := hh.Index()
|
||||
|
||||
// Field (0) 'EnrSeq'
|
||||
hh.PutUint64(p.EnrSeq)
|
||||
|
||||
// Field (1) 'CustomPayload'
|
||||
{
|
||||
elemIndx := hh.Index()
|
||||
byteLen := uint64(len(p.CustomPayload))
|
||||
if byteLen > 2048 {
|
||||
err = ssz.ErrIncorrectListSize
|
||||
return
|
||||
}
|
||||
hh.Append(p.CustomPayload)
|
||||
hh.MerkleizeWithMixin(elemIndx, byteLen, (2048+31)/32)
|
||||
}
|
||||
|
||||
hh.Merkleize(indx)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTree ssz hashes the Pong object
|
||||
func (p *Pong) GetTree() (*ssz.Node, error) {
|
||||
return ssz.ProofTree(p)
|
||||
}
|
||||
|
||||
// MarshalSSZ ssz marshals the Nodes object
|
||||
func (n *Nodes) MarshalSSZ() ([]byte, error) {
|
||||
return ssz.MarshalSSZ(n)
|
||||
}
|
||||
|
||||
// MarshalSSZTo ssz marshals the Nodes object to a target array
|
||||
func (n *Nodes) MarshalSSZTo(buf []byte) (dst []byte, err error) {
|
||||
dst = buf
|
||||
offset := int(5)
|
||||
|
||||
// Field (0) 'Total'
|
||||
dst = ssz.MarshalUint8(dst, n.Total)
|
||||
|
||||
// Offset (1) 'Enrs'
|
||||
dst = ssz.WriteOffset(dst, offset)
|
||||
for ii := 0; ii < len(n.Enrs); ii++ {
|
||||
offset += 4
|
||||
offset += len(n.Enrs[ii])
|
||||
}
|
||||
|
||||
// Field (1) 'Enrs'
|
||||
if size := len(n.Enrs); size > 32 {
|
||||
err = ssz.ErrListTooBigFn("Nodes.Enrs", size, 32)
|
||||
return
|
||||
}
|
||||
{
|
||||
offset = 4 * len(n.Enrs)
|
||||
for ii := 0; ii < len(n.Enrs); ii++ {
|
||||
dst = ssz.WriteOffset(dst, offset)
|
||||
offset += len(n.Enrs[ii])
|
||||
}
|
||||
}
|
||||
for ii := 0; ii < len(n.Enrs); ii++ {
|
||||
if size := len(n.Enrs[ii]); size > 2048 {
|
||||
err = ssz.ErrBytesLengthFn("Nodes.Enrs[ii]", size, 2048)
|
||||
return
|
||||
}
|
||||
dst = append(dst, n.Enrs[ii]...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalSSZ ssz unmarshals the Nodes object
|
||||
func (n *Nodes) UnmarshalSSZ(buf []byte) error {
|
||||
var err error
|
||||
size := uint64(len(buf))
|
||||
if size < 5 {
|
||||
return ssz.ErrSize
|
||||
}
|
||||
|
||||
tail := buf
|
||||
var o1 uint64
|
||||
|
||||
// Field (0) 'Total'
|
||||
n.Total = ssz.UnmarshallUint8(buf[0:1])
|
||||
|
||||
// Offset (1) 'Enrs'
|
||||
if o1 = ssz.ReadOffset(buf[1:5]); o1 > size {
|
||||
return ssz.ErrOffset
|
||||
}
|
||||
|
||||
if o1 < 5 {
|
||||
return ssz.ErrInvalidVariableOffset
|
||||
}
|
||||
|
||||
// Field (1) 'Enrs'
|
||||
{
|
||||
buf = tail[o1:]
|
||||
num, err := ssz.DecodeDynamicLength(buf, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.Enrs = make([][]byte, num)
|
||||
err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) {
|
||||
if len(buf) > 2048 {
|
||||
return ssz.ErrBytesLength
|
||||
}
|
||||
if cap(n.Enrs[indx]) == 0 {
|
||||
n.Enrs[indx] = make([]byte, 0, len(buf))
|
||||
}
|
||||
n.Enrs[indx] = append(n.Enrs[indx], buf...)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SizeSSZ returns the ssz encoded size in bytes for the Nodes object
|
||||
func (n *Nodes) SizeSSZ() (size int) {
|
||||
size = 5
|
||||
|
||||
// Field (1) 'Enrs'
|
||||
for ii := 0; ii < len(n.Enrs); ii++ {
|
||||
size += 4
|
||||
size += len(n.Enrs[ii])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// HashTreeRoot ssz hashes the Nodes object
|
||||
func (n *Nodes) HashTreeRoot() ([32]byte, error) {
|
||||
return ssz.HashWithDefaultHasher(n)
|
||||
}
|
||||
|
||||
// HashTreeRootWith ssz hashes the Nodes object with a hasher
|
||||
func (n *Nodes) HashTreeRootWith(hh ssz.HashWalker) (err error) {
|
||||
indx := hh.Index()
|
||||
|
||||
// Field (0) 'Total'
|
||||
hh.PutUint8(n.Total)
|
||||
|
||||
// Field (1) 'Enrs'
|
||||
{
|
||||
subIndx := hh.Index()
|
||||
num := uint64(len(n.Enrs))
|
||||
if num > 32 {
|
||||
err = ssz.ErrIncorrectListSize
|
||||
return
|
||||
}
|
||||
for _, elem := range n.Enrs {
|
||||
{
|
||||
elemIndx := hh.Index()
|
||||
byteLen := uint64(len(elem))
|
||||
if byteLen > 2048 {
|
||||
err = ssz.ErrIncorrectListSize
|
||||
return
|
||||
}
|
||||
hh.AppendBytes32(elem)
|
||||
hh.MerkleizeWithMixin(elemIndx, byteLen, (2048+31)/32)
|
||||
}
|
||||
}
|
||||
hh.MerkleizeWithMixin(subIndx, num, 32)
|
||||
}
|
||||
|
||||
hh.Merkleize(indx)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTree ssz hashes the Nodes object
|
||||
func (n *Nodes) GetTree() (*ssz.Node, error) {
|
||||
return ssz.ProofTree(n)
|
||||
}
|
||||
|
||||
// MarshalSSZ ssz marshals the ConnectionId object
|
||||
func (c *ConnectionId) MarshalSSZ() ([]byte, error) {
|
||||
return ssz.MarshalSSZ(c)
|
||||
}
|
||||
|
||||
// MarshalSSZTo ssz marshals the ConnectionId object to a target array
|
||||
func (c *ConnectionId) MarshalSSZTo(buf []byte) (dst []byte, err error) {
|
||||
dst = buf
|
||||
|
||||
// Field (0) 'Id'
|
||||
if size := len(c.Id); size != 2 {
|
||||
err = ssz.ErrBytesLengthFn("ConnectionId.Id", size, 2)
|
||||
return
|
||||
}
|
||||
dst = append(dst, c.Id...)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalSSZ ssz unmarshals the ConnectionId object
|
||||
func (c *ConnectionId) UnmarshalSSZ(buf []byte) error {
|
||||
var err error
|
||||
size := uint64(len(buf))
|
||||
if size != 2 {
|
||||
return ssz.ErrSize
|
||||
}
|
||||
|
||||
// Field (0) 'Id'
|
||||
if cap(c.Id) == 0 {
|
||||
c.Id = make([]byte, 0, len(buf[0:2]))
|
||||
}
|
||||
c.Id = append(c.Id, buf[0:2]...)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// SizeSSZ returns the ssz encoded size in bytes for the ConnectionId object
|
||||
func (c *ConnectionId) SizeSSZ() (size int) {
|
||||
size = 2
|
||||
return
|
||||
}
|
||||
|
||||
// HashTreeRoot ssz hashes the ConnectionId object
|
||||
func (c *ConnectionId) HashTreeRoot() ([32]byte, error) {
|
||||
return ssz.HashWithDefaultHasher(c)
|
||||
}
|
||||
|
||||
// HashTreeRootWith ssz hashes the ConnectionId object with a hasher
|
||||
func (c *ConnectionId) HashTreeRootWith(hh ssz.HashWalker) (err error) {
|
||||
indx := hh.Index()
|
||||
|
||||
// Field (0) 'Id'
|
||||
if size := len(c.Id); size != 2 {
|
||||
err = ssz.ErrBytesLengthFn("ConnectionId.Id", size, 2)
|
||||
return
|
||||
}
|
||||
hh.PutBytes(c.Id)
|
||||
|
||||
hh.Merkleize(indx)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTree ssz hashes the ConnectionId object
|
||||
func (c *ConnectionId) GetTree() (*ssz.Node, error) {
|
||||
return ssz.ProofTree(c)
|
||||
}
|
||||
|
||||
// MarshalSSZ ssz marshals the Accept object
|
||||
func (a *Accept) MarshalSSZ() ([]byte, error) {
|
||||
return ssz.MarshalSSZ(a)
|
||||
}
|
||||
|
||||
// MarshalSSZTo ssz marshals the Accept object to a target array
|
||||
func (a *Accept) MarshalSSZTo(buf []byte) (dst []byte, err error) {
|
||||
dst = buf
|
||||
offset := int(6)
|
||||
|
||||
// Field (0) 'ConnectionId'
|
||||
if size := len(a.ConnectionId); size != 2 {
|
||||
err = ssz.ErrBytesLengthFn("Accept.ConnectionId", size, 2)
|
||||
return
|
||||
}
|
||||
dst = append(dst, a.ConnectionId...)
|
||||
|
||||
// Offset (1) 'ContentKeys'
|
||||
dst = ssz.WriteOffset(dst, offset)
|
||||
offset += len(a.ContentKeys)
|
||||
|
||||
// Field (1) 'ContentKeys'
|
||||
if size := len(a.ContentKeys); size > 64 {
|
||||
err = ssz.ErrBytesLengthFn("Accept.ContentKeys", size, 64)
|
||||
return
|
||||
}
|
||||
dst = append(dst, a.ContentKeys...)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalSSZ ssz unmarshals the Accept object
|
||||
func (a *Accept) UnmarshalSSZ(buf []byte) error {
|
||||
var err error
|
||||
size := uint64(len(buf))
|
||||
if size < 6 {
|
||||
return ssz.ErrSize
|
||||
}
|
||||
|
||||
tail := buf
|
||||
var o1 uint64
|
||||
|
||||
// Field (0) 'ConnectionId'
|
||||
if cap(a.ConnectionId) == 0 {
|
||||
a.ConnectionId = make([]byte, 0, len(buf[0:2]))
|
||||
}
|
||||
a.ConnectionId = append(a.ConnectionId, buf[0:2]...)
|
||||
|
||||
// Offset (1) 'ContentKeys'
|
||||
if o1 = ssz.ReadOffset(buf[2:6]); o1 > size {
|
||||
return ssz.ErrOffset
|
||||
}
|
||||
|
||||
if o1 < 6 {
|
||||
return ssz.ErrInvalidVariableOffset
|
||||
}
|
||||
|
||||
// Field (1) 'ContentKeys'
|
||||
{
|
||||
buf = tail[o1:]
|
||||
if err = ssz.ValidateBitlist(buf, 64); err != nil {
|
||||
return err
|
||||
}
|
||||
if cap(a.ContentKeys) == 0 {
|
||||
a.ContentKeys = make([]byte, 0, len(buf))
|
||||
}
|
||||
a.ContentKeys = append(a.ContentKeys, buf...)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SizeSSZ returns the ssz encoded size in bytes for the Accept object
|
||||
func (a *Accept) SizeSSZ() (size int) {
|
||||
size = 6
|
||||
|
||||
// Field (1) 'ContentKeys'
|
||||
size += len(a.ContentKeys)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// HashTreeRoot ssz hashes the Accept object
|
||||
func (a *Accept) HashTreeRoot() ([32]byte, error) {
|
||||
return ssz.HashWithDefaultHasher(a)
|
||||
}
|
||||
|
||||
// HashTreeRootWith ssz hashes the Accept object with a hasher
|
||||
func (a *Accept) HashTreeRootWith(hh ssz.HashWalker) (err error) {
|
||||
indx := hh.Index()
|
||||
|
||||
// Field (0) 'ConnectionId'
|
||||
if size := len(a.ConnectionId); size != 2 {
|
||||
err = ssz.ErrBytesLengthFn("Accept.ConnectionId", size, 2)
|
||||
return
|
||||
}
|
||||
hh.PutBytes(a.ConnectionId)
|
||||
|
||||
// Field (1) 'ContentKeys'
|
||||
if len(a.ContentKeys) == 0 {
|
||||
err = ssz.ErrEmptyBitlist
|
||||
return
|
||||
}
|
||||
hh.PutBitlist(a.ContentKeys, 64)
|
||||
|
||||
hh.Merkleize(indx)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTree ssz hashes the Accept object
|
||||
func (a *Accept) GetTree() (*ssz.Node, error) {
|
||||
return ssz.ProofTree(a)
|
||||
}
|
||||
212
portalnetwork/portalwire/messages_test.go
Normal file
212
portalnetwork/portalwire/messages_test.go
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
package portalwire
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
ssz "github.com/ferranbt/fastssz"
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var maxUint256 = uint256.MustFromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
|
||||
|
||||
// https://github.com/ethereum/portal-network-specs/blob/master/portal-wire-test-vectors.md
|
||||
// we remove the message type here
|
||||
func TestPingMessage(t *testing.T) {
|
||||
dataRadius := maxUint256.Sub(maxUint256, uint256.NewInt(1))
|
||||
reverseBytes, err := dataRadius.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
customData := &PingPongCustomData{
|
||||
Radius: reverseBytes,
|
||||
}
|
||||
dataBytes, err := customData.MarshalSSZ()
|
||||
assert.NoError(t, err)
|
||||
ping := &Ping{
|
||||
EnrSeq: 1,
|
||||
CustomPayload: dataBytes,
|
||||
}
|
||||
|
||||
expected := "0x01000000000000000c000000feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
|
||||
data, err := ping.MarshalSSZ()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, fmt.Sprintf("0x%x", data))
|
||||
}
|
||||
|
||||
func TestPongMessage(t *testing.T) {
|
||||
dataRadius := maxUint256.Div(maxUint256, uint256.NewInt(2))
|
||||
reverseBytes, err := dataRadius.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
customData := &PingPongCustomData{
|
||||
Radius: reverseBytes,
|
||||
}
|
||||
|
||||
dataBytes, err := customData.MarshalSSZ()
|
||||
assert.NoError(t, err)
|
||||
pong := &Pong{
|
||||
EnrSeq: 1,
|
||||
CustomPayload: dataBytes,
|
||||
}
|
||||
|
||||
expected := "0x01000000000000000c000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"
|
||||
|
||||
data, err := pong.MarshalSSZ()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, fmt.Sprintf("0x%x", data))
|
||||
}
|
||||
|
||||
func TestFindNodesMessage(t *testing.T) {
|
||||
distances := []uint16{256, 255}
|
||||
|
||||
distancesBytes := make([][2]byte, len(distances))
|
||||
for i, distance := range distances {
|
||||
copy(distancesBytes[i][:], ssz.MarshalUint16(make([]byte, 0), distance))
|
||||
}
|
||||
|
||||
findNode := &FindNodes{
|
||||
Distances: distancesBytes,
|
||||
}
|
||||
|
||||
data, err := findNode.MarshalSSZ()
|
||||
expected := "0x040000000001ff00"
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, fmt.Sprintf("0x%x", data))
|
||||
}
|
||||
|
||||
func TestNodes(t *testing.T) {
|
||||
enrs := []string{
|
||||
"enr:-HW4QBzimRxkmT18hMKaAL3IcZF1UcfTMPyi3Q1pxwZZbcZVRI8DC5infUAB_UauARLOJtYTxaagKoGmIjzQxO2qUygBgmlkgnY0iXNlY3AyNTZrMaEDymNMrg1JrLQB2KTGtv6MVbcNEVv0AHacwUAPMljNMTg",
|
||||
"enr:-HW4QNfxw543Ypf4HXKXdYxkyzfcxcO-6p9X986WldfVpnVTQX1xlTnWrktEWUbeTZnmgOuAY_KUhbVV1Ft98WoYUBMBgmlkgnY0iXNlY3AyNTZrMaEDDiy3QkHAxPyOgWbxp5oF1bDdlYE6dLCUUp8xfVw50jU",
|
||||
}
|
||||
|
||||
enrsBytes := make([][]byte, 0)
|
||||
for _, enr := range enrs {
|
||||
n, err := enode.Parse(enode.ValidSchemes, enr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
enrBytes, err := rlp.EncodeToBytes(n.Record())
|
||||
assert.NoError(t, err)
|
||||
enrsBytes = append(enrsBytes, enrBytes)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
input [][]byte
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "empty nodes",
|
||||
input: make([][]byte, 0),
|
||||
expected: "0x0105000000",
|
||||
},
|
||||
{
|
||||
name: "two nodes",
|
||||
input: enrsBytes,
|
||||
expected: "0x0105000000080000007f000000f875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
nodes := &Nodes{
|
||||
Total: 1,
|
||||
Enrs: test.input,
|
||||
}
|
||||
|
||||
data, err := nodes.MarshalSSZ()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, test.expected, fmt.Sprintf("0x%x", data))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContent(t *testing.T) {
|
||||
contentKey := "0x706f7274616c"
|
||||
|
||||
content := &FindContent{
|
||||
ContentKey: hexutil.MustDecode(contentKey),
|
||||
}
|
||||
expected := "0x04000000706f7274616c"
|
||||
data, err := content.MarshalSSZ()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, fmt.Sprintf("0x%x", data))
|
||||
|
||||
expected = "0x7468652063616b652069732061206c6965"
|
||||
|
||||
contentRes := &Content{
|
||||
Content: hexutil.MustDecode("0x7468652063616b652069732061206c6965"),
|
||||
}
|
||||
|
||||
data, err = contentRes.MarshalSSZ()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, fmt.Sprintf("0x%x", data))
|
||||
|
||||
expectData := &Content{}
|
||||
err = expectData.UnmarshalSSZ(data)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, contentRes.Content, expectData.Content)
|
||||
|
||||
enrs := []string{
|
||||
"enr:-HW4QBzimRxkmT18hMKaAL3IcZF1UcfTMPyi3Q1pxwZZbcZVRI8DC5infUAB_UauARLOJtYTxaagKoGmIjzQxO2qUygBgmlkgnY0iXNlY3AyNTZrMaEDymNMrg1JrLQB2KTGtv6MVbcNEVv0AHacwUAPMljNMTg",
|
||||
"enr:-HW4QNfxw543Ypf4HXKXdYxkyzfcxcO-6p9X986WldfVpnVTQX1xlTnWrktEWUbeTZnmgOuAY_KUhbVV1Ft98WoYUBMBgmlkgnY0iXNlY3AyNTZrMaEDDiy3QkHAxPyOgWbxp5oF1bDdlYE6dLCUUp8xfVw50jU",
|
||||
}
|
||||
|
||||
enrsBytes := make([][]byte, 0)
|
||||
for _, enr := range enrs {
|
||||
n, err := enode.Parse(enode.ValidSchemes, enr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
enrBytes, err := rlp.EncodeToBytes(n.Record())
|
||||
assert.NoError(t, err)
|
||||
enrsBytes = append(enrsBytes, enrBytes)
|
||||
}
|
||||
|
||||
enrsRes := &Enrs{
|
||||
Enrs: enrsBytes,
|
||||
}
|
||||
|
||||
expected = "0x080000007f000000f875b8401ce2991c64993d7c84c29a00bdc871917551c7d330fca2dd0d69c706596dc655448f030b98a77d4001fd46ae0112ce26d613c5a6a02a81a6223cd0c4edaa53280182696482763489736563703235366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138f875b840d7f1c39e376297f81d7297758c64cb37dcc5c3beea9f57f7ce9695d7d5a67553417d719539d6ae4b445946de4d99e680eb8063f29485b555d45b7df16a1850130182696482763489736563703235366b31a1030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235"
|
||||
|
||||
data, err = enrsRes.MarshalSSZ()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, fmt.Sprintf("0x%x", data))
|
||||
|
||||
expectEnrs := &Enrs{}
|
||||
err = expectEnrs.UnmarshalSSZ(data)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectEnrs.Enrs, enrsRes.Enrs)
|
||||
}
|
||||
|
||||
func TestOfferAndAcceptMessage(t *testing.T) {
|
||||
contentKey := "0x010203"
|
||||
contentBytes := hexutil.MustDecode(contentKey)
|
||||
contentKeys := [][]byte{contentBytes}
|
||||
offer := &Offer{
|
||||
ContentKeys: contentKeys,
|
||||
}
|
||||
|
||||
expected := "0x0400000004000000010203"
|
||||
|
||||
data, err := offer.MarshalSSZ()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, fmt.Sprintf("0x%x", data))
|
||||
|
||||
contentKeyBitlist := bitfield.NewBitlist(8)
|
||||
contentKeyBitlist.SetBitAt(0, true)
|
||||
accept := &Accept{
|
||||
ConnectionId: []byte{0x01, 0x02},
|
||||
ContentKeys: contentKeyBitlist,
|
||||
}
|
||||
|
||||
expected = "0x0102060000000101"
|
||||
|
||||
data, err = accept.MarshalSSZ()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, fmt.Sprintf("0x%x", data))
|
||||
}
|
||||
Loading…
Reference in a new issue