diff --git a/examples/bootstrap.nim b/examples/bootstrap.nim index 9cd92e224..fc4c2ec97 100644 --- a/examples/bootstrap.nim +++ b/examples/bootstrap.nim @@ -1,4 +1,4 @@ -import asyncdispatch2, nimcrypto, strutils +import chronos, nimcrypto, strutils import ../libp2p/daemon/daemonapi, ../libp2p/[base58, multicodec, multiaddress] import hexdump diff --git a/examples/node.nim b/examples/node.nim index 9f6d2e756..8fae43d37 100644 --- a/examples/node.nim +++ b/examples/node.nim @@ -1,4 +1,4 @@ -import asyncdispatch2, nimcrypto, strutils, os +import chronos, nimcrypto, strutils, os import ../libp2p/daemon/daemonapi, ../libp2p/[base58, multiaddress] proc main(bn: string) {.async.} = diff --git a/libp2p/daemon/daemonapi.nim b/libp2p/daemon/daemonapi.nim index 7da60e761..a70da119f 100644 --- a/libp2p/daemon/daemonapi.nim +++ b/libp2p/daemon/daemonapi.nim @@ -10,7 +10,7 @@ ## This module implementes API for `go-libp2p-daemon`. import os, osproc, strutils, tables, streams, strtabs import chronos -import ../varint, ../multiaddress, ../multicodec, ../base58, ../cid +import ../varint, ../multiaddress, ../multicodec, ../base58, ../cid, ../peer import ../wire, ../protobuf/minprotobuf when not defined(windows): @@ -76,7 +76,7 @@ type VALUE = 1, END = 2 - PeerID* = seq[byte] + # PeerID* = seq[byte] MultiProtocol* = string LibP2PPublicKey* = seq[byte] DHTValue* = seq[byte] @@ -745,8 +745,7 @@ proc transactMessage(transp: StreamTransport, proc getPeerInfo(pb: var ProtoBuffer): PeerInfo = ## Get PeerInfo object from ``pb``. result.addresses = newSeq[MultiAddress]() - result.peer = newSeq[byte]() - if pb.getBytes(1, result.peer) == -1: + if pb.getValue(1, result.peer) == -1: raise newException(DaemonLocalError, "Missing required field `peer`!") var address = newSeq[byte]() while pb.getBytes(2, address) != -1: @@ -803,10 +802,10 @@ proc openStream*(api: DaemonAPI, peer: PeerID, pb.withMessage() do: var res = pb.enterSubmessage() if res == cast[int](ResponseType.STREAMINFO): - stream.peer = newSeq[byte]() + # stream.peer = newSeq[byte]() var raddress = newSeq[byte]() stream.protocol = "" - if pb.getLengthValue(1, stream.peer) == -1: + if pb.getValue(1, stream.peer) == -1: raise newException(DaemonLocalError, "Missing `peer` field!") if pb.getLengthValue(2, raddress) == -1: raise newException(DaemonLocalError, "Missing `address` field!") @@ -825,10 +824,9 @@ proc streamHandler(server: StreamServer, transp: StreamTransport) {.async.} = var message = await transp.recvMessage() var pb = initProtoBuffer(message) var stream = new P2PStream - stream.peer = newSeq[byte]() var raddress = newSeq[byte]() stream.protocol = "" - if pb.getLengthValue(1, stream.peer) == -1: + if pb.getValue(1, stream.peer) == -1: raise newException(DaemonLocalError, "Missing `peer` field!") if pb.getLengthValue(2, raddress) == -1: raise newException(DaemonLocalError, "Missing `address` field!") @@ -929,6 +927,10 @@ proc dhtGetSingleValue(pb: var ProtoBuffer): seq[byte] = if pb.getLengthValue(3, result) == -1: raise newException(DaemonLocalError, "Missing field `value`!") +proc dhtGetSinglePeerID(pb: var ProtoBuffer): PeerID = + if pb.getValue(3, result) == -1: + raise newException(DaemonLocalError, "Missing field `value`!") + proc enterDhtMessage(pb: var ProtoBuffer, rt: DHTResponseType) {.inline.} = var dtype: uint var res = pb.enterSubmessage() @@ -1074,7 +1076,7 @@ proc dhtGetClosestPeers*(api: DaemonAPI, key: string, var cpb = initProtoBuffer(message) if cpb.getDhtMessageType() == DHTResponseType.END: break - list.add(cpb.dhtGetSingleValue()) + list.add(cpb.dhtGetSinglePeerID()) result = list finally: await api.closeConnection(transp) @@ -1152,12 +1154,11 @@ proc pubsubListPeers*(api: DaemonAPI, try: var pb = await transp.transactMessage(requestPSListPeers(topic)) withMessage(pb) do: + var peer: PeerID pb.enterPsMessage() var peers = newSeq[PeerID]() - var peer = newSeq[byte]() - while pb.getBytes(2, peer) != -1: + while pb.getValue(2, peer) != -1: peers.add(peer) - peer.setLen(0) result = peers finally: await api.closeConnection(transp) @@ -1174,28 +1175,25 @@ proc pubsubPublish*(api: DaemonAPI, topic: string, await api.closeConnection(transp) proc getPubsubMessage*(pb: var ProtoBuffer): PubSubMessage = + result.data = newSeq[byte]() + result.seqno = newSeq[byte]() + result.signature = newSeq[byte]() + result.key = newSeq[byte]() + discard pb.getValue(1, result.peer) + discard pb.getBytes(2, result.data) + discard pb.getBytes(3, result.seqno) var item = newSeq[byte]() - for field in 1..6: - while true: - if pb.getBytes(field, item) == -1: - break - if field == 1: - result.peer = item - elif field == 2: - result.data = item - elif field == 3: - result.seqno = item - elif field == 4: - var copyitem = item - var stritem = cast[string](copyitem) - if len(result.topics) == 0: - result.topics = newSeq[string]() - result.topics.add(stritem) - elif field == 5: - result.signature = item - elif field == 6: - result.key = item - item.setLen(0) + while true: + if pb.getBytes(4, item) == -1: + break + var copyitem = item + var stritem = cast[string](copyitem) + if len(result.topics) == 0: + result.topics = newSeq[string]() + result.topics.add(stritem) + item.setLen(0) + discard pb.getBytes(5, result.signature) + discard pb.getBytes(6, result.key) proc pubsubLoop(api: DaemonAPI, ticket: PubsubTicket) {.async.} = while true: @@ -1232,7 +1230,7 @@ proc `$`*(pinfo: PeerInfo): string = ## Get string representation of ``PeerInfo`` object. result = newStringOfCap(128) result.add("{PeerID: '") - result.add(Base58.encode(pinfo.peer)) + result.add($pinfo.peer.pretty()) result.add("' Addresses: [") let length = len(pinfo.addresses) for i in 0..= result and result > 0: + copyMem(addr data[0], addr p[0], result) + +proc getBytes*(peerid: PeerID): seq[byte] {.inline.} = + ## Return PeerID as array of bytes. + var p = cast[seq[byte]](peerid) + result = @p + +proc fromKey*(pubkey: PublicKey): PeerID = + ## Returns the PeerID corresponding to public key ``pubkey``. + var pubraw = pubkey.getBytes() + var mh: MultiHash + var codec: MultiCodec + if len(pubraw) <= maxInlineKeyLength: + mh = MultiHash.digest("identity", pubraw) + else: + mh = MultiHash.digest("sha2-256", pubraw) + result = cast[PeerID](mh.data.buffer) + +proc fromKey*(seckey: PrivateKey): PeerID {.inline.} = + ## Returns the PeerID corresponding to private key ``seckey``. + result = fromKey(seckey.getKey()) + +proc hex*(peerid: PeerID): string = + ## Returns hexadecimal string representation of ``peerid``. + var p = cast[seq[byte]](peerid) + if len(p) > 0: + result = toHex(p) + +proc len*(a: PeerID): int {.borrow.} + +proc cmp*(a, b: PeerID): int = + ## Compares two peer ids ``a`` and ``b``. + ## Returns: + ## + ## | 0 iff a == b + ## | < 0 iff a < b + ## | > 0 iff a > b + var ab = cast[seq[byte]](a) + var bb = cast[seq[byte]](b) + var i = 0 + var m = min(len(ab), len(bb)) + while i < m: + result = ord(ab[i]) - ord(bb[i]) + if result != 0: return + inc(i) + result = len(ab) - len(bb) + +proc `<=`*(a, b: PeerID): bool {.inline.} = + (cmp(a, b) <= 0) + +proc `<`*(a, b: PeerID): bool {.inline.} = + (cmp(a, b) < 0) + +proc `>=`*(a, b: PeerID): bool {.inline.} = + (cmp(a, b) >= 0) + +proc `>`*(a, b: PeerID): bool {.inline.} = + (cmp(a, b) > 0) + +proc `==`*(a, b: PeerID): bool {.inline.} = + (cmp(a, b) == 0) + +proc hash*(peerid: PeerID): Hash {.inline.} = + var p = cast[seq[byte]](peerid) + result = hash(p) + +proc validate*(peerid: PeerID): bool = + ## Validate check if ``peerid`` is empty or not. + var p = cast[seq[byte]](peerid) + if len(p) > 0: + result = MultiHash.validate(p) + +proc hasPublicKey*(peerid: PeerID): bool = + ## Returns ``true`` if ``peerid`` is small enough to hold public key inside. + var mh: MultiHash + var p = cast[seq[byte]](peerid) + if len(p) > 0: + if MultiHash.decode(p, mh) > 0: + if mh.mcodec == multiCodec("identity"): + result = true + +proc extractPublicKey*(peerid: PeerID, pubkey: var PublicKey): bool = + ## Returns ``true`` if public key was successfully decoded and stored + ## in ``pubkey``. + ## + ## Returns ``false`` otherwise + var mh: MultiHash + var p = cast[seq[byte]](peerid) + if len(p) > 0: + if MultiHash.decode(p, mh) > 0: + if mh.mcodec == multiCodec("identity"): + let length = len(mh.data.buffer) + result = pubkey.init(mh.data.buffer.toOpenArray(mh.dpos, length - 1)) + +proc match*(peerid: PeerID, pubkey: PublicKey): bool {.inline.} = + ## Returns ``true`` if ``peerid`` matches public key ``pubkey``. + result = (peerid == pubkey.fromKey()) + +proc match*(peerid: PeerID, seckey: PrivateKey): bool {.inline.} = + ## Returns ``true`` if ``peerid`` matches private key ``seckey``. + result = (peerid == seckey.fromKey()) + +proc `$`*(peerid: PeerID): string = + ## Returns compact string representation of ``peerid``. + var pid = peerid.pretty() + if len(pid) <= 10: + result = pid + else: + for i in 0..<2: + result.add(pid[i]) + result.add("*") + for i in (len(pid) - 6)..(len(pid) - 1): + result.add(pid[i]) + +proc write*(vb: var VBuffer, peerid: PeerID) {.inline.} = + ## Write PeerID value ``peerid`` to buffer ``vb``. + var p = cast[seq[byte]](peerid) + vb.writeSeq(p) + +proc initProtoField*(index: int, peerid: PeerID): ProtoField = + ## Initialize ProtoField with PeerID ``value``. + var p = cast[seq[byte]](peerid) + result = initProtoField(index, p) + +proc getValue*(data: var ProtoBuffer, field: int, value: var PeerID): int = + ## Read ``PeerID`` from ProtoBuf's message and validate it. + var buffer: seq[byte] + result = getLengthValue(data, field, buffer) + if result > 0: + value = cast[PeerID](buffer) + if not value.validate(): + result = -1