feat: finish contentLookup

This commit is contained in:
fearlessfe 2024-01-03 23:24:48 +08:00 committed by Chen Kai
parent c0ac288ee7
commit 800ecad368

View file

@ -13,7 +13,6 @@ import (
"math/big" "math/big"
"net" "net"
"sort" "sort"
"sync"
"time" "time"
"github.com/tetratelabs/wabin/leb128" "github.com/tetratelabs/wabin/leb128"
@ -1352,7 +1351,7 @@ func (p *PortalProtocol) collectTableNodes(rip net.IP, distances []uint, limit i
return nodes return nodes
} }
type contentLookupResult struct { type findContentResult struct {
flag byte flag byte
content any content any
src *enode.Node src *enode.Node
@ -1364,100 +1363,27 @@ type ContentLookupResult struct {
NodeInterestedInContent []*enode.Node NodeInterestedInContent []*enode.Node
} }
func (p *PortalProtocol) ContentLookup(contentKey []byte) (res *ContentLookupResult, err error) { func (p *PortalProtocol) ContentLookup(contentKey []byte) (*ContentLookupResult, error) {
var mutex sync.Mutex
contentId := p.toContentId(contentKey) contentId := p.toContentId(contentKey)
nodesByDis := p.table.findnodeByID(enode.ID(contentId), bucketSize, false) nodesByDis := p.table.findnodeByID(enode.ID(contentId), bucketSize, false)
closestNodes := nodesByDis.entries closestNodes := nodesByDis.entries
// findContent enrs chan
asked := make(map[enode.ID]struct{}) newNodeChan := make(chan []*enode.Node)
seen := make(map[enode.ID]struct{}) // target node for findContent
asked[p.localNode.ID()] = struct{}{} nodeChan := p.genNodeChan(closestNodes, contentId, newNodeChan)
seen[p.localNode.ID()] = struct{}{}
for _, node := range closestNodes {
seen[node.ID()] = struct{}{}
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
pendingQueries := make(map[enode.ID]struct{}, 0)
// find content query chan
pendingChan := make(chan struct{}, alpha)
for i := 0; i < alpha; i++ {
pendingChan <- struct{}{}
}
requestAmount := 0
nodesWithoutContent := make([]*enode.Node, 0) nodesWithoutContent := make([]*enode.Node, 0)
// find content result // find content result
contentResultChan := make(chan contentLookupResult)
stopFindContentChan := make(chan struct{}) stopFindContentChan := make(chan struct{})
// content lookup result
resChan := make(chan *ContentLookupResult)
defer close(resChan)
defer close(stopFindContentChan)
// get findContent contentResultChan := p.parallelFindContent(stopFindContentChan, nodeChan, contentKey)
go func() {
defer close(contentResultChan)
for {
select {
case <-pendingChan:
for i := 0; i < len(closestNodes) && len(pendingQueries) < alpha; i++ {
wrapedNode := closestNodes[i]
if _, ok := asked[wrapedNode.ID()]; !ok {
mutex.Lock()
asked[wrapedNode.ID()] = struct{}{}
pendingQueries[wrapedNode.ID()] = struct{}{}
mutex.Unlock()
go func() {
flag, content, err := p.findContent(unwrapNode(wrapedNode), contentKey)
select {
case contentResultChan <- contentLookupResult{flag: flag, content: content, err: err, src: unwrapNode(wrapedNode)}:
return
case <-stopFindContentChan:
return
}
}()
requestAmount++
fmt.Printf("requestAmount %v", requestAmount)
}
if len(pendingQueries) == 0 { defer close(newNodeChan)
contentResultChan <- contentLookupResult{err: ContentNotFound}
return
}
}
case <-ctx.Done():
return
}
}
}()
// handler result
go func() {
// cancel context
defer cancel()
defer close(pendingChan)
for result := range contentResultChan { for result := range contentResultChan {
if result.err == ContentNotFound { if result.err == ContentNotFound {
err = ContentNotFound return nil, result.err
resChan <- res
return
} }
mutex.Lock()
delete(pendingQueries, result.src.ID())
mutex.Unlock()
if result.err != nil {
p.log.Trace("find content err: %v", result.err)
continue
}
switch result.flag { switch result.flag {
case portalwire.ContentRawSelector: case portalwire.ContentRawSelector:
content, ok := result.content.([]byte) content, ok := result.content.([]byte)
@ -1466,9 +1392,7 @@ func (p *PortalProtocol) ContentLookup(contentKey []byte) (res *ContentLookupRes
continue continue
} }
stopFindContentChan <- struct{}{} stopFindContentChan <- struct{}{}
resChan <- &ContentLookupResult{Content: content, NodeInterestedInContent: nodesWithoutContent} return &ContentLookupResult{Content: content, NodeInterestedInContent: nodesWithoutContent}, nil
return
case portalwire.ContentEnrsSelector: case portalwire.ContentEnrsSelector:
nodeIdStr := result.src.ID().String() nodeIdStr := result.src.ID().String()
// Get may modify the content of srcId // Get may modify the content of srcId
@ -1484,6 +1408,34 @@ func (p *PortalProtocol) ContentLookup(contentKey []byte) (res *ContentLookupRes
p.log.Trace("failed to assert to enrs content, value is: %v", result.content) p.log.Trace("failed to assert to enrs content, value is: %v", result.content)
continue continue
} }
// handle new nodes
newNodeChan <- nodes
}
}
return nil, ContentNotFound
}
func (p *PortalProtocol) genNodeChan(initNodes []*node, contentId []byte, newNodeChan chan []*enode.Node) <-chan *node {
nodeChan := make(chan *node, alpha)
closestNodes := initNodes
asked := make(map[enode.ID]struct{})
seen := make(map[enode.ID]struct{})
asked[p.localNode.ID()] = struct{}{}
seen[p.localNode.ID()] = struct{}{}
for _, node := range closestNodes {
seen[node.ID()] = struct{}{}
}
for i := 0; i < len(closestNodes) && i < alpha; i++ {
wrapedNode := closestNodes[i]
if _, ok := asked[wrapedNode.ID()]; !ok {
asked[wrapedNode.ID()] = struct{}{}
nodeChan <- wrapedNode
}
}
go func() {
for nodes := range newNodeChan {
for _, n := range nodes { for _, n := range nodes {
if _, ok := seen[n.ID()]; !ok { if _, ok := seen[n.ID()]; !ok {
seen[n.ID()] = struct{}{} seen[n.ID()] = struct{}{}
@ -1495,14 +1447,44 @@ func (p *PortalProtocol) ContentLookup(contentKey []byte) (res *ContentLookupRes
} }
} }
} }
pendingChan <- struct{}{} newRequest := false
for _, wrapedNode := range closestNodes {
if _, ok := asked[wrapedNode.ID()]; !ok {
newRequest = true
asked[wrapedNode.ID()] = struct{}{}
nodeChan <- wrapedNode
}
}
if !newRequest {
close(nodeChan)
} }
} }
}() }()
res = <-resChan return nodeChan
}
return res, err func (p *PortalProtocol) parallelFindContent(closeChan <-chan struct{}, nodeChan <-chan *node, contentKey []byte) <-chan findContentResult {
contentResultChan := make(chan findContentResult, alpha)
requestAmount := 0
go func() {
defer close(contentResultChan)
for {
select {
case wrapedNode, ok := <-nodeChan:
if wrapedNode == nil && !ok {
contentResultChan <- findContentResult{err: ContentNotFound}
return
}
flag, content, err := p.findContent(unwrapNode(wrapedNode), contentKey)
requestAmount++
contentResultChan <- findContentResult{flag: flag, content: content, err: err, src: unwrapNode(wrapedNode)}
case <-closeChan:
return
}
}
}()
return contentResultChan
} }
func inRange(nodeId enode.ID, nodeRadius *uint256.Int, contentId []byte) bool { func inRange(nodeId enode.ID, nodeRadius *uint256.Int, contentId []byte) bool {