2021-04-28 14:20:05 +00:00
|
|
|
# nim-eth
|
2024-06-18 16:09:27 +00:00
|
|
|
# Copyright (c) 2018-2024 Status Research & Development GmbH
|
2021-04-28 14:20:05 +00:00
|
|
|
# Licensed and distributed under either of
|
|
|
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
|
|
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
|
2023-05-10 13:50:04 +00:00
|
|
|
{.push raises: [].}
|
2019-02-05 15:40:29 +00:00
|
|
|
|
|
|
|
import
|
2023-04-20 09:52:42 +00:00
|
|
|
std/[times, net],
|
2024-10-29 09:03:13 +00:00
|
|
|
chronos,
|
|
|
|
stint,
|
|
|
|
nimcrypto/keccak,
|
|
|
|
chronicles,
|
|
|
|
stew/objects,
|
|
|
|
results,
|
2024-09-29 08:52:19 +00:00
|
|
|
../rlp,
|
|
|
|
../common/keys,
|
2021-04-06 11:33:24 +00:00
|
|
|
"."/[kademlia, enode]
|
2019-02-05 15:40:29 +00:00
|
|
|
|
2024-10-29 09:03:13 +00:00
|
|
|
export Node, results
|
2019-02-05 15:40:29 +00:00
|
|
|
|
|
|
|
logScope:
|
2022-12-06 13:54:03 +00:00
|
|
|
topics = "eth p2p discovery"
|
2019-02-05 15:40:29 +00:00
|
|
|
|
|
|
|
const
|
|
|
|
# UDP packet constants.
|
2024-10-29 09:03:13 +00:00
|
|
|
MAC_SIZE = 256 div 8 # 32
|
|
|
|
SIG_SIZE = 520 div 8 # 65
|
|
|
|
HEAD_SIZE = MAC_SIZE + SIG_SIZE # 97
|
|
|
|
EXPIRATION = 60 # let messages expire after N seconds
|
2019-02-05 15:40:29 +00:00
|
|
|
PROTO_VERSION = 4
|
|
|
|
|
|
|
|
type
|
|
|
|
DiscoveryProtocol* = ref object
|
|
|
|
privKey: PrivateKey
|
|
|
|
address: Address
|
|
|
|
bootstrapNodes*: seq[Node]
|
2022-03-15 17:08:15 +00:00
|
|
|
localNode*: Node
|
2021-04-06 13:15:56 +00:00
|
|
|
kademlia*: KademliaProtocol[DiscoveryProtocol]
|
2019-02-05 15:40:29 +00:00
|
|
|
transp: DatagramTransport
|
2022-03-15 17:08:15 +00:00
|
|
|
bindIp: IpAddress
|
|
|
|
bindPort: Port
|
2019-02-05 15:40:29 +00:00
|
|
|
|
2019-05-23 10:02:58 +00:00
|
|
|
DiscProtocolError* = object of CatchableError
|
|
|
|
|
2020-04-06 16:24:15 +00:00
|
|
|
DiscResult*[T] = Result[T, cstring]
|
|
|
|
|
2024-09-29 08:52:19 +00:00
|
|
|
keccak256 = keccak.keccak256
|
|
|
|
|
2022-04-04 20:31:09 +00:00
|
|
|
const MinListLen: array[CommandId, int] = [4, 3, 2, 2, 1, 2]
|
2019-02-05 15:40:29 +00:00
|
|
|
|
|
|
|
proc append*(w: var RlpWriter, a: IpAddress) =
|
|
|
|
case a.family
|
|
|
|
of IpAddressFamily.IPv6:
|
2020-04-20 18:14:39 +00:00
|
|
|
w.append(a.address_v6)
|
2019-02-05 15:40:29 +00:00
|
|
|
of IpAddressFamily.IPv4:
|
2020-04-20 18:14:39 +00:00
|
|
|
w.append(a.address_v4)
|
2019-02-05 15:40:29 +00:00
|
|
|
|
2024-10-29 09:03:13 +00:00
|
|
|
proc append(w: var RlpWriter, p: Port) =
|
|
|
|
w.append(p.uint)
|
|
|
|
|
|
|
|
proc append(w: var RlpWriter, pk: PublicKey) =
|
|
|
|
w.append(pk.toRaw())
|
|
|
|
|
|
|
|
proc append(w: var RlpWriter, h: MDigest[256]) =
|
|
|
|
w.append(h.data)
|
2019-02-05 15:40:29 +00:00
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
proc pack(cmdId: CommandId, payload: openArray[byte], pk: PrivateKey): seq[byte] =
|
2019-02-05 15:40:29 +00:00
|
|
|
## Create and sign a UDP message to be sent to a remote node.
|
|
|
|
##
|
|
|
|
## See https://github.com/ethereum/devp2p/blob/master/rlpx.md#node-discovery for information on
|
|
|
|
## how UDP packets are structured.
|
|
|
|
|
2024-10-29 09:03:13 +00:00
|
|
|
result = newSeq[byte](HEAD_SIZE + 1 + payload.len)
|
|
|
|
result[HEAD_SIZE] = cmdId.byte
|
|
|
|
result[HEAD_SIZE + 1 ..< result.len] = payload
|
|
|
|
result[MAC_SIZE ..< MAC_SIZE + SIG_SIZE] =
|
|
|
|
pk.sign(result.toOpenArray(HEAD_SIZE, result.high)).toRaw()
|
|
|
|
result[0 ..< MAC_SIZE] =
|
|
|
|
keccak256.digest(result.toOpenArray(MAC_SIZE, result.high)).data
|
2019-02-05 15:40:29 +00:00
|
|
|
|
2020-04-20 18:14:39 +00:00
|
|
|
proc validateMsgHash(msg: openArray[byte]): DiscResult[MDigest[256]] =
|
2019-03-25 09:20:53 +00:00
|
|
|
if msg.len > HEAD_SIZE:
|
2020-04-06 16:24:15 +00:00
|
|
|
var ret: MDigest[256]
|
|
|
|
ret.data[0 .. ^1] = msg.toOpenArray(0, ret.data.high)
|
|
|
|
if ret == keccak256.digest(msg.toOpenArray(MAC_SIZE, msg.high)):
|
|
|
|
ok(ret)
|
|
|
|
else:
|
|
|
|
err("disc: invalid message hash")
|
|
|
|
else:
|
|
|
|
err("disc: msg missing hash")
|
2019-02-05 15:40:29 +00:00
|
|
|
|
2020-04-06 16:24:15 +00:00
|
|
|
proc recoverMsgPublicKey(msg: openArray[byte]): DiscResult[PublicKey] =
|
|
|
|
if msg.len <= HEAD_SIZE:
|
|
|
|
return err("disc: can't get public key")
|
2024-10-29 09:03:13 +00:00
|
|
|
let sig = ?Signature.fromRaw(msg.toOpenArray(MAC_SIZE, HEAD_SIZE))
|
2020-04-06 16:24:15 +00:00
|
|
|
recover(sig, msg.toOpenArray(HEAD_SIZE, msg.high))
|
2019-02-05 15:40:29 +00:00
|
|
|
|
2024-10-29 09:03:13 +00:00
|
|
|
proc unpack(
|
|
|
|
msg: openArray[byte]
|
|
|
|
): tuple[cmdId: CommandId, payload: seq[byte]] {.raises: [DiscProtocolError].} =
|
2023-06-03 18:47:55 +00:00
|
|
|
# Check against possible RangeDefect
|
2024-10-29 09:03:13 +00:00
|
|
|
if msg[HEAD_SIZE].int < CommandId.low.ord or msg[HEAD_SIZE].int > CommandId.high.ord:
|
2019-05-23 10:02:58 +00:00
|
|
|
raise newException(DiscProtocolError, "Unsupported packet id")
|
|
|
|
|
2024-10-29 09:03:13 +00:00
|
|
|
(cmdId: msg[HEAD_SIZE].CommandId, payload: msg[HEAD_SIZE + 1 .. ^1])
|
2019-02-05 15:40:29 +00:00
|
|
|
|
2024-10-29 09:03:13 +00:00
|
|
|
proc expiration(): uint64 =
|
|
|
|
uint64(getTime().toUnix() + EXPIRATION)
|
2019-02-05 15:40:29 +00:00
|
|
|
|
|
|
|
# Wire protocol
|
|
|
|
|
2023-05-10 13:50:04 +00:00
|
|
|
proc send(d: DiscoveryProtocol, n: Node, data: seq[byte]) =
|
2019-02-05 15:40:29 +00:00
|
|
|
let ta = initTAddress(n.node.address.ip, n.node.address.udpPort)
|
|
|
|
let f = d.transp.sendTo(ta, data)
|
2024-01-19 10:31:17 +00:00
|
|
|
let cb = proc(data: pointer) {.gcsafe.} =
|
2019-02-05 15:40:29 +00:00
|
|
|
if f.failed:
|
2024-02-19 07:16:33 +00:00
|
|
|
when defined(chronicles_log_level):
|
|
|
|
try:
|
|
|
|
# readError will raise FutureError
|
2024-10-29 09:03:13 +00:00
|
|
|
debug "Discovery send failed",
|
|
|
|
msg = f.readError.msg, address = $n.node.address
|
2024-02-19 07:16:33 +00:00
|
|
|
except FutureError as exc:
|
2024-10-29 09:03:13 +00:00
|
|
|
error "Failed to get discovery send future error", msg = exc.msg
|
2024-06-12 02:29:18 +00:00
|
|
|
|
2024-01-19 10:31:17 +00:00
|
|
|
f.addCallback cb
|
2019-02-05 15:40:29 +00:00
|
|
|
|
2023-05-10 13:50:04 +00:00
|
|
|
proc sendPing*(d: DiscoveryProtocol, n: Node): seq[byte] =
|
2024-10-29 09:03:13 +00:00
|
|
|
let payload =
|
|
|
|
rlp.encode((PROTO_VERSION.uint, d.address, n.node.address, expiration()))
|
2019-02-05 15:40:29 +00:00
|
|
|
let msg = pack(cmdPing, payload, d.privKey)
|
|
|
|
result = msg[0 ..< MAC_SIZE]
|
|
|
|
trace ">>> ping ", n
|
|
|
|
d.send(n, msg)
|
|
|
|
|
|
|
|
proc sendPong*(d: DiscoveryProtocol, n: Node, token: MDigest[256]) =
|
2020-04-20 18:14:39 +00:00
|
|
|
let payload = rlp.encode((n.node.address, token, expiration()))
|
2019-02-05 15:40:29 +00:00
|
|
|
let msg = pack(cmdPong, payload, d.privKey)
|
|
|
|
trace ">>> pong ", n
|
|
|
|
d.send(n, msg)
|
|
|
|
|
|
|
|
proc sendFindNode*(d: DiscoveryProtocol, n: Node, targetNodeId: NodeId) =
|
|
|
|
var data: array[64, byte]
|
2024-12-04 11:40:44 +00:00
|
|
|
data[32 .. ^1] = targetNodeId.toBytesBE()
|
2020-04-20 18:14:39 +00:00
|
|
|
let payload = rlp.encode((data, expiration()))
|
2019-02-05 15:40:29 +00:00
|
|
|
let msg = pack(cmdFindNode, payload, d.privKey)
|
2024-10-29 09:03:13 +00:00
|
|
|
trace ">>> find_node to ", n #, ": ", msg.toHex()
|
2019-02-05 15:40:29 +00:00
|
|
|
d.send(n, msg)
|
|
|
|
|
|
|
|
proc sendNeighbours*(d: DiscoveryProtocol, node: Node, neighbours: seq[Node]) =
|
|
|
|
const MAX_NEIGHBOURS_PER_PACKET = 12 # TODO: Implement a smarter way to compute it
|
|
|
|
type Neighbour = tuple[ip: IpAddress, udpPort, tcpPort: Port, pk: PublicKey]
|
|
|
|
var nodes = newSeqOfCap[Neighbour](MAX_NEIGHBOURS_PER_PACKET)
|
|
|
|
|
|
|
|
template flush() =
|
|
|
|
block:
|
2020-04-20 18:14:39 +00:00
|
|
|
let payload = rlp.encode((nodes, expiration()))
|
2021-12-20 12:14:50 +00:00
|
|
|
let msg = pack(cmdNeighbours, payload, d.privKey)
|
2023-04-18 11:51:02 +00:00
|
|
|
trace "Neighbours to", node, nodes = $nodes
|
2019-02-05 15:40:29 +00:00
|
|
|
d.send(node, msg)
|
|
|
|
nodes.setLen(0)
|
|
|
|
|
|
|
|
for i, n in neighbours:
|
2024-10-29 09:03:13 +00:00
|
|
|
nodes.add(
|
|
|
|
(n.node.address.ip, n.node.address.udpPort, n.node.address.tcpPort, n.node.pubkey)
|
|
|
|
)
|
2019-02-05 15:40:29 +00:00
|
|
|
if nodes.len == MAX_NEIGHBOURS_PER_PACKET:
|
|
|
|
flush()
|
|
|
|
|
2024-10-29 09:03:13 +00:00
|
|
|
if nodes.len != 0:
|
|
|
|
flush()
|
2019-02-05 15:40:29 +00:00
|
|
|
|
2022-03-15 17:08:15 +00:00
|
|
|
proc newDiscoveryProtocol*(
|
2024-10-29 09:03:13 +00:00
|
|
|
privKey: PrivateKey,
|
|
|
|
address: Address,
|
2022-03-15 17:08:15 +00:00
|
|
|
bootstrapNodes: openArray[ENode],
|
2024-10-29 09:03:13 +00:00
|
|
|
bindPort: Port,
|
|
|
|
bindIp = IPv6_any(),
|
|
|
|
rng = newRng(),
|
|
|
|
): DiscoveryProtocol =
|
2022-03-15 17:08:15 +00:00
|
|
|
let
|
|
|
|
localNode = newNode(privKey.toPublicKey(), address)
|
|
|
|
discovery = DiscoveryProtocol(
|
|
|
|
privKey: privKey,
|
|
|
|
address: address,
|
|
|
|
localNode: localNode,
|
|
|
|
bindIp: bindIp,
|
2024-10-29 09:03:13 +00:00
|
|
|
bindPort: bindPort,
|
|
|
|
)
|
2022-03-15 17:08:15 +00:00
|
|
|
kademlia = newKademliaProtocol(localNode, discovery, rng = rng)
|
|
|
|
|
|
|
|
discovery.kademlia = kademlia
|
|
|
|
|
2024-10-29 09:03:13 +00:00
|
|
|
for n in bootstrapNodes:
|
|
|
|
discovery.bootstrapNodes.add(newNode(n))
|
2022-03-15 17:08:15 +00:00
|
|
|
|
|
|
|
discovery
|
2019-02-05 15:40:29 +00:00
|
|
|
|
2024-10-29 09:03:13 +00:00
|
|
|
proc recvPing(
|
|
|
|
d: DiscoveryProtocol, node: Node, msgHash: MDigest[256]
|
|
|
|
) {.raises: [ValueError].} =
|
2019-02-05 15:40:29 +00:00
|
|
|
d.kademlia.recvPing(node, msgHash)
|
|
|
|
|
2024-10-29 09:03:13 +00:00
|
|
|
proc recvPong(
|
|
|
|
d: DiscoveryProtocol, node: Node, payload: seq[byte]
|
|
|
|
) {.raises: [RlpError].} =
|
2020-04-20 18:14:39 +00:00
|
|
|
let rlp = rlpFromBytes(payload)
|
|
|
|
let tok = rlp.listElem(1).toBytes()
|
2019-02-05 15:40:29 +00:00
|
|
|
d.kademlia.recvPong(node, tok)
|
|
|
|
|
2024-10-29 09:03:13 +00:00
|
|
|
proc recvNeighbours(
|
|
|
|
d: DiscoveryProtocol, node: Node, payload: seq[byte]
|
|
|
|
) {.raises: [RlpError].} =
|
2020-04-20 18:14:39 +00:00
|
|
|
let rlp = rlpFromBytes(payload)
|
2019-02-05 15:40:29 +00:00
|
|
|
let neighboursList = rlp.listElem(0)
|
|
|
|
let sz = neighboursList.listLen()
|
|
|
|
|
|
|
|
var neighbours = newSeqOfCap[Node](16)
|
|
|
|
for i in 0 ..< sz:
|
|
|
|
let n = neighboursList.listElem(i)
|
2024-12-02 08:38:00 +00:00
|
|
|
if n.listLen() != 4:
|
|
|
|
raise newException(RlpError, "Invalid nodes list")
|
|
|
|
|
2019-02-05 15:40:29 +00:00
|
|
|
let ipBlob = n.listElem(0).toBytes
|
|
|
|
var ip: IpAddress
|
|
|
|
case ipBlob.len
|
|
|
|
of 4:
|
2024-10-29 09:03:13 +00:00
|
|
|
ip = IpAddress(family: IpAddressFamily.IPv4, address_v4: toArray(4, ipBlob))
|
2019-02-05 15:40:29 +00:00
|
|
|
of 16:
|
2024-10-29 09:03:13 +00:00
|
|
|
ip = IpAddress(family: IpAddressFamily.IPv6, address_v6: toArray(16, ipBlob))
|
2019-02-05 15:40:29 +00:00
|
|
|
else:
|
2024-12-02 08:38:00 +00:00
|
|
|
raise newException(RlpError, "Invalid RLP byte string length for IP address")
|
2019-02-05 15:40:29 +00:00
|
|
|
|
|
|
|
let udpPort = n.listElem(1).toInt(uint16).Port
|
|
|
|
let tcpPort = n.listElem(2).toInt(uint16).Port
|
2024-12-02 08:38:00 +00:00
|
|
|
let pk = PublicKey.fromRaw(n.listElem(3).toBytes).valueOr:
|
|
|
|
raise newException(RlpError, "Invalid RLP byte string for node id")
|
2019-02-05 15:40:29 +00:00
|
|
|
|
2024-12-02 08:38:00 +00:00
|
|
|
neighbours.add(newNode(pk, Address(ip: ip, udpPort: udpPort, tcpPort: tcpPort)))
|
2019-02-05 15:40:29 +00:00
|
|
|
d.kademlia.recvNeighbours(node, neighbours)
|
|
|
|
|
2024-10-29 09:03:13 +00:00
|
|
|
proc recvFindNode(
|
|
|
|
d: DiscoveryProtocol, node: Node, payload: openArray[byte]
|
|
|
|
) {.raises: [RlpError, ValueError].} =
|
2020-04-20 18:14:39 +00:00
|
|
|
let rlp = rlpFromBytes(payload)
|
2019-02-05 15:40:29 +00:00
|
|
|
trace "<<< find_node from ", node
|
|
|
|
let rng = rlp.listElem(0).toBytes
|
2019-05-23 10:02:58 +00:00
|
|
|
# Check for pubkey len
|
|
|
|
if rng.len == 64:
|
2024-12-04 11:40:44 +00:00
|
|
|
let nodeId = UInt256.fromBytesBE(rng.toOpenArray(32, 63))
|
2019-05-23 10:02:58 +00:00
|
|
|
d.kademlia.recvFindNode(node, nodeId)
|
|
|
|
else:
|
|
|
|
trace "Invalid target public key received"
|
|
|
|
|
2024-10-29 09:03:13 +00:00
|
|
|
proc expirationValid(
|
|
|
|
cmdId: CommandId, rlpEncodedPayload: openArray[byte]
|
|
|
|
): bool {.raises: [DiscProtocolError, RlpError].} =
|
2019-05-23 10:02:58 +00:00
|
|
|
## Can only raise `DiscProtocolError` and all of `RlpError`
|
2019-05-24 08:14:05 +00:00
|
|
|
# Check if there is a payload
|
2019-05-23 10:02:58 +00:00
|
|
|
if rlpEncodedPayload.len <= 0:
|
|
|
|
raise newException(DiscProtocolError, "RLP stream is empty")
|
2020-04-20 18:14:39 +00:00
|
|
|
let rlp = rlpFromBytes(rlpEncodedPayload)
|
2019-05-23 10:02:58 +00:00
|
|
|
# Check payload is an RLP list and if the list has the minimum items required
|
|
|
|
# for this packet type
|
2019-05-24 08:14:05 +00:00
|
|
|
if rlp.isList and rlp.listLen >= MinListLen[cmdId]:
|
2019-05-23 10:02:58 +00:00
|
|
|
# Expiration is always the last mandatory item of the list
|
|
|
|
let expiration = rlp.listElem(MinListLen[cmdId] - 1).toInt(uint32)
|
|
|
|
result = epochTime() <= expiration.float
|
|
|
|
else:
|
|
|
|
raise newException(DiscProtocolError, "Invalid RLP list for this packet id")
|
2019-02-05 15:40:29 +00:00
|
|
|
|
2024-10-29 09:03:13 +00:00
|
|
|
proc receive*(
|
|
|
|
d: DiscoveryProtocol, a: Address, msg: openArray[byte]
|
|
|
|
) {.raises: [DiscProtocolError, RlpError, ValueError].} =
|
2019-06-17 20:12:50 +00:00
|
|
|
# Note: export only needed for testing
|
2020-04-06 16:24:15 +00:00
|
|
|
let msgHash = validateMsgHash(msg)
|
|
|
|
if msgHash.isOk():
|
|
|
|
let remotePubkey = recoverMsgPublicKey(msg)
|
|
|
|
if remotePubkey.isOk:
|
2019-02-05 15:40:29 +00:00
|
|
|
let (cmdId, payload) = unpack(msg)
|
2019-06-17 20:12:50 +00:00
|
|
|
|
2019-05-23 10:02:58 +00:00
|
|
|
if expirationValid(cmdId, payload):
|
2020-04-06 16:24:15 +00:00
|
|
|
let node = newNode(remotePubkey[], a)
|
2019-02-05 15:40:29 +00:00
|
|
|
case cmdId
|
|
|
|
of cmdPing:
|
2020-04-06 16:24:15 +00:00
|
|
|
d.recvPing(node, msgHash[])
|
2019-02-05 15:40:29 +00:00
|
|
|
of cmdPong:
|
|
|
|
d.recvPong(node, payload)
|
|
|
|
of cmdNeighbours:
|
|
|
|
d.recvNeighbours(node, payload)
|
|
|
|
of cmdFindNode:
|
|
|
|
d.recvFindNode(node, payload)
|
2022-04-04 20:31:09 +00:00
|
|
|
of cmdENRRequest, cmdENRResponse:
|
|
|
|
# TODO: Implement EIP-868
|
|
|
|
discard
|
2019-02-05 15:40:29 +00:00
|
|
|
else:
|
|
|
|
trace "Received msg already expired", cmdId, a
|
|
|
|
else:
|
2020-04-06 16:24:15 +00:00
|
|
|
notice "Wrong public key from ", a, err = remotePubkey.error
|
2019-02-05 15:40:29 +00:00
|
|
|
else:
|
2020-04-06 16:24:15 +00:00
|
|
|
notice "Wrong msg mac from ", a
|
2019-02-05 15:40:29 +00:00
|
|
|
|
2024-10-29 09:03:13 +00:00
|
|
|
proc processClient(
|
|
|
|
transp: DatagramTransport, raddr: TransportAddress
|
|
|
|
): Future[void] {.async: (raises: []).} =
|
2019-02-05 15:40:29 +00:00
|
|
|
var proto = getUserData[DiscoveryProtocol](transp)
|
2024-10-29 09:03:13 +00:00
|
|
|
let buf =
|
|
|
|
try:
|
|
|
|
transp.getMessage()
|
|
|
|
except TransportOsError as e:
|
|
|
|
# This is likely to be local network connection issues.
|
|
|
|
warn "Transport getMessage", exception = e.name, msg = e.msg
|
|
|
|
return
|
|
|
|
except TransportError as exc:
|
|
|
|
debug "getMessage error", msg = exc.msg
|
|
|
|
return
|
2019-02-05 15:40:29 +00:00
|
|
|
try:
|
2024-01-22 09:47:46 +00:00
|
|
|
let a = Address(ip: raddr.address, udpPort: raddr.port, tcpPort: raddr.port)
|
2019-02-05 15:40:29 +00:00
|
|
|
proto.receive(a, buf)
|
2019-12-02 15:31:10 +00:00
|
|
|
except RlpError as e:
|
|
|
|
debug "Receive failed", exc = e.name, err = e.msg
|
|
|
|
except DiscProtocolError as e:
|
|
|
|
debug "Receive failed", exc = e.name, err = e.msg
|
2021-04-18 15:10:10 +00:00
|
|
|
except ValueError as e:
|
2019-12-02 15:31:10 +00:00
|
|
|
debug "Receive failed", exc = e.name, err = e.msg
|
2019-02-05 15:40:29 +00:00
|
|
|
|
2023-05-10 13:50:04 +00:00
|
|
|
proc open*(d: DiscoveryProtocol) {.raises: [CatchableError].} =
|
2022-03-15 17:08:15 +00:00
|
|
|
# TODO: allow binding to both IPv4 and IPv6
|
|
|
|
let ta = initTAddress(d.bindIp, d.bindPort)
|
2019-02-05 15:40:29 +00:00
|
|
|
d.transp = newDatagramTransport(processClient, udata = d, local = ta)
|
|
|
|
|
2021-04-27 19:11:46 +00:00
|
|
|
proc lookupRandom*(d: DiscoveryProtocol): Future[seq[Node]] =
|
2019-02-05 15:40:29 +00:00
|
|
|
d.kademlia.lookupRandom()
|
|
|
|
|
|
|
|
proc run(d: DiscoveryProtocol) {.async.} =
|
|
|
|
while true:
|
|
|
|
discard await d.lookupRandom()
|
2021-04-27 09:29:54 +00:00
|
|
|
await sleepAsync(chronos.seconds(3))
|
2019-02-05 15:40:29 +00:00
|
|
|
trace "Discovered nodes", nodes = d.kademlia.nodesDiscovered
|
|
|
|
|
|
|
|
proc bootstrap*(d: DiscoveryProtocol) {.async.} =
|
|
|
|
await d.kademlia.bootstrap(d.bootstrapNodes)
|
|
|
|
discard d.run()
|
|
|
|
|
|
|
|
proc resolve*(d: DiscoveryProtocol, n: NodeId): Future[Node] =
|
|
|
|
d.kademlia.resolve(n)
|
|
|
|
|
2021-04-27 19:11:46 +00:00
|
|
|
proc randomNodes*(d: DiscoveryProtocol, count: int): seq[Node] =
|
2019-02-05 15:40:29 +00:00
|
|
|
d.kademlia.randomNodes(count)
|
|
|
|
|
|
|
|
when isMainModule:
|
2024-10-29 09:03:13 +00:00
|
|
|
import stew/byteutils, ./bootnodes
|
2019-02-05 15:40:29 +00:00
|
|
|
|
|
|
|
block:
|
2024-10-29 09:03:13 +00:00
|
|
|
let m =
|
|
|
|
hexToSeqByte"79664bff52ee17327b4a2d8f97d8fb32c9244d719e5038eb4f6b64da19ca6d271d659c3ad9ad7861a928ca85f8d8debfbe6b7ade26ad778f2ae2ba712567fcbd55bc09eb3e74a893d6b180370b266f6aaf3fe58a0ad95f7435bf3ddf1db940d20102f2cb842edbd4d182944382765da0ab56fb9e64a85a597e6bb27c656b4f1afb7e06b0fd4e41ccde6dba69a3c4a150845aaa4de2"
|
2020-04-06 16:24:15 +00:00
|
|
|
discard validateMsgHash(m).expect("valid hash")
|
|
|
|
var remotePubkey = recoverMsgPublicKey(m).expect("valid key")
|
2019-02-05 15:40:29 +00:00
|
|
|
|
|
|
|
let (cmdId, payload) = unpack(m)
|
2024-10-29 09:03:13 +00:00
|
|
|
doAssert(
|
|
|
|
payload ==
|
|
|
|
hexToSeqByte"f2cb842edbd4d182944382765da0ab56fb9e64a85a597e6bb27c656b4f1afb7e06b0fd4e41ccde6dba69a3c4a150845aaa4de2"
|
|
|
|
)
|
2019-03-13 22:15:26 +00:00
|
|
|
doAssert(cmdId == cmdPong)
|
2024-10-29 09:03:13 +00:00
|
|
|
doAssert(
|
|
|
|
remotePubkey ==
|
|
|
|
PublicKey.fromHex(
|
|
|
|
"78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d"
|
|
|
|
)[]
|
|
|
|
)
|
2019-02-05 15:40:29 +00:00
|
|
|
|
2024-10-29 09:03:13 +00:00
|
|
|
let privKey = PrivateKey.fromHex(
|
|
|
|
"a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617"
|
|
|
|
)[]
|
2019-02-05 15:40:29 +00:00
|
|
|
|
|
|
|
# echo privKey
|
|
|
|
|
|
|
|
# block:
|
|
|
|
# var b = @[1.byte, 2, 3]
|
|
|
|
# let m = pack(cmdPing, b.initBytesRange, privKey)
|
|
|
|
# let (remotePubkey, cmdId, payload) = unpack(m)
|
2019-03-13 22:15:26 +00:00
|
|
|
# doAssert(remotePubkey.raw_key.toHex == privKey.public_key.raw_key.toHex)
|
2019-02-05 15:40:29 +00:00
|
|
|
|
2024-10-29 09:03:13 +00:00
|
|
|
var nodes = newSeq[ENode]()
|
|
|
|
for item in MainnetBootnodes:
|
|
|
|
nodes.add(ENode.fromString(item)[])
|
2019-02-05 15:40:29 +00:00
|
|
|
|
|
|
|
let listenPort = Port(30310)
|
|
|
|
var address = Address(udpPort: listenPort, tcpPort: listenPort)
|
|
|
|
address.ip.family = IpAddressFamily.IPv4
|
2024-10-29 09:03:13 +00:00
|
|
|
let discovery = newDiscoveryProtocol(privKey, address, nodes, bindPort = listenPort)
|
2019-02-05 15:40:29 +00:00
|
|
|
|
2022-03-15 17:08:15 +00:00
|
|
|
echo discovery.localNode.node.pubkey
|
|
|
|
echo "this_node.id: ", discovery.localNode.id.toHex()
|
2019-02-05 15:40:29 +00:00
|
|
|
|
|
|
|
discovery.open()
|
|
|
|
|
|
|
|
proc test() {.async.} =
|
2020-04-02 12:40:29 +00:00
|
|
|
{.gcsafe.}:
|
|
|
|
await discovery.bootstrap()
|
2024-10-29 09:03:13 +00:00
|
|
|
for node in discovery.randomNodes(discovery.kademlia.nodesDiscovered):
|
|
|
|
echo node
|
2019-02-05 15:40:29 +00:00
|
|
|
waitFor test()
|