mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-30 00:23:46 +00:00
434 lines
9 KiB
Go
434 lines
9 KiB
Go
package discover
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
"github.com/ethereum/go-ethereum/p2p/discover/portalwire"
|
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
"github.com/holiman/uint256"
|
|
)
|
|
|
|
type DiscV5API struct {
|
|
DiscV5 *UDPv5
|
|
}
|
|
|
|
func NewAPI(discV5 *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 uint64 `json:"enrSeq"`
|
|
DataRadius string `json:"dataRadius"`
|
|
}
|
|
|
|
type ContentInfo struct {
|
|
Content string `json:"content"`
|
|
UtpTransfer bool `json:"utpTransfer"`
|
|
}
|
|
|
|
type Enrs struct {
|
|
Enrs []string `json:"enrs"`
|
|
}
|
|
|
|
func (d *DiscV5API) NodeInfo() *NodeInfo {
|
|
n := d.DiscV5.LocalNode().Node()
|
|
|
|
return &NodeInfo{
|
|
NodeId: n.ID().String(),
|
|
Enr: n.String(),
|
|
Ip: n.IP().String(),
|
|
}
|
|
}
|
|
|
|
func (d *DiscV5API) RoutingTableInfo() *RoutingTableInfo {
|
|
n := d.DiscV5.LocalNode().Node()
|
|
|
|
closestNodes := d.DiscV5.AllNodes()
|
|
buckets := make([]string, len(closestNodes))
|
|
for _, e := range closestNodes {
|
|
buckets = append(buckets, e.ID().String())
|
|
}
|
|
|
|
return &RoutingTableInfo{
|
|
Buckets: buckets,
|
|
LocalNodeId: n.ID().String(),
|
|
}
|
|
}
|
|
|
|
func (d *DiscV5API) AddEnr(enr string) (bool, error) {
|
|
n, err := enode.Parse(enode.ValidSchemes, enr)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
d.DiscV5.tab.addSeenNode(wrapNode(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.tab.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.tab.getNode(id)
|
|
if n == nil {
|
|
return false, errors.New("record not in local routing table")
|
|
}
|
|
|
|
d.DiscV5.tab.delete(wrapNode(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.pingInner(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 PortalAPI struct {
|
|
*DiscV5API
|
|
portalProtocol *PortalProtocol
|
|
}
|
|
|
|
func NewPortalAPI(portalProtocol *PortalProtocol) *PortalAPI {
|
|
return &PortalAPI{
|
|
DiscV5API: &DiscV5API{portalProtocol.DiscV5},
|
|
portalProtocol: portalProtocol,
|
|
}
|
|
}
|
|
|
|
func (p *PortalAPI) NodeInfo() *NodeInfo {
|
|
n := p.portalProtocol.localNode.Node()
|
|
|
|
return &NodeInfo{
|
|
NodeId: n.ID().String(),
|
|
Enr: n.String(),
|
|
Ip: n.IP().String(),
|
|
}
|
|
}
|
|
|
|
func (p *PortalAPI) RoutingTableInfo() *RoutingTableInfo {
|
|
n := p.portalProtocol.localNode.Node()
|
|
|
|
closestNodes := p.portalProtocol.table.Nodes()
|
|
buckets := make([]string, len(closestNodes))
|
|
for _, e := range closestNodes {
|
|
buckets = append(buckets, e.ID().String())
|
|
}
|
|
|
|
return &RoutingTableInfo{
|
|
Buckets: buckets,
|
|
LocalNodeId: n.ID().String(),
|
|
}
|
|
}
|
|
|
|
func (p *PortalAPI) AddEnr(enr string) (bool, error) {
|
|
n, err := enode.Parse(enode.ValidSchemes, enr)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
p.portalProtocol.table.addSeenNode(wrapNode(n))
|
|
return true, nil
|
|
}
|
|
|
|
func (p *PortalAPI) AddEnrs(enrs []string) bool {
|
|
// Note: unspecified RPC, but useful for our local testnet test
|
|
for _, enr := range enrs {
|
|
n, err := enode.Parse(enode.ValidSchemes, enr)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
p.portalProtocol.table.addSeenNode(wrapNode(n))
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (p *PortalAPI) 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 *PortalAPI) 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, errors.New("record not in local routing table")
|
|
}
|
|
|
|
p.portalProtocol.table.delete(wrapNode(n))
|
|
return true, nil
|
|
}
|
|
|
|
func (p *PortalAPI) 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 *PortalAPI) 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: pong.EnrSeq,
|
|
DataRadius: nodeRadius.Hex(),
|
|
}, nil
|
|
}
|
|
|
|
func (p *PortalAPI) 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 *PortalAPI) 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:
|
|
return &ContentInfo{
|
|
Content: hexutil.Encode(findContent.([]byte)),
|
|
UtpTransfer: false,
|
|
}, nil
|
|
case portalwire.ContentConnIdSelector:
|
|
return &ContentInfo{
|
|
Content: hexutil.Encode(findContent.([]byte)),
|
|
UtpTransfer: true,
|
|
}, nil
|
|
default:
|
|
enrs := make([]string, 0)
|
|
for _, r := range findContent.([]*enode.Node) {
|
|
enrs = append(enrs, r.String())
|
|
}
|
|
|
|
return &Enrs{
|
|
Enrs: enrs,
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
func (p *PortalAPI) Offer(enr string, contentKey string, contentValue string) (string, error) {
|
|
n, err := enode.Parse(enode.ValidSchemes, enr)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
contentKeyBytes, err := hexutil.Decode(contentKey)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
contentValueBytes, err := hexutil.Decode(contentValue)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
contentEntry := &ContentEntry{
|
|
ContentKey: contentKeyBytes,
|
|
Content: contentValueBytes,
|
|
}
|
|
|
|
transientOfferRequest := &TransientOfferRequest{
|
|
Contents: []*ContentEntry{contentEntry},
|
|
}
|
|
|
|
offerReq := &OfferRequest{
|
|
Kind: portalwire.OfferRequestDirect,
|
|
Request: transientOfferRequest,
|
|
}
|
|
accept, err := p.portalProtocol.offer(n, offerReq)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return hexutil.Encode(accept), nil
|
|
}
|
|
|
|
func (p *PortalAPI) 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
|
|
}
|