Add VBuffer implementation.
Add MultiAddress implementation and tests. Adapt chat to use /p2p-circuit
This commit is contained in:
parent
cd190e62c2
commit
98aa3ed281
|
@ -1,5 +1,5 @@
|
||||||
import asyncdispatch2, nimcrypto, strutils
|
import asyncdispatch2, nimcrypto, strutils
|
||||||
import ../libp2p/daemon/daemonapi
|
import ../libp2p/daemon/daemonapi, ../libp2p/[base58, multiaddress]
|
||||||
|
|
||||||
const
|
const
|
||||||
ConsoleAddress = "/tmp/console-chat.sock"
|
ConsoleAddress = "/tmp/console-chat.sock"
|
||||||
|
@ -39,19 +39,23 @@ proc serveThread(server: StreamServer,
|
||||||
if line.startsWith("/connect"):
|
if line.startsWith("/connect"):
|
||||||
var parts = line.split(" ")
|
var parts = line.split(" ")
|
||||||
if len(parts) == 2:
|
if len(parts) == 2:
|
||||||
var address = fromHex(parts[1])
|
var peerId = Base58.decode(parts[1])
|
||||||
echo "= Searching for peer ", toHex(address)
|
var address = MultiAddress.init(P_P2PCIRCUIT)
|
||||||
var id = await udata.api.dhtFindPeer(address)
|
address &= MultiAddress.init(P_P2P, peerId)
|
||||||
echo "==="
|
echo "= Searching for peer ", parts[1]
|
||||||
echo repr id
|
var id = await udata.api.dhtFindPeer(peerId)
|
||||||
echo "==="
|
echo "Peer " & parts[1] & " found at addresses:"
|
||||||
echo "= Connecting to peer ", toHex(address)
|
for item in id.addresses:
|
||||||
await udata.api.connect(id.peer, id.addresses)
|
echo $item
|
||||||
echo "= Opening stream to peer chat ", toHex(address)
|
echo "= Connecting to peer ", $address
|
||||||
var stream = await udata.api.openStream(id.peer, ServerProtocols)
|
await udata.api.connect(peerId, @[address], 30)
|
||||||
|
echo "= Opening stream to peer chat ", parts[1]
|
||||||
|
var stream = await udata.api.openStream(peerId, ServerProtocols)
|
||||||
udata.remotes.add(stream.transp)
|
udata.remotes.add(stream.transp)
|
||||||
echo "= Connected to peer chat ", toHex(address)
|
echo "= Connected to peer chat ", parts[1]
|
||||||
asyncCheck remoteReader(stream.transp)
|
asyncCheck remoteReader(stream.transp)
|
||||||
|
elif line.startsWith("/exit"):
|
||||||
|
quit(0)
|
||||||
else:
|
else:
|
||||||
var msg = line & "\r\n"
|
var msg = line & "\r\n"
|
||||||
echo "<< ", line
|
echo "<< ", line
|
||||||
|
@ -88,7 +92,7 @@ proc main() {.async.} =
|
||||||
echo ">> ", line
|
echo ">> ", line
|
||||||
|
|
||||||
await data.api.addHandler(ServerProtocols, streamHandler)
|
await data.api.addHandler(ServerProtocols, streamHandler)
|
||||||
echo "= Your PeerID is ", toHex(id.peer)
|
echo "= Your PeerID is ", Base58.encode(id.peer)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
waitFor(main())
|
waitFor(main())
|
||||||
|
|
|
@ -111,6 +111,8 @@ proc encode*(btype: typedesc[Base58C],
|
||||||
if btype.encode(inbytes, result.toOpenArray(0, size - 1),
|
if btype.encode(inbytes, result.toOpenArray(0, size - 1),
|
||||||
size) == Base58Status.Success:
|
size) == Base58Status.Success:
|
||||||
result.setLen(size)
|
result.setLen(size)
|
||||||
|
else:
|
||||||
|
result = ""
|
||||||
|
|
||||||
proc decode*(btype: typedesc[Base58C], instr: string,
|
proc decode*(btype: typedesc[Base58C], instr: string,
|
||||||
outbytes: var openarray[byte], outlen: var int): Base58Status =
|
outbytes: var openarray[byte], outlen: var int): Base58Status =
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
## This module implementes API for `go-libp2p-daemon`.
|
## This module implementes API for `go-libp2p-daemon`.
|
||||||
import os, osproc, strutils, tables, streams
|
import os, osproc, strutils, tables, streams
|
||||||
import asyncdispatch2
|
import asyncdispatch2
|
||||||
import ../varint, ../protobuf/minprotobuf, transpool
|
import ../varint, ../multiaddress, ../protobuf/minprotobuf, transpool
|
||||||
|
|
||||||
when not defined(windows):
|
when not defined(windows):
|
||||||
import posix
|
import posix
|
||||||
|
@ -65,7 +65,7 @@ type
|
||||||
|
|
||||||
PeerID* = seq[byte]
|
PeerID* = seq[byte]
|
||||||
MultiProtocol* = string
|
MultiProtocol* = string
|
||||||
MultiAddress* = seq[byte]
|
# MultiAddress* = seq[byte]
|
||||||
CID* = seq[byte]
|
CID* = seq[byte]
|
||||||
LibP2PPublicKey* = seq[byte]
|
LibP2PPublicKey* = seq[byte]
|
||||||
DHTValue* = seq[byte]
|
DHTValue* = seq[byte]
|
||||||
|
@ -120,7 +120,7 @@ proc requestConnect(peerid: PeerID,
|
||||||
var msg = initProtoBuffer()
|
var msg = initProtoBuffer()
|
||||||
msg.write(initProtoField(1, peerid))
|
msg.write(initProtoField(1, peerid))
|
||||||
for item in addresses:
|
for item in addresses:
|
||||||
msg.write(initProtoField(2, item))
|
msg.write(initProtoField(2, item.data.buffer))
|
||||||
if timeout > 0:
|
if timeout > 0:
|
||||||
msg.write(initProtoField(3, timeout))
|
msg.write(initProtoField(3, timeout))
|
||||||
result.write(initProtoField(1, cast[uint](RequestType.CONNECT)))
|
result.write(initProtoField(1, cast[uint](RequestType.CONNECT)))
|
||||||
|
@ -501,7 +501,8 @@ proc getPeerInfo(pb: var ProtoBuffer): PeerInfo =
|
||||||
var address = newSeq[byte]()
|
var address = newSeq[byte]()
|
||||||
while pb.getBytes(2, address) != -1:
|
while pb.getBytes(2, address) != -1:
|
||||||
if len(address) != 0:
|
if len(address) != 0:
|
||||||
result.addresses.add(address)
|
var copyaddr = address
|
||||||
|
result.addresses.add(MultiAddress.init(copyaddr))
|
||||||
address.setLen(0)
|
address.setLen(0)
|
||||||
|
|
||||||
proc identity*(api: DaemonAPI): Future[PeerInfo] {.async.} =
|
proc identity*(api: DaemonAPI): Future[PeerInfo] {.async.} =
|
||||||
|
@ -549,12 +550,13 @@ proc openStream*(api: DaemonAPI, peer: PeerID,
|
||||||
var res = pb.enterSubmessage()
|
var res = pb.enterSubmessage()
|
||||||
if res == cast[int](ResponseType.STREAMINFO):
|
if res == cast[int](ResponseType.STREAMINFO):
|
||||||
stream.peer = newSeq[byte]()
|
stream.peer = newSeq[byte]()
|
||||||
stream.raddress = newSeq[byte]()
|
var raddress = newSeq[byte]()
|
||||||
stream.protocol = ""
|
stream.protocol = ""
|
||||||
if pb.getLengthValue(1, stream.peer) == -1:
|
if pb.getLengthValue(1, stream.peer) == -1:
|
||||||
raise newException(DaemonLocalError, "Missing `peer` field!")
|
raise newException(DaemonLocalError, "Missing `peer` field!")
|
||||||
if pb.getLengthValue(2, stream.raddress) == -1:
|
if pb.getLengthValue(2, raddress) == -1:
|
||||||
raise newException(DaemonLocalError, "Missing `address` field!")
|
raise newException(DaemonLocalError, "Missing `address` field!")
|
||||||
|
stream.raddress = MultiAddress.init(raddress)
|
||||||
if pb.getLengthValue(3, stream.protocol) == -1:
|
if pb.getLengthValue(3, stream.protocol) == -1:
|
||||||
raise newException(DaemonLocalError, "Missing `proto` field!")
|
raise newException(DaemonLocalError, "Missing `proto` field!")
|
||||||
stream.flags.incl(Outbound)
|
stream.flags.incl(Outbound)
|
||||||
|
@ -571,12 +573,13 @@ proc streamHandler(server: StreamServer, transp: StreamTransport) {.async.} =
|
||||||
var pb = initProtoBuffer(message)
|
var pb = initProtoBuffer(message)
|
||||||
var stream = new P2PStream
|
var stream = new P2PStream
|
||||||
stream.peer = newSeq[byte]()
|
stream.peer = newSeq[byte]()
|
||||||
stream.raddress = newSeq[byte]()
|
var raddress = newSeq[byte]()
|
||||||
stream.protocol = ""
|
stream.protocol = ""
|
||||||
if pb.getLengthValue(1, stream.peer) == -1:
|
if pb.getLengthValue(1, stream.peer) == -1:
|
||||||
raise newException(DaemonLocalError, "Missing `peer` field!")
|
raise newException(DaemonLocalError, "Missing `peer` field!")
|
||||||
if pb.getLengthValue(2, stream.raddress) == -1:
|
if pb.getLengthValue(2, raddress) == -1:
|
||||||
raise newException(DaemonLocalError, "Missing `address` field!")
|
raise newException(DaemonLocalError, "Missing `address` field!")
|
||||||
|
stream.raddress = MultiAddress.init(raddress)
|
||||||
if pb.getLengthValue(3, stream.protocol) == -1:
|
if pb.getLengthValue(3, stream.protocol) == -1:
|
||||||
raise newException(DaemonLocalError, "Missing `proto` field!")
|
raise newException(DaemonLocalError, "Missing `proto` field!")
|
||||||
stream.flags.incl(Inbound)
|
stream.flags.incl(Inbound)
|
||||||
|
|
|
@ -0,0 +1,636 @@
|
||||||
|
## Nim-Libp2p
|
||||||
|
## Copyright (c) 2018 Status Research & Development GmbH
|
||||||
|
## Licensed under either of
|
||||||
|
## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
||||||
|
## * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||||
|
## at your option.
|
||||||
|
## This file may not be copied, modified, or distributed except according to
|
||||||
|
## those terms.
|
||||||
|
|
||||||
|
## This module implements MultiAddress.
|
||||||
|
import tables, strutils, net
|
||||||
|
import transcoder, base58, vbuffer
|
||||||
|
|
||||||
|
{.deadCodeElim:on.}
|
||||||
|
|
||||||
|
const
|
||||||
|
P_IP4* = 0x0004
|
||||||
|
P_TCP* = 0x0006
|
||||||
|
P_UDP* = 0x0111
|
||||||
|
P_DCCP* = 0x0021
|
||||||
|
P_IP6* = 0x0029
|
||||||
|
P_IP6ZONE* = 0x002A
|
||||||
|
P_DNS4* = 0x0036
|
||||||
|
P_DNS6* = 0x0037
|
||||||
|
P_DNSADDR* = 0x0038
|
||||||
|
P_QUIC* = 0x01CC
|
||||||
|
P_SCTP* = 0x0084
|
||||||
|
P_UDT* = 0x012D
|
||||||
|
P_UTP* = 0x012E
|
||||||
|
P_UNIX* = 0x0190
|
||||||
|
P_P2P* = 0x01A5
|
||||||
|
P_IPFS* = 0x01A5 # alias for backwards compatability
|
||||||
|
P_HTTP* = 0x01E0
|
||||||
|
P_HTTPS* = 0x01BB
|
||||||
|
P_ONION* = 0x01BC
|
||||||
|
P_WS* = 0x01DD
|
||||||
|
LP2P_WSSTAR* = 0x01DF
|
||||||
|
LP2P_WRTCSTAR* = 0x0113
|
||||||
|
LP2P_WRTCDIR* = 0x0114
|
||||||
|
P_P2PCIRCUIT* = 0x0122
|
||||||
|
|
||||||
|
type
|
||||||
|
MAKind* = enum
|
||||||
|
None, Fixed, Length, Path, Marker
|
||||||
|
|
||||||
|
MAProtocol* = object
|
||||||
|
name*: string
|
||||||
|
code*: int
|
||||||
|
size*: int
|
||||||
|
kind: MAKind
|
||||||
|
coder*: Transcoder
|
||||||
|
|
||||||
|
MultiAddress* = object
|
||||||
|
data*: VBuffer
|
||||||
|
|
||||||
|
MultiAddressError* = object of Exception
|
||||||
|
|
||||||
|
proc ip4StB(s: string, vb: var VBuffer): bool =
|
||||||
|
## IPv4 stringToBuffer() implementation.
|
||||||
|
try:
|
||||||
|
var a = parseIpAddress(s)
|
||||||
|
if a.family == IpAddressFamily.IPv4:
|
||||||
|
vb.writeArray(a.address_v4)
|
||||||
|
result = true
|
||||||
|
except:
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc ip4BtS(vb: var VBuffer, s: var string): bool =
|
||||||
|
## IPv4 bufferToString() implementation.
|
||||||
|
var a = IpAddress(family: IpAddressFamily.IPv4)
|
||||||
|
if vb.readArray(a.address_v4) == 4:
|
||||||
|
s = $a
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc ip4VB(vb: var VBuffer): bool =
|
||||||
|
## IPv4 validateBuffer() implementation.
|
||||||
|
var a = IpAddress(family: IpAddressFamily.IPv4)
|
||||||
|
if vb.readArray(a.address_v4) == 4:
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc ip6StB(s: string, vb: var VBuffer): bool =
|
||||||
|
## IPv6 stringToBuffer() implementation.
|
||||||
|
try:
|
||||||
|
var a = parseIpAddress(s)
|
||||||
|
if a.family == IpAddressFamily.IPv6:
|
||||||
|
vb.writeArray(a.address_v6)
|
||||||
|
result = true
|
||||||
|
except:
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc ip6BtS(vb: var VBuffer, s: var string): bool =
|
||||||
|
## IPv6 bufferToString() implementation.
|
||||||
|
var a = IpAddress(family: IpAddressFamily.IPv6)
|
||||||
|
if vb.readArray(a.address_v6) == 16:
|
||||||
|
s = $a
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc ip6VB(vb: var VBuffer): bool =
|
||||||
|
## IPv6 validateBuffer() implementation.
|
||||||
|
var a = IpAddress(family: IpAddressFamily.IPv6)
|
||||||
|
if vb.readArray(a.address_v6) == 16:
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc ip6zoneStB(s: string, vb: var VBuffer): bool =
|
||||||
|
## IPv6 stringToBuffer() implementation.
|
||||||
|
if len(s) > 0:
|
||||||
|
vb.writeSeq(s)
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc ip6zoneBtS(vb: var VBuffer, s: var string): bool =
|
||||||
|
## IPv6 bufferToString() implementation.
|
||||||
|
if vb.readSeq(s) > 0:
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc ip6zoneVB(vb: var VBuffer): bool =
|
||||||
|
## IPv6 validateBuffer() implementation.
|
||||||
|
var s = ""
|
||||||
|
if vb.readSeq(s) > 0:
|
||||||
|
if s.find('/') == -1:
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc portStB(s: string, vb: var VBuffer): bool =
|
||||||
|
## Port number stringToBuffer() implementation.
|
||||||
|
var port: array[2, byte]
|
||||||
|
try:
|
||||||
|
var nport = parseInt(s)
|
||||||
|
if nport < 65536:
|
||||||
|
port[0] = cast[byte]((nport shr 8) and 0xFF)
|
||||||
|
port[1] = cast[byte](nport and 0xFF)
|
||||||
|
vb.writeArray(port)
|
||||||
|
result = true
|
||||||
|
except:
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc portBtS(vb: var VBuffer, s: var string): bool =
|
||||||
|
## Port number bufferToString() implementation.
|
||||||
|
var port: array[2, byte]
|
||||||
|
if vb.readArray(port) == 2:
|
||||||
|
var nport = (cast[uint16](port[0]) shl 8) or cast[uint16](port[1])
|
||||||
|
s = $nport
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc portVB(vb: var VBuffer): bool =
|
||||||
|
## Port number validateBuffer() implementation.
|
||||||
|
var port: array[2, byte]
|
||||||
|
if vb.readArray(port) == 2:
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc p2pStB(s: string, vb: var VBuffer): bool =
|
||||||
|
## P2P address stringToBuffer() implementation.
|
||||||
|
try:
|
||||||
|
var data = Base58.decode(s)
|
||||||
|
vb.writeSeq(data)
|
||||||
|
result = true
|
||||||
|
except:
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc p2pBtS(vb: var VBuffer, s: var string): bool =
|
||||||
|
## P2P address bufferToString() implementation.
|
||||||
|
var address = newSeq[byte]()
|
||||||
|
if vb.readSeq(address) > 0:
|
||||||
|
s = Base58.encode(address)
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc p2pVB(vb: var VBuffer): bool =
|
||||||
|
## P2P address validateBuffer() implementation.
|
||||||
|
## TODO (multihash required)
|
||||||
|
var address = newSeq[byte]()
|
||||||
|
if vb.readSeq(address) > 0:
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc onionStB(s: string, vb: var VBuffer): bool =
|
||||||
|
# TODO (base32, multihash required)
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc onionBtS(vb: var VBuffer, s: var string): bool =
|
||||||
|
# TODO (base32, multihash required)
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc onionVB(vb: var VBuffer): bool =
|
||||||
|
# TODO (base32, multihash required)
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc unixStB(s: string, vb: var VBuffer): bool =
|
||||||
|
## Unix socket name stringToBuffer() implementation.
|
||||||
|
if len(s) > 0:
|
||||||
|
vb.writeSeq(s)
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc unixBtS(vb: var VBuffer, s: var string): bool =
|
||||||
|
## Unix socket name bufferToString() implementation.
|
||||||
|
s = ""
|
||||||
|
if vb.readSeq(s) > 0:
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc unixVB(vb: var VBuffer): bool =
|
||||||
|
## Unix socket name validateBuffer() implementation.
|
||||||
|
var s = ""
|
||||||
|
if vb.readSeq(s) > 0:
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc dnsStB(s: string, vb: var VBuffer): bool =
|
||||||
|
## DNS name stringToBuffer() implementation.
|
||||||
|
if len(s) > 0:
|
||||||
|
vb.writeSeq(s)
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc dnsBtS(vb: var VBuffer, s: var string): bool =
|
||||||
|
## DNS name bufferToString() implementation.
|
||||||
|
s = ""
|
||||||
|
if vb.readSeq(s) > 0:
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc dnsVB(vb: var VBuffer): bool =
|
||||||
|
## DNS name validateBuffer() implementation.
|
||||||
|
var s = ""
|
||||||
|
if vb.readSeq(s) > 0:
|
||||||
|
if s.find('/') == -1:
|
||||||
|
result = true
|
||||||
|
|
||||||
|
const
|
||||||
|
TranscoderIP4* = Transcoder(
|
||||||
|
stringToBuffer: ip4StB,
|
||||||
|
bufferToString: ip4BtS,
|
||||||
|
validateBuffer: ip4VB
|
||||||
|
)
|
||||||
|
TranscoderIP6* = Transcoder(
|
||||||
|
stringToBuffer: ip6StB,
|
||||||
|
bufferToString: ip6BtS,
|
||||||
|
validateBuffer: ip6VB
|
||||||
|
)
|
||||||
|
TranscoderIP6Zone* = Transcoder(
|
||||||
|
stringToBuffer: ip6zoneStB,
|
||||||
|
bufferToString: ip6zoneBtS,
|
||||||
|
validateBuffer: ip6zoneVB
|
||||||
|
)
|
||||||
|
TranscoderUnix* = Transcoder(
|
||||||
|
stringToBuffer: unixStB,
|
||||||
|
bufferToString: unixBtS,
|
||||||
|
validateBuffer: unixVB
|
||||||
|
)
|
||||||
|
TranscoderP2P* = Transcoder(
|
||||||
|
stringToBuffer: p2pStB,
|
||||||
|
bufferToString: p2pBtS,
|
||||||
|
validateBuffer: p2pVB
|
||||||
|
)
|
||||||
|
TranscoderPort* = Transcoder(
|
||||||
|
stringToBuffer: portStB,
|
||||||
|
bufferToString: portBtS,
|
||||||
|
validateBuffer: portVB
|
||||||
|
)
|
||||||
|
TranscoderOnion* = Transcoder(
|
||||||
|
stringToBuffer: onionStB,
|
||||||
|
bufferToString: onionBtS,
|
||||||
|
validateBuffer: onionVB
|
||||||
|
)
|
||||||
|
TranscoderDNS* = Transcoder(
|
||||||
|
stringToBuffer: dnsStB,
|
||||||
|
bufferToString: dnsBtS,
|
||||||
|
validateBuffer: dnsVB
|
||||||
|
)
|
||||||
|
ProtocolsList = [
|
||||||
|
MAProtocol(
|
||||||
|
name: "ip4", code: P_IP4, kind: Fixed, size: 4,
|
||||||
|
coder: TranscoderIP4
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "tcp", code: P_TCP, kind: Fixed, size: 2,
|
||||||
|
coder: TranscoderPort
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "udp", code: P_UDP, kind: Fixed, size: 2,
|
||||||
|
coder: TranscoderPort
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "ip6", code: P_IP6, kind: Fixed, size: 16,
|
||||||
|
coder: TranscoderIP6
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "dccp", code: P_DCCP, kind: Fixed, size: 2,
|
||||||
|
coder: TranscoderPort
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "sctp", code: P_SCTP, kind: Fixed, size: 2,
|
||||||
|
coder: TranscoderPort
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "udt", code: P_UDT, kind: Marker, size: 0
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "utp", code: P_UTP, kind: Marker, size: 0
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "http", code: P_HTTP, kind: Marker, size: 0
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "https", code: P_HTTPS, kind: Marker, size: 0
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "quic", code: P_QUIC, kind: Marker, size: 0
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "ip6zone", code: P_IP6ZONE, kind: Length, size: 0,
|
||||||
|
coder: TranscoderIP6Zone
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "onion", code: P_ONION, kind: Fixed, size: 10,
|
||||||
|
coder: TranscoderOnion
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "ws", code: P_WS, kind: Marker, size: 0
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "ws", code: P_WS, kind: Marker, size: 0
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "ipfs", code: P_IPFS, kind: Length, size: 0,
|
||||||
|
coder: TranscoderP2P
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "p2p", code: P_P2P, kind: Length, size: 0,
|
||||||
|
coder: TranscoderP2P
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "unix", code: P_UNIX, kind: Path, size: 0,
|
||||||
|
coder: TranscoderUnix
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "dns4", code: P_DNS4, kind: Length, size: 0,
|
||||||
|
coder: TranscoderDNS
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "dns6", code: P_DNS6, kind: Length, size: 0,
|
||||||
|
coder: TranscoderDNS
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "dnsaddr", code: P_DNS6, kind: Length, size: 0,
|
||||||
|
coder: TranscoderDNS
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "p2p-circuit", code: P_P2PCIRCUIT, kind: Marker, size: 0
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "p2p-websocket-star", code: LP2P_WSSTAR, kind: Marker, size: 0
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "p2p-webrtc-star", code: LP2P_WRTCSTAR, kind: Marker, size: 0
|
||||||
|
),
|
||||||
|
MAProtocol(
|
||||||
|
name: "p2p-webrtc-direct", code: LP2P_WRTCDIR, kind: Marker, size: 0
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
proc initMultiAddressNameTable(): Table[string, MAProtocol] {.compileTime.} =
|
||||||
|
result = initTable[string, MAProtocol]()
|
||||||
|
for item in ProtocolsList:
|
||||||
|
result[item.name] = item
|
||||||
|
|
||||||
|
proc initMultiAddressCodeTable(): Table[int, MAProtocol] {.compileTime.} =
|
||||||
|
result = initTable[int, MAProtocol]()
|
||||||
|
for item in ProtocolsList:
|
||||||
|
result[item.code] = item
|
||||||
|
|
||||||
|
const
|
||||||
|
CodeAddresses = initMultiAddressCodeTable()
|
||||||
|
NameAddresses = initMultiAddressNameTable()
|
||||||
|
|
||||||
|
proc trimRight(s: string, ch: char): string =
|
||||||
|
## Consume trailing characters ``ch`` from string ``s`` and return result.
|
||||||
|
var m = 0
|
||||||
|
for i in countdown(len(s) - 1, 0):
|
||||||
|
if s[i] == ch:
|
||||||
|
inc(m)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
result = s[0..(len(s) - 1 - m)]
|
||||||
|
|
||||||
|
proc protoCode*(mtype: typedesc[MultiAddress], protocol: string): int =
|
||||||
|
## Returns protocol code from protocol name ``protocol``.
|
||||||
|
let proto = NameAddresses.getOrDefault(protocol)
|
||||||
|
if proto.kind == None:
|
||||||
|
raise newException(MultiAddressError, "Protocol not found")
|
||||||
|
result = proto.code
|
||||||
|
|
||||||
|
proc protoName*(mtype: typedesc[MultiAddress], protocol: int): string =
|
||||||
|
## Returns protocol name from protocol code ``protocol``.
|
||||||
|
let proto = CodeAddresses.getOrDefault(protocol)
|
||||||
|
if proto.kind == None:
|
||||||
|
raise newException(MultiAddressError, "Protocol not found")
|
||||||
|
result = proto.name
|
||||||
|
|
||||||
|
proc protoCode*(ma: MultiAddress): int =
|
||||||
|
## Returns MultiAddress ``ma`` protocol code.
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc protoName*(ma: MultiAddress): string =
|
||||||
|
## Returns MultiAddress ``ma`` protocol name.
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc protoValue*(ma: MultiAddress, value: var openarray[byte]): int =
|
||||||
|
## Returns MultiAddress ``ma`` protocol address value.
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc getPart(ma: MultiAddress, index: int): MultiAddress =
|
||||||
|
var header: uint64
|
||||||
|
var data = newSeq[byte]()
|
||||||
|
var offset = 0
|
||||||
|
var vb = ma
|
||||||
|
result.data = initVBuffer()
|
||||||
|
while offset <= index:
|
||||||
|
if vb.data.readVarint(header) == -1:
|
||||||
|
raise newException(MultiAddressError, "Malformed binary address!")
|
||||||
|
let proto = CodeAddresses.getOrDefault(int(header))
|
||||||
|
if proto.kind == None:
|
||||||
|
raise newException(MultiAddressError,
|
||||||
|
"Unsupported protocol '" & $header & "'")
|
||||||
|
elif proto.kind == Fixed:
|
||||||
|
data.setLen(proto.size)
|
||||||
|
if vb.data.readArray(data) != proto.size:
|
||||||
|
raise newException(MultiAddressError, "Decoding protocol error")
|
||||||
|
if offset == index:
|
||||||
|
result.data.writeVarint(header)
|
||||||
|
result.data.writeArray(data)
|
||||||
|
result.data.finish()
|
||||||
|
elif proto.kind in {Length, Path}:
|
||||||
|
if vb.data.readSeq(data) == -1:
|
||||||
|
raise newException(MultiAddressError, "Decoding protocol error")
|
||||||
|
if offset == index:
|
||||||
|
result.data.writeVarint(header)
|
||||||
|
result.data.writeSeq(data)
|
||||||
|
result.data.finish()
|
||||||
|
elif proto.kind == Marker:
|
||||||
|
if offset == index:
|
||||||
|
result.data.writeVarint(header)
|
||||||
|
result.data.finish()
|
||||||
|
inc(offset)
|
||||||
|
|
||||||
|
proc `[]`*(ma: MultiAddress, i: int): MultiAddress {.inline.} =
|
||||||
|
result = ma.getPart(i)
|
||||||
|
|
||||||
|
iterator items*(ma: MultiAddress): MultiAddress =
|
||||||
|
## Iterates over all addresses inside of MultiAddress ``ma``.
|
||||||
|
var header: uint64
|
||||||
|
var data = newSeq[byte]()
|
||||||
|
var vb = ma
|
||||||
|
while true:
|
||||||
|
if vb.data.isEmpty():
|
||||||
|
break
|
||||||
|
var res = MultiAddress(data: initVBuffer())
|
||||||
|
if vb.data.readVarint(header) == -1:
|
||||||
|
raise newException(MultiAddressError, "Malformed binary address!")
|
||||||
|
let proto = CodeAddresses.getOrDefault(int(header))
|
||||||
|
if proto.kind == None:
|
||||||
|
raise newException(MultiAddressError,
|
||||||
|
"Unsupported protocol '" & $header & "'")
|
||||||
|
elif proto.kind == Fixed:
|
||||||
|
data.setLen(proto.size)
|
||||||
|
if vb.data.readArray(data) != proto.size:
|
||||||
|
raise newException(MultiAddressError, "Decoding protocol error")
|
||||||
|
res.data.writeVarint(header)
|
||||||
|
res.data.writeArray(data)
|
||||||
|
elif proto.kind in {Length, Path}:
|
||||||
|
if vb.data.readSeq(data) == -1:
|
||||||
|
raise newException(MultiAddressError, "Decoding protocol error")
|
||||||
|
res.data.writeVarint(header)
|
||||||
|
res.data.writeSeq(data)
|
||||||
|
elif proto.kind == Marker:
|
||||||
|
res.data.writeVarint(header)
|
||||||
|
res.data.finish()
|
||||||
|
yield res
|
||||||
|
|
||||||
|
proc `$`*(value: MultiAddress): string =
|
||||||
|
## Return string representation of MultiAddress ``value``.
|
||||||
|
var header: uint64
|
||||||
|
var vb = value
|
||||||
|
var data = newSeq[byte]()
|
||||||
|
var parts = newSeq[string]()
|
||||||
|
var part: string
|
||||||
|
while true:
|
||||||
|
if vb.data.isEmpty():
|
||||||
|
break
|
||||||
|
if vb.data.readVarint(header) == -1:
|
||||||
|
raise newException(MultiAddressError, "Malformed binary address!")
|
||||||
|
let proto = CodeAddresses.getOrDefault(int(header))
|
||||||
|
if proto.kind == None:
|
||||||
|
raise newException(MultiAddressError,
|
||||||
|
"Unsupported protocol '" & $header & "'")
|
||||||
|
if proto.kind in {Fixed, Length, Path}:
|
||||||
|
if isNil(proto.coder.bufferToString):
|
||||||
|
raise newException(MultiAddressError,
|
||||||
|
"Missing protocol '" & proto.name & "' transcoder")
|
||||||
|
if not proto.coder.bufferToString(vb.data, part):
|
||||||
|
raise newException(MultiAddressError, "Decoding protocol error")
|
||||||
|
parts.add(proto.name)
|
||||||
|
parts.add(part)
|
||||||
|
elif proto.kind == Marker:
|
||||||
|
parts.add(proto.name)
|
||||||
|
if len(parts) > 0:
|
||||||
|
result = "/" & parts.join("/")
|
||||||
|
|
||||||
|
proc hex*(value: MultiAddress): string =
|
||||||
|
## Return hexadecimal string representation of MultiAddress ``value``.
|
||||||
|
result = $(value.data)
|
||||||
|
|
||||||
|
proc buffer*(value: MultiAddress): seq[byte] =
|
||||||
|
## Returns shallow copy of internal buffer
|
||||||
|
shallowCopy(result, value.data.buffer)
|
||||||
|
|
||||||
|
proc validate*(ma: MultiAddress): bool =
|
||||||
|
## Returns ``true`` if MultiAddress ``ma`` is valid.
|
||||||
|
var header: uint64
|
||||||
|
var vb = ma
|
||||||
|
while true:
|
||||||
|
if vb.data.isEmpty():
|
||||||
|
break
|
||||||
|
if vb.data.readVarint(header) == -1:
|
||||||
|
return false
|
||||||
|
let proto = CodeAddresses.getOrDefault(int(header))
|
||||||
|
if proto.kind == None:
|
||||||
|
return false
|
||||||
|
if proto.kind in {Fixed, Length, Path}:
|
||||||
|
if isNil(proto.coder.validateBuffer):
|
||||||
|
return false
|
||||||
|
if not proto.coder.validateBuffer(vb.data):
|
||||||
|
return false
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc init*(mtype: typedesc[MultiAddress], protocol: int,
|
||||||
|
value: openarray[byte]): MultiAddress =
|
||||||
|
## Initialize MultiAddress object from protocol id ``protocol`` and array
|
||||||
|
## of bytes ``value``.
|
||||||
|
let proto = CodeAddresses.getOrDefault(protocol)
|
||||||
|
if proto.kind == None:
|
||||||
|
raise newException(MultiAddressError, "Protocol not found")
|
||||||
|
result.data = initVBuffer()
|
||||||
|
result.data.writeVarint(cast[uint64](proto.code))
|
||||||
|
if proto.kind in {Fixed, Length, Path}:
|
||||||
|
if len(value) == 0:
|
||||||
|
raise newException(MultiAddressError, "Value must not be empty array")
|
||||||
|
if proto.kind == Fixed:
|
||||||
|
result.data.writeArray(value)
|
||||||
|
else:
|
||||||
|
var data = newSeq[byte](len(value))
|
||||||
|
copyMem(addr data[0], unsafeAddr value[0], len(value))
|
||||||
|
result.data.writeSeq(data)
|
||||||
|
result.data.finish()
|
||||||
|
|
||||||
|
proc init*(mtype: typedesc[MultiAddress], protocol: int): MultiAddress =
|
||||||
|
## Initialize MultiAddress object from protocol id ``protocol``.
|
||||||
|
let proto = CodeAddresses.getOrDefault(protocol)
|
||||||
|
if proto.kind == None:
|
||||||
|
raise newException(MultiAddressError, "Protocol not found")
|
||||||
|
result.data = initVBuffer()
|
||||||
|
if proto.kind != Marker:
|
||||||
|
raise newException(MultiAddressError, "Protocol missing value")
|
||||||
|
result.data.writeVarint(cast[uint64](proto.code))
|
||||||
|
result.data.finish()
|
||||||
|
|
||||||
|
proc init*(mtype: typedesc[MultiAddress], value: string): MultiAddress =
|
||||||
|
## Initialize MultiAddress object from string representation ``value``.
|
||||||
|
var parts = value.trimRight('/').split('/')
|
||||||
|
if len(parts[0]) != 0:
|
||||||
|
raise newException(MultiAddressError,
|
||||||
|
"Invalid MultiAddress, must start with `/`")
|
||||||
|
var offset = 1
|
||||||
|
result.data = initVBuffer()
|
||||||
|
while offset < len(parts):
|
||||||
|
let part = parts[offset]
|
||||||
|
let proto = NameAddresses.getOrDefault(part)
|
||||||
|
if proto.kind == None:
|
||||||
|
raise newException(MultiAddressError,
|
||||||
|
"Unsupported protocol '" & part & "'")
|
||||||
|
if proto.kind in {Fixed, Length, Path}:
|
||||||
|
if isNil(proto.coder.stringToBuffer):
|
||||||
|
raise newException(MultiAddressError,
|
||||||
|
"Missing protocol '" & part & "' transcoder")
|
||||||
|
if offset + 1 >= len(parts):
|
||||||
|
raise newException(MultiAddressError,
|
||||||
|
"Missing protocol '" & part & "' argument")
|
||||||
|
|
||||||
|
if proto.kind in {Fixed, Length}:
|
||||||
|
result.data.writeVarint(cast[uint](proto.code))
|
||||||
|
if not proto.coder.stringToBuffer(parts[offset + 1], result.data):
|
||||||
|
raise newException(MultiAddressError,
|
||||||
|
"Error encoding `$1/$2`" % [part, parts[offset + 1]])
|
||||||
|
offset += 2
|
||||||
|
elif proto.kind == Path:
|
||||||
|
var path = "/" & (parts[(offset + 1)..^1].join("/"))
|
||||||
|
result.data.writeVarint(cast[uint](proto.code))
|
||||||
|
if not proto.coder.stringToBuffer(path, result.data):
|
||||||
|
raise newException(MultiAddressError,
|
||||||
|
"Error encoding `$1/$2`" % [part, path])
|
||||||
|
break
|
||||||
|
elif proto.kind == Marker:
|
||||||
|
result.data.writeVarint(cast[uint](proto.code))
|
||||||
|
offset += 1
|
||||||
|
result.data.finish()
|
||||||
|
|
||||||
|
proc init*(mtype: typedesc[MultiAddress], data: openarray[byte]): MultiAddress =
|
||||||
|
## Initialize MultiAddress with array of bytes ``data``.
|
||||||
|
if len(data) == 0:
|
||||||
|
raise newException(MultiAddressError, "Address could not be empty!")
|
||||||
|
result.data = initVBuffer()
|
||||||
|
result.data.buffer.setLen(len(data))
|
||||||
|
copyMem(addr result.data.buffer[0], unsafeAddr data[0], len(data))
|
||||||
|
if not result.validate():
|
||||||
|
raise newException(MultiAddressError, "Incorrect MultiAddress!")
|
||||||
|
|
||||||
|
proc init*(mtype: typedesc[MultiAddress]): MultiAddress =
|
||||||
|
## Initialize empty MultiAddress.
|
||||||
|
result.data = initVBuffer()
|
||||||
|
|
||||||
|
proc isEmpty*(ma: MultiAddress): bool =
|
||||||
|
## Returns ``true``, if MultiAddress ``ma`` is empty or non initialized.
|
||||||
|
result = len(ma.data) == 0
|
||||||
|
|
||||||
|
proc `&`*(m1, m2: MultiAddress): MultiAddress =
|
||||||
|
## Concatenates two addresses ``m1`` and ``m2``, and returns result.
|
||||||
|
##
|
||||||
|
## This procedure performs validation of concatenated result and can raise
|
||||||
|
## exception on error.
|
||||||
|
result.data = initVBuffer()
|
||||||
|
result.data.buffer = m1.data.buffer & m2.data.buffer
|
||||||
|
if not result.validate():
|
||||||
|
raise newException(MultiAddressError, "Incorrect MultiAddress!")
|
||||||
|
|
||||||
|
proc `&=`*(m1: var MultiAddress, m2: MultiAddress) =
|
||||||
|
## Concatenates two addresses ``m1`` and ``m2``.
|
||||||
|
##
|
||||||
|
## This procedure performs validation of concatenated result and can raise
|
||||||
|
## exception on error.
|
||||||
|
m1.data.buffer &= m2.data.buffer
|
||||||
|
if not m1.validate():
|
||||||
|
raise newException(MultiAddressError, "Incorrect MultiAddress!")
|
|
@ -0,0 +1,17 @@
|
||||||
|
## Nim-Libp2p
|
||||||
|
## Copyright (c) 2018 Status Research & Development GmbH
|
||||||
|
## Licensed under either of
|
||||||
|
## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
||||||
|
## * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||||
|
## at your option.
|
||||||
|
## This file may not be copied, modified, or distributed except according to
|
||||||
|
## those terms.
|
||||||
|
|
||||||
|
## This module implements transcoder interface.
|
||||||
|
import vbuffer
|
||||||
|
|
||||||
|
type
|
||||||
|
Transcoder* = object
|
||||||
|
stringToBuffer*: proc(s: string, vb: var VBuffer): bool {.nimcall.}
|
||||||
|
bufferToString*: proc(vb: var VBuffer, s: var string): bool {.nimcall.}
|
||||||
|
validateBuffer*: proc(vb: var VBuffer): bool {.nimcall.}
|
|
@ -0,0 +1,180 @@
|
||||||
|
## Nim-Libp2p
|
||||||
|
## Copyright (c) 2018 Status Research & Development GmbH
|
||||||
|
## Licensed under either of
|
||||||
|
## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
||||||
|
## * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||||
|
## at your option.
|
||||||
|
## This file may not be copied, modified, or distributed except according to
|
||||||
|
## those terms.
|
||||||
|
|
||||||
|
## This module implements variable buffer.
|
||||||
|
import varint, strutils
|
||||||
|
|
||||||
|
type
|
||||||
|
VBuffer* = object
|
||||||
|
buffer*: seq[byte]
|
||||||
|
offset*: int
|
||||||
|
length*: int
|
||||||
|
|
||||||
|
template isEmpty*(vb: VBuffer): bool =
|
||||||
|
## Returns ``true`` if buffer ``vb`` is empty.
|
||||||
|
len(vb.buffer) - vb.offset <= 0
|
||||||
|
|
||||||
|
template isEnough*(vb: VBuffer, length: int): bool =
|
||||||
|
## Returns ``true`` if buffer ``vb`` holds at least ``length`` bytes.
|
||||||
|
len(vb.buffer) - vb.offset - length >= 0
|
||||||
|
|
||||||
|
proc len*(vb: VBuffer): int =
|
||||||
|
## Returns number of bytes left in buffer ``vb``.
|
||||||
|
result = len(vb.buffer) - vb.offset
|
||||||
|
|
||||||
|
proc isLiteral[T](s: seq[T]): bool {.inline.} =
|
||||||
|
type
|
||||||
|
SeqHeader = object
|
||||||
|
length, reserved: int
|
||||||
|
(cast[ptr SeqHeader](s).reserved and (1 shl (sizeof(int) * 8 - 2))) != 0
|
||||||
|
|
||||||
|
proc initVBuffer*(data: seq[byte], offset = 0): VBuffer =
|
||||||
|
## Initialize VBuffer with shallow copy of ``data``.
|
||||||
|
if isLiteral(data):
|
||||||
|
result.buffer = data
|
||||||
|
else:
|
||||||
|
shallowCopy(result.buffer, data)
|
||||||
|
result.offset = offset
|
||||||
|
|
||||||
|
proc initVBuffer*(data: openarray[byte], offset = 0): VBuffer =
|
||||||
|
## Initialize VBuffer with copy of ``data``.
|
||||||
|
result.buffer = newSeq[byte](len(data))
|
||||||
|
if len(data) > 0:
|
||||||
|
copyMem(addr result.buffer[0], unsafeAddr data[0], len(data))
|
||||||
|
result.offset = offset
|
||||||
|
|
||||||
|
proc initVBuffer*(): VBuffer =
|
||||||
|
## Initialize empty VBuffer.
|
||||||
|
result.buffer = newSeqOfCap[byte](128)
|
||||||
|
|
||||||
|
proc writeVarint*(vb: var VBuffer, value: LPSomeUVarint) =
|
||||||
|
## Write ``value`` as variable unsigned integer.
|
||||||
|
var length = 0
|
||||||
|
when sizeof(value) == 8:
|
||||||
|
# LibP2P varint supports only 63 bits.
|
||||||
|
var v = value and cast[type(value)](0x7FFF_FFFF_FFFF_FFFF)
|
||||||
|
else:
|
||||||
|
var v = uint64(v)
|
||||||
|
vb.buffer.setLen(len(vb.buffer) + vsizeof(v))
|
||||||
|
let res = LP.putUVarint(toOpenArray(vb.buffer, vb.offset, len(vb.buffer) - 1),
|
||||||
|
length, v)
|
||||||
|
assert(res == VarintStatus.Success)
|
||||||
|
vb.offset += length
|
||||||
|
|
||||||
|
proc writeSeq*[T: byte|char](vb: var VBuffer, value: openarray[T]) =
|
||||||
|
## Write array ``value`` to buffer ``vb``, value will be prefixed with
|
||||||
|
## varint length of the array.
|
||||||
|
var length = 0
|
||||||
|
vb.buffer.setLen(len(vb.buffer) + vsizeof(len(value)) + len(value))
|
||||||
|
let res = LP.putUVarint(toOpenArray(vb.buffer, vb.offset, len(vb.buffer) - 1),
|
||||||
|
length, uint(len(value)))
|
||||||
|
assert(res == VarintStatus.Success)
|
||||||
|
vb.offset += length
|
||||||
|
if len(value) > 0:
|
||||||
|
copyMem(addr vb.buffer[vb.offset], unsafeAddr value[0], len(value))
|
||||||
|
vb.offset += len(value)
|
||||||
|
|
||||||
|
proc writeArray*[T: byte|char](vb: var VBuffer, value: openarray[T]) =
|
||||||
|
## Write array ``value`` to buffer ``vb``, value will NOT be prefixed with
|
||||||
|
## varint length of the array.
|
||||||
|
var length = 0
|
||||||
|
if len(value) > 0:
|
||||||
|
vb.buffer.setLen(len(vb.buffer) + len(value))
|
||||||
|
copyMem(addr vb.buffer[vb.offset], unsafeAddr value[0], len(value))
|
||||||
|
vb.offset += len(value)
|
||||||
|
|
||||||
|
proc finish*(vb: var VBuffer) =
|
||||||
|
## Finishes ``vb``.
|
||||||
|
vb.offset = 0
|
||||||
|
|
||||||
|
proc peekVarint*(vb: var VBuffer, value: var LPSomeUVarint): int =
|
||||||
|
## Peek unsigned integer from buffer ``vb`` and store result to ``value``.
|
||||||
|
##
|
||||||
|
## This procedure will not adjust internal offset.
|
||||||
|
##
|
||||||
|
## Returns number of bytes peeked from ``vb`` or ``-1`` on error.
|
||||||
|
result = -1
|
||||||
|
value = cast[type(value)](0)
|
||||||
|
var length = 0
|
||||||
|
if not vb.isEmpty():
|
||||||
|
let res = LP.getUVarint(
|
||||||
|
toOpenArray(vb.buffer, vb.offset, len(vb.buffer) - 1), length, value)
|
||||||
|
if res == VarintStatus.Success:
|
||||||
|
result = length
|
||||||
|
|
||||||
|
proc peekSeq*[T: string|seq[byte]](vb: var VBuffer, value: var T): int =
|
||||||
|
## Peek length prefixed array from buffer ``vb`` and store result to
|
||||||
|
## ``value``.
|
||||||
|
##
|
||||||
|
## This procedure will not adjust internal offset.
|
||||||
|
##
|
||||||
|
## Returns number of bytes peeked from ``vb`` or ``-1`` on error.
|
||||||
|
result = -1
|
||||||
|
value.setLen(0)
|
||||||
|
var length = 0
|
||||||
|
var size = 0'u64
|
||||||
|
if not vb.isEmpty() and
|
||||||
|
LP.getUVarint(toOpenArray(vb.buffer, vb.offset, len(vb.buffer) - 1),
|
||||||
|
length, size) == VarintStatus.Success:
|
||||||
|
vb.offset += length
|
||||||
|
result = length
|
||||||
|
if vb.isEnough(int(size)):
|
||||||
|
value.setLen(size)
|
||||||
|
if size > 0'u64:
|
||||||
|
copyMem(addr value[0], addr vb.buffer[vb.offset], size)
|
||||||
|
result += int(size)
|
||||||
|
vb.offset -= length
|
||||||
|
|
||||||
|
proc peekArray*[T: char|byte](vb: var VBuffer,
|
||||||
|
value: var openarray[T]): int =
|
||||||
|
## Peek array from buffer ``vb`` and store result to ``value``.
|
||||||
|
##
|
||||||
|
## This procedure will not adjust internal offset.
|
||||||
|
##
|
||||||
|
## Returns number of bytes peeked from ``vb`` or ``-1`` on error.
|
||||||
|
result = -1
|
||||||
|
let length = len(value)
|
||||||
|
if vb.isEnough(length):
|
||||||
|
if length > 0:
|
||||||
|
copyMem(addr value[0], addr vb.buffer[vb.offset], length)
|
||||||
|
result = length
|
||||||
|
|
||||||
|
proc readVarint*(vb: var VBuffer, value: var LPSomeUVarint): int {.inline.} =
|
||||||
|
## Read unsigned integer from buffer ``vb`` and store result to ``value``.
|
||||||
|
##
|
||||||
|
## Returns number of bytes consumed from ``vb`` or ``-1`` on error.
|
||||||
|
result = vb.peekVarint(value)
|
||||||
|
if result != -1:
|
||||||
|
vb.offset += result
|
||||||
|
|
||||||
|
proc readSeq*[T: string|seq[byte]](vb: var VBuffer,
|
||||||
|
value: var T): int {.inline.} =
|
||||||
|
## Read length prefixed array from buffer ``vb`` and store result to
|
||||||
|
## ``value``.
|
||||||
|
##
|
||||||
|
## Returns number of bytes consumed from ``vb`` or ``-1`` on error.
|
||||||
|
result = vb.peekSeq(value)
|
||||||
|
if result != -1:
|
||||||
|
vb.offset += result
|
||||||
|
|
||||||
|
proc readArray*[T: char|byte](vb: var VBuffer,
|
||||||
|
value: var openarray[T]): int {.inline.} =
|
||||||
|
## Read array from buffer ``vb`` and store result to ``value``.
|
||||||
|
##
|
||||||
|
## Returns number of bytes consumed from ``vb`` or ``-1`` on error.
|
||||||
|
result = vb.peekArray(value)
|
||||||
|
if result != -1:
|
||||||
|
vb.offset += result
|
||||||
|
|
||||||
|
proc `$`*(vb: VBuffer): string =
|
||||||
|
## Return hexadecimal string representation of buffer ``vb``.
|
||||||
|
let length = (len(vb.buffer) - vb.offset) * 2
|
||||||
|
result = newStringOfCap(length)
|
||||||
|
for i in 0..<len(vb.buffer):
|
||||||
|
result.add(toHex(vb.buffer[i]))
|
|
@ -0,0 +1,218 @@
|
||||||
|
import unittest
|
||||||
|
import ../libp2p/multiaddress
|
||||||
|
|
||||||
|
const
|
||||||
|
SuccessVectors = [
|
||||||
|
"/ip4/1.2.3.4",
|
||||||
|
"/ip4/0.0.0.0",
|
||||||
|
"/ip6/::1",
|
||||||
|
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21",
|
||||||
|
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21/udp/1234/quic",
|
||||||
|
"/ip6zone/x/ip6/fe80::1",
|
||||||
|
"/ip6zone/x%y/ip6/fe80::1",
|
||||||
|
"/ip6zone/x%y/ip6/::",
|
||||||
|
"/ip6zone/x/ip6/fe80::1/udp/1234/quic",
|
||||||
|
# "/onion/timaq4ygg2iegci7:1234",
|
||||||
|
# "/onion/timaq4ygg2iegci7:80/http",
|
||||||
|
"/udp/0",
|
||||||
|
"/tcp/0",
|
||||||
|
"/sctp/0",
|
||||||
|
"/udp/1234",
|
||||||
|
"/tcp/1234",
|
||||||
|
"/sctp/1234",
|
||||||
|
"/udp/65535",
|
||||||
|
"/tcp/65535",
|
||||||
|
"/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
|
||||||
|
"/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
|
||||||
|
"/udp/1234/sctp/1234",
|
||||||
|
"/udp/1234/udt",
|
||||||
|
"/udp/1234/utp",
|
||||||
|
"/tcp/1234/http",
|
||||||
|
"/tcp/1234/https",
|
||||||
|
"/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234",
|
||||||
|
"/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234",
|
||||||
|
"/ip4/127.0.0.1/udp/1234",
|
||||||
|
"/ip4/127.0.0.1/udp/0",
|
||||||
|
"/ip4/127.0.0.1/tcp/1234",
|
||||||
|
"/ip4/127.0.0.1/tcp/1234/",
|
||||||
|
"/ip4/127.0.0.1/udp/1234/quic",
|
||||||
|
"/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
|
||||||
|
"/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234",
|
||||||
|
"/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
|
||||||
|
"/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234",
|
||||||
|
"/unix/a/b/c/d/e",
|
||||||
|
"/unix/stdio",
|
||||||
|
"/ip4/1.2.3.4/tcp/80/unix/a/b/c/d/e/f",
|
||||||
|
"/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234/unix/stdio",
|
||||||
|
"/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234/unix/stdio",
|
||||||
|
]
|
||||||
|
|
||||||
|
FailureVectors = [
|
||||||
|
"/ip4",
|
||||||
|
"/ip4/::1",
|
||||||
|
"/ip4/fdpsofodsajfdoisa",
|
||||||
|
"/ip6",
|
||||||
|
"/ip6zone",
|
||||||
|
"/ip6zone/",
|
||||||
|
"/ip6zone//ip6/fe80::1",
|
||||||
|
"/udp",
|
||||||
|
"/tcp",
|
||||||
|
"/sctp",
|
||||||
|
"/udp/65536",
|
||||||
|
"/tcp/65536",
|
||||||
|
"/quic/65536",
|
||||||
|
# "/onion/9imaq4ygg2iegci7:80", # TODO: Requires ONION validate
|
||||||
|
# "/onion/aaimaq4ygg2iegci7:80",
|
||||||
|
# "/onion/timaq4ygg2iegci7:0",
|
||||||
|
# "/onion/timaq4ygg2iegci7:-1",
|
||||||
|
# "/onion/timaq4ygg2iegci7",
|
||||||
|
# "/onion/timaq4ygg2iegci@:666",
|
||||||
|
"/udp/1234/sctp",
|
||||||
|
"/udp/1234/udt/1234",
|
||||||
|
"/udp/1234/utp/1234",
|
||||||
|
"/ip4/127.0.0.1/udp/jfodsajfidosajfoidsa",
|
||||||
|
"/ip4/127.0.0.1/udp",
|
||||||
|
"/ip4/127.0.0.1/tcp/jfodsajfidosajfoidsa",
|
||||||
|
"/ip4/127.0.0.1/tcp",
|
||||||
|
"/ip4/127.0.0.1/quic/1234",
|
||||||
|
"/ip4/127.0.0.1/ipfs",
|
||||||
|
# "/ip4/127.0.0.1/ipfs/tcp", # TODO: Requires P2P/IPFS validate
|
||||||
|
"/ip4/127.0.0.1/p2p",
|
||||||
|
# "/ip4/127.0.0.1/p2p/tcp", # TODO: Requires P2P/IPFS validate
|
||||||
|
"/unix"
|
||||||
|
]
|
||||||
|
|
||||||
|
RustSuccessVectors = [
|
||||||
|
"/ip4/1.2.3.4",
|
||||||
|
"/ip4/0.0.0.0",
|
||||||
|
"/ip6/::1",
|
||||||
|
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21",
|
||||||
|
"/udp/0",
|
||||||
|
"/tcp/0",
|
||||||
|
"/sctp/0",
|
||||||
|
"/udp/1234",
|
||||||
|
"/tcp/1234",
|
||||||
|
"/sctp/1234",
|
||||||
|
"/udp/65535",
|
||||||
|
"/tcp/65535",
|
||||||
|
"/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
|
||||||
|
"/udp/1234/sctp/1234",
|
||||||
|
"/udp/1234/udt",
|
||||||
|
"/udp/1234/utp",
|
||||||
|
"/tcp/1234/http",
|
||||||
|
"/tcp/1234/https",
|
||||||
|
"/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234",
|
||||||
|
"/ip4/127.0.0.1/udp/1234",
|
||||||
|
"/ip4/127.0.0.1/udp/0",
|
||||||
|
"/ip4/127.0.0.1/tcp/1234",
|
||||||
|
"/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
|
||||||
|
"/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234",
|
||||||
|
"/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/8000/ws/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
|
||||||
|
"/p2p-webrtc-star/ip4/127.0.0.1/tcp/9090/ws/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
|
||||||
|
"/ip4/127.0.0.1/tcp/9090/p2p-circuit/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"
|
||||||
|
]
|
||||||
|
|
||||||
|
RustSuccessExpects = [
|
||||||
|
"0401020304",
|
||||||
|
"0400000000",
|
||||||
|
"2900000000000000000000000000000001",
|
||||||
|
"29260100094F819700803ECA6566E80C21",
|
||||||
|
"91020000",
|
||||||
|
"060000",
|
||||||
|
"84010000",
|
||||||
|
"910204D2",
|
||||||
|
"0604D2",
|
||||||
|
"840104D2",
|
||||||
|
"9102FFFF",
|
||||||
|
"06FFFF",
|
||||||
|
"A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
|
||||||
|
"910204D2840104D2",
|
||||||
|
"910204D2AD02",
|
||||||
|
"910204D2AE02",
|
||||||
|
"0604D2E003",
|
||||||
|
"0604D2BB03",
|
||||||
|
"A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B0604D2",
|
||||||
|
"047F000001910204D2",
|
||||||
|
"047F00000191020000",
|
||||||
|
"047F0000010604D2",
|
||||||
|
"047F000001A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
|
||||||
|
"047F000001A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B0604D2",
|
||||||
|
"29200108A07AC542013AC986FFFE317095061F40DD03A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
|
||||||
|
"9302047F000001062382DD03A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
|
||||||
|
"047F000001062382A202A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B"
|
||||||
|
]
|
||||||
|
|
||||||
|
RustFailureVectors = [
|
||||||
|
"/ip4",
|
||||||
|
"/ip4/::1",
|
||||||
|
"/ip4/fdpsofodsajfdoisa",
|
||||||
|
"/ip6",
|
||||||
|
"/udp",
|
||||||
|
"/tcp",
|
||||||
|
"/sctp",
|
||||||
|
"/udp/65536",
|
||||||
|
"/tcp/65536",
|
||||||
|
# "/onion/9imaq4ygg2iegci7:80", # TODO: Requires ONION validate
|
||||||
|
# "/onion/aaimaq4ygg2iegci7:80",
|
||||||
|
# "/onion/timaq4ygg2iegci7:0",
|
||||||
|
# "/onion/timaq4ygg2iegci7:-1",
|
||||||
|
# "/onion/timaq4ygg2iegci7",
|
||||||
|
# "/onion/timaq4ygg2iegci@:666",
|
||||||
|
"/udp/1234/sctp",
|
||||||
|
"/udp/1234/udt/1234",
|
||||||
|
"/udp/1234/utp/1234",
|
||||||
|
"/ip4/127.0.0.1/udp/jfodsajfidosajfoidsa",
|
||||||
|
"/ip4/127.0.0.1/udp",
|
||||||
|
"/ip4/127.0.0.1/tcp/jfodsajfidosajfoidsa",
|
||||||
|
"/ip4/127.0.0.1/tcp",
|
||||||
|
"/ip4/127.0.0.1/ipfs",
|
||||||
|
# "/ip4/127.0.0.1/ipfs/tcp", # TODO: Requires P2P/IPFS validate
|
||||||
|
"/p2p-circuit/50"
|
||||||
|
]
|
||||||
|
|
||||||
|
suite "MultiAddress test suite":
|
||||||
|
|
||||||
|
test "go-multiaddr success test vectors":
|
||||||
|
for item in SuccessVectors:
|
||||||
|
var a = MultiAddress.init(item)
|
||||||
|
check a.isEmpty() == false
|
||||||
|
check a.validate() == true
|
||||||
|
|
||||||
|
test "go-multiaddr failure test vectors":
|
||||||
|
for item in FailureVectors:
|
||||||
|
var r = false
|
||||||
|
try:
|
||||||
|
var a = MultiAddress.init(item)
|
||||||
|
except:
|
||||||
|
r = true
|
||||||
|
check r == true
|
||||||
|
|
||||||
|
test "rust-multiaddr success test vectors":
|
||||||
|
## Rust test vectors are with changed UDP encoding and without WSS
|
||||||
|
for i in 0..<len(RustSuccessVectors):
|
||||||
|
var a = MultiAddress.init(RustSuccessVectors[i])
|
||||||
|
check:
|
||||||
|
hex(a) == RustSuccessExpects[i]
|
||||||
|
|
||||||
|
test "rust-multiaddr failure test vectors":
|
||||||
|
for item in RustFailureVectors:
|
||||||
|
var r = false
|
||||||
|
try:
|
||||||
|
var a = MultiAddress.init(item)
|
||||||
|
except:
|
||||||
|
r = true
|
||||||
|
check r == true
|
||||||
|
|
||||||
|
test "Concatenation test":
|
||||||
|
var ma1 = MultiAddress.init()
|
||||||
|
var ma2 = MultiAddress.init()
|
||||||
|
var ma3 = MultiAddress.init("/ip4/127.0.0.1")
|
||||||
|
var ma4 = MultiAddress.init("/udp/30000")
|
||||||
|
var ma5 = MultiAddress.init("/p2p-circuit")
|
||||||
|
var cma = ma1 & ma3 & ma4 & ma5
|
||||||
|
ma2 &= ma3
|
||||||
|
ma2 &= ma4
|
||||||
|
ma2 &= ma5
|
||||||
|
check:
|
||||||
|
$cma == "/ip4/127.0.0.1/udp/30000/p2p-circuit"
|
||||||
|
$ma2 == "/ip4/127.0.0.1/udp/30000/p2p-circuit"
|
Loading…
Reference in New Issue