mirror of https://github.com/vacp2p/nim-libp2p.git
Merge pull request #1 from status-im/chat
[WIP] Do not merge! Chat and further improvements.
This commit is contained in:
commit
f9a807af53
|
@ -0,0 +1,129 @@
|
|||
import asyncdispatch2, nimcrypto, strutils
|
||||
import ../libp2p/daemon/daemonapi, ../libp2p/[base58, multiaddress]
|
||||
|
||||
const
|
||||
ConsoleAddress = "/tmp/console-chat.sock"
|
||||
ServerAddress = "/tmp/remote-chat.sock"
|
||||
ServerProtocols = @["/test-chat-stream"]
|
||||
|
||||
type
|
||||
CustomData = ref object
|
||||
api: DaemonAPI
|
||||
remotes: seq[StreamTransport]
|
||||
|
||||
proc threadMain(a: int) {.thread.} =
|
||||
## This procedure performs reading from `stdin` and sends data over
|
||||
## unix domain socket to main thread.
|
||||
var transp = waitFor connect(initTAddress(ConsoleAddress))
|
||||
|
||||
while true:
|
||||
var line = stdin.readLine()
|
||||
let res = waitFor transp.write(line & "\r\n")
|
||||
|
||||
proc serveThread(server: StreamServer,
|
||||
transp: StreamTransport) {.async.} =
|
||||
## This procedure perform readin on local unix domain socket and
|
||||
## sends data to remote clients.
|
||||
var udata = getUserData[CustomData](server)
|
||||
|
||||
proc remoteReader(transp: StreamTransport) {.async.} =
|
||||
while true:
|
||||
var line = await transp.readLine()
|
||||
if len(line) == 0:
|
||||
break
|
||||
echo ">> ", line
|
||||
|
||||
while true:
|
||||
try:
|
||||
var line = await transp.readLine()
|
||||
if line.startsWith("/connect"):
|
||||
var parts = line.split(" ")
|
||||
if len(parts) == 2:
|
||||
var peerId = Base58.decode(parts[1])
|
||||
var address = MultiAddress.init(P_P2PCIRCUIT)
|
||||
address &= MultiAddress.init(P_P2P, peerId)
|
||||
echo "= Searching for peer ", parts[1]
|
||||
var id = await udata.api.dhtFindPeer(peerId)
|
||||
echo "= Peer " & parts[1] & " found at addresses:"
|
||||
for item in id.addresses:
|
||||
echo $item
|
||||
echo "= Connecting to peer ", $address
|
||||
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)
|
||||
echo "= Connected to peer chat ", parts[1]
|
||||
asyncCheck remoteReader(stream.transp)
|
||||
elif line.startsWith("/search"):
|
||||
var parts = line.split(" ")
|
||||
if len(parts) == 2:
|
||||
var peerId = Base58.decode(parts[1])
|
||||
echo "= Searching for peer ", parts[1]
|
||||
var id = await udata.api.dhtFindPeer(peerId)
|
||||
echo "= Peer " & parts[1] & " found at addresses:"
|
||||
for item in id.addresses:
|
||||
echo $item
|
||||
elif line.startsWith("/consearch"):
|
||||
var parts = line.split(" ")
|
||||
if len(parts) == 2:
|
||||
var peerId = Base58.decode(parts[1])
|
||||
echo "= Searching for peers connected to peer ", parts[1]
|
||||
var peers = await udata.api.dhtFindPeersConnectedToPeer(peerId)
|
||||
echo "= Found ", len(peers), " connected to peer ", parts[1]
|
||||
for item in peers:
|
||||
var peer = Base58.encode(item.peer)
|
||||
var addresses = newSeq[string]()
|
||||
var relay = false
|
||||
for a in item.addresses:
|
||||
addresses.add($a)
|
||||
if a.protoName() == "/p2p-circuit":
|
||||
relay = true
|
||||
break
|
||||
if relay:
|
||||
echo peer, " * ", " [", addresses.join(", "), "]"
|
||||
else:
|
||||
echo peer, " [", addresses.join(", "), "]"
|
||||
elif line.startsWith("/exit"):
|
||||
quit(0)
|
||||
else:
|
||||
var msg = line & "\r\n"
|
||||
echo "<< ", line
|
||||
var pending = newSeq[Future[int]]()
|
||||
for item in udata.remotes:
|
||||
pending.add(item.write(msg))
|
||||
if len(pending) > 0:
|
||||
var results = await all(pending)
|
||||
except:
|
||||
break
|
||||
|
||||
proc main() {.async.} =
|
||||
var data = new CustomData
|
||||
data.remotes = newSeq[StreamTransport]()
|
||||
|
||||
var lserver = createStreamServer(initTAddress(ConsoleAddress),
|
||||
serveThread, udata = data)
|
||||
lserver.start()
|
||||
var thread: Thread[int]
|
||||
thread.createThread(threadMain, 0)
|
||||
|
||||
echo "= Starting P2P node"
|
||||
data.api = await newDaemonApi({DHTFull, Bootstrap})
|
||||
await sleepAsync(3000)
|
||||
var id = await data.api.identity()
|
||||
|
||||
proc streamHandler(api: DaemonAPI, stream: P2PStream) {.async.} =
|
||||
echo "= Peer ", Base58.encode(stream.peer), " joined chat"
|
||||
data.remotes.add(stream.transp)
|
||||
while true:
|
||||
var line = await stream.transp.readLine()
|
||||
if len(line) == 0:
|
||||
break
|
||||
echo ">> ", line
|
||||
|
||||
await data.api.addHandler(ServerProtocols, streamHandler)
|
||||
echo "= Your PeerID is ", Base58.encode(id.peer)
|
||||
|
||||
when isMainModule:
|
||||
waitFor(main())
|
||||
while true:
|
||||
poll()
|
|
@ -5,11 +5,18 @@ version = "0.0.1"
|
|||
author = "Status Research & Development GmbH"
|
||||
description = "LibP2P implementation"
|
||||
license = "MIT"
|
||||
skipDirs = @["tests", "Nim"]
|
||||
skipDirs = @["tests", "examples", "Nim"]
|
||||
|
||||
requires "nim > 0.18.0",
|
||||
"nimcrypto",
|
||||
"https://github.com/status-im/nim-asyncdispatch2"
|
||||
|
||||
task test, "Runs the test suite":
|
||||
exec "nim c -r tests/testvarint"
|
||||
exec "nim c -r tests/testbase58"
|
||||
exec "nim c -r tests/testbase32"
|
||||
exec "nim c -r tests/testmultiaddress"
|
||||
exec "nim c -r tests/testmultihash"
|
||||
exec "nim c -r tests/testmultibase"
|
||||
exec "nim c -r tests/testcid"
|
||||
exec "nim c -r tests/testdaemon"
|
|
@ -0,0 +1,290 @@
|
|||
## 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 BASE32 encoding and decoding procedures.
|
||||
## This module supports RFC4648's BASE32.
|
||||
|
||||
type
|
||||
Base32Status* {.pure.} = enum
|
||||
Error,
|
||||
Success,
|
||||
Incorrect,
|
||||
Overrun
|
||||
|
||||
Base32Alphabet* = object
|
||||
decode*: array[128, int8]
|
||||
encode*: array[32, uint8]
|
||||
|
||||
Base32Upper* = object
|
||||
## Type to use RFC4868 alphabet in uppercase without padding
|
||||
Base32Lower* = object
|
||||
## Type to use RFC4868 alphabet in lowercase without padding
|
||||
Base32UpperPad* = object
|
||||
## Type to use RFC4868 alphabet in uppercase with padding
|
||||
Base32LowerPad* = object
|
||||
## Type to use RFC4868 alphabet in lowercase with padding
|
||||
HexBase32Upper* = object
|
||||
## Type to use RFC4868-HEX alphabet in uppercase without padding
|
||||
HexBase32Lower* = object
|
||||
## Type to use RFC4868-HEX alphabet in lowercase without padding
|
||||
HexBase32UpperPad* = object
|
||||
## Type to use RFC4868-HEX alphabet in uppercase with padding
|
||||
HexBase32LowerPad* = object
|
||||
## Type to use RFC4868-HEX alphabet in lowercase with padding
|
||||
Base32* = Base32Upper
|
||||
## By default we are using RFC4868 alphabet in uppercase without padding
|
||||
Base32PadTypes* = Base32UpperPad | Base32LowerPad |
|
||||
HexBase32UpperPad | HexBase32LowerPad
|
||||
## All types with padding support
|
||||
Base32NoPadTypes* = Base32Upper | Base32Lower | HexBase32Upper |
|
||||
HexBase32Lower
|
||||
## All types without padding
|
||||
Base32Types* = Base32NoPadTypes | Base32PadTypes
|
||||
## Supported types
|
||||
|
||||
Base32Error* = object of Exception
|
||||
## Base32 specific exception type
|
||||
|
||||
proc newAlphabet32*(s: string): Base32Alphabet =
|
||||
doAssert(len(s) == 32)
|
||||
for i in 0..<len(s):
|
||||
result.encode[i] = cast[uint8](s[i])
|
||||
for i in 0..<len(result.decode):
|
||||
result.decode[i] = -1
|
||||
for i in 0..<len(result.encode):
|
||||
result.decode[int(result.encode[i])] = int8(i)
|
||||
|
||||
const
|
||||
RFCUpperCaseAlphabet* = newAlphabet32("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567")
|
||||
RFCLowerCaseAlphabet* = newAlphabet32("abcdefghijklmnopqrstuvwxyz234567")
|
||||
HEXUpperCaseAlphabet* = newAlphabet32("0123456789ABCDEFGHIJKLMNOPQRSTUV")
|
||||
HEXLowerCaseAlphabet* = newAlphabet32("0123456789abcdefghijklmnopqrstuv")
|
||||
|
||||
proc encodedLength*(btype: typedesc[Base32Types], length: int): int =
|
||||
## Return estimated length of BASE32 encoded value for plain length
|
||||
## ``length``.
|
||||
let reminder = length mod 5
|
||||
when btype is Base32NoPadTypes:
|
||||
result = ((length div 5) * 8) + (((reminder * 8) + 4) div 5)
|
||||
else:
|
||||
result = ((length div 5) * 8)
|
||||
if reminder != 0:
|
||||
result += 8
|
||||
|
||||
proc decodedLength*(btype: typedesc[Base32Types], length: int): int =
|
||||
## Return estimated length of decoded value of BASE32 encoded value of length
|
||||
## ``length``.
|
||||
let reminder = length mod 8
|
||||
result = (length div 8) * 5 + ((reminder * 5) div 8)
|
||||
|
||||
proc convert5to8(inbytes: openarray[byte], outbytes: var openarray[char],
|
||||
length: int): int {.inline.} =
|
||||
if length >= 1:
|
||||
outbytes[0] = chr(inbytes[0] shr 3)
|
||||
outbytes[1] = chr((inbytes[0] and 7'u8) shl 2)
|
||||
result = 2
|
||||
if length >= 2:
|
||||
outbytes[1] = chr(cast[byte](outbytes[1]) or cast[byte](inbytes[1] shr 6))
|
||||
outbytes[2] = chr((inbytes[1] shr 1) and 31'u8)
|
||||
outbytes[3] = chr((inbytes[1] and 1'u8) shl 4)
|
||||
result = 4
|
||||
if length >= 3:
|
||||
outbytes[3] = chr(cast[byte](outbytes[3]) or (inbytes[2] shr 4))
|
||||
outbytes[4] = chr((inbytes[2] and 15'u8) shl 1)
|
||||
result = 5
|
||||
if length >= 4:
|
||||
outbytes[4] = chr(cast[byte](outbytes[4]) or (inbytes[3] shr 7))
|
||||
outbytes[5] = chr((inbytes[3] shr 2) and 31'u8)
|
||||
outbytes[6] = chr((inbytes[3] and 3'u8) shl 3)
|
||||
result = 7
|
||||
if length >= 5:
|
||||
outbytes[6] = chr(cast[byte](outbytes[6]) or (inbytes[4] shr 5))
|
||||
outbytes[7] = chr(inbytes[4] and 31'u8)
|
||||
result = 8
|
||||
|
||||
proc convert8to5(inbytes: openarray[byte], outbytes: var openarray[byte],
|
||||
length: int): int {.inline.} =
|
||||
if length >= 2:
|
||||
outbytes[0] = inbytes[0] shl 3
|
||||
outbytes[0] = outbytes[0] or (inbytes[1] shr 2)
|
||||
result = 1
|
||||
if length >= 4:
|
||||
outbytes[1] = (inbytes[1] and 3'u8) shl 6
|
||||
outbytes[1] = outbytes[1] or (inbytes[2] shl 1)
|
||||
outbytes[1] = outbytes[1] or (inbytes[3] shr 4)
|
||||
result = 2
|
||||
if length >= 5:
|
||||
outbytes[2] = (inbytes[3] and 15'u8) shl 4
|
||||
outbytes[2] = outbytes[2] or (inbytes[4] shr 1)
|
||||
result = 3
|
||||
if length >= 7:
|
||||
outbytes[3] = (inbytes[4] and 1'u8) shl 7
|
||||
outbytes[3] = outbytes[3] or (inbytes[5] shl 2)
|
||||
outbytes[3] = outbytes[3] or (inbytes[6] shr 3)
|
||||
result = 4
|
||||
if length >= 8:
|
||||
outbytes[4] = (inbytes[6] and 7'u8) shl 5
|
||||
outbytes[4] = outbytes[4] or (inbytes[7] and 31'u8)
|
||||
result = 5
|
||||
|
||||
proc encode*(btype: typedesc[Base32Types], inbytes: openarray[byte],
|
||||
outstr: var openarray[char], outlen: var int): Base32Status =
|
||||
## Encode array of bytes ``inbytes`` using BASE32 encoding and store
|
||||
## result to ``outstr``. On success ``Base32Status.Success`` will be returned
|
||||
## and ``outlen`` will be set to number of characters stored inside of
|
||||
## ``outstr``. If length of ``outstr`` is not enough then
|
||||
## ``Base32Status.Overrun`` will be returned and ``outlen`` will be set to
|
||||
## number of characters required.
|
||||
when (btype is Base32Upper) or (btype is Base32UpperPad):
|
||||
const alphabet = RFCUpperCaseAlphabet
|
||||
elif (btype is Base32Lower) or (btype is Base32LowerPad):
|
||||
const alphabet = RFCLowerCaseAlphabet
|
||||
elif (btype is HexBase32Upper) or (btype is HexBase32UpperPad):
|
||||
const alphabet = HEXUpperCaseAlphabet
|
||||
elif (btype is HexBase32Lower) or (btype is HexBase32LowerPad):
|
||||
const alphabet = HEXLowerCaseAlphabet
|
||||
|
||||
if len(inbytes) == 0:
|
||||
outlen = 0
|
||||
return Base32Status.Success
|
||||
|
||||
let length = btype.encodedLength(len(inbytes))
|
||||
if length > len(outstr):
|
||||
outlen = length
|
||||
return Base32Status.Overrun
|
||||
|
||||
let reminder = len(inbytes) mod 5
|
||||
let limit = len(inbytes) - reminder
|
||||
var i, k: int
|
||||
while i < limit:
|
||||
discard convert5to8(inbytes.toOpenArray(i, i + 4),
|
||||
outstr.toOpenArray(k, k + 7), 5)
|
||||
for j in 0..7:
|
||||
outstr[k + j] = chr(alphabet.encode[ord(outstr[k + j])])
|
||||
k += 8
|
||||
i += 5
|
||||
|
||||
if reminder != 0:
|
||||
let left = convert5to8(inbytes.toOpenArray(i, i + reminder - 1),
|
||||
outstr.toOpenArray(k, length - 1), reminder)
|
||||
for j in 0..(left - 1):
|
||||
outstr[k] = chr(alphabet.encode[ord(outstr[k])])
|
||||
inc(k)
|
||||
when (btype is Base32UpperPad) or (btype is Base32LowerPad) or
|
||||
(btype is HexBase32UpperPad) or (btype is HexBase32LowerPad):
|
||||
while k < len(outstr):
|
||||
outstr[k] = '='
|
||||
inc(k)
|
||||
outlen = k
|
||||
result = Base32Status.Success
|
||||
|
||||
proc encode*(btype: typedesc[Base32Types],
|
||||
inbytes: openarray[byte]): string {.inline.} =
|
||||
## Encode array of bytes ``inbytes`` using BASE32 encoding and return
|
||||
## encoded string.
|
||||
if len(inbytes) == 0:
|
||||
result = ""
|
||||
else:
|
||||
var length = 0
|
||||
result = newString(btype.encodedLength(len(inbytes)))
|
||||
if btype.encode(inbytes, result, length) == Base32Status.Success:
|
||||
result.setLen(length)
|
||||
else:
|
||||
result = ""
|
||||
|
||||
proc decode*[T: byte|char](btype: typedesc[Base32Types], instr: openarray[T],
|
||||
outbytes: var openarray[byte], outlen: var int): Base32Status =
|
||||
## Decode BASE32 string and store array of bytes to ``outbytes``. On success
|
||||
## ``Base32Status.Success`` will be returned and ``outlen`` will be set
|
||||
## to number of bytes stored.
|
||||
##
|
||||
## ## If length of ``outbytes`` is not enough to store decoded bytes, then
|
||||
## ``Base32Status.Overrun`` will be returned and ``outlen`` will be set to
|
||||
## number of bytes required.
|
||||
when (btype is Base32Upper) or (btype is Base32UpperPad):
|
||||
const alphabet = RFCUpperCaseAlphabet
|
||||
elif (btype is Base32Lower) or (btype is Base32LowerPad):
|
||||
const alphabet = RFCLowerCaseAlphabet
|
||||
elif (btype is HexBase32Upper) or (btype is HexBase32UpperPad):
|
||||
const alphabet = HEXUpperCaseAlphabet
|
||||
elif (btype is HexBase32Lower) or (btype is HexBase32LowerPad):
|
||||
const alphabet = HEXLowerCaseAlphabet
|
||||
|
||||
if len(instr) == 0:
|
||||
outlen = 0
|
||||
return Base32Status.Success
|
||||
|
||||
let length = btype.decodedLength(len(instr))
|
||||
if length > len(outbytes):
|
||||
outlen = length
|
||||
return Base32Status.Overrun
|
||||
|
||||
var inlen = len(instr)
|
||||
when (btype is Base32PadTypes):
|
||||
for i in countdown(inlen - 1, 0):
|
||||
if instr[i] != '=':
|
||||
break
|
||||
dec(inlen)
|
||||
|
||||
let reminder = inlen mod 8
|
||||
let limit = inlen - reminder
|
||||
var buffer: array[8, byte]
|
||||
var i, k: int
|
||||
while i < limit:
|
||||
for j in 0..<8:
|
||||
if (cast[byte](instr[i + j]) and 0x80'u8) != 0:
|
||||
outlen = 0
|
||||
zeroMem(addr outbytes[0], i + 8)
|
||||
return Base32Status.Incorrect
|
||||
let ch = alphabet.decode[int8(instr[i + j])]
|
||||
if ch == -1:
|
||||
outlen = 0
|
||||
zeroMem(addr outbytes[0], i + 8)
|
||||
return Base32Status.Incorrect
|
||||
buffer[j] = cast[byte](ch)
|
||||
discard convert8to5(buffer, outbytes.toOpenArray(k, k + 4), 8)
|
||||
k += 5
|
||||
i += 8
|
||||
|
||||
var left = 0
|
||||
if reminder != 0:
|
||||
if reminder == 1 or reminder == 3 or reminder == 6:
|
||||
outlen = 0
|
||||
zeroMem(addr outbytes[0], i + 8)
|
||||
return Base32Status.Incorrect
|
||||
for j in 0..<reminder:
|
||||
if (cast[byte](instr[i + j]) and 0x80'u8) != 0:
|
||||
outlen = 0
|
||||
zeroMem(addr outbytes[0], i + 8)
|
||||
result = Base32Status.Incorrect
|
||||
return
|
||||
let ch = alphabet.decode[int8(instr[i + j])]
|
||||
if ch == -1:
|
||||
outlen = 0
|
||||
zeroMem(addr outbytes[0], i + 8)
|
||||
result = Base32Status.Incorrect
|
||||
return
|
||||
buffer[j] = cast[byte](ch)
|
||||
left = convert8to5(buffer.toOpenArray(0, reminder - 1),
|
||||
outbytes.toOpenArray(k, length - 1), reminder)
|
||||
outlen = k + left
|
||||
result = Base32Status.Success
|
||||
|
||||
proc decode*[T: byte|char](btype: typedesc[Base32Types],
|
||||
instr: openarray[T]): seq[byte] =
|
||||
## Decode BASE32 string ``instr`` and return sequence of bytes as result.
|
||||
if len(instr) == 0:
|
||||
result = newSeq[byte]()
|
||||
else:
|
||||
var length = 0
|
||||
result = newSeq[byte](btype.decodedLength(len(instr)))
|
||||
if btype.decode(instr, result, length) == Base32Status.Success:
|
||||
result.setLen(length)
|
||||
else:
|
||||
raise newException(Base32Error, "Incorrect base58 string")
|
|
@ -0,0 +1,239 @@
|
|||
## 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 BASE58 encoding and decoding procedures.
|
||||
## This module supports two variants of BASE58 encoding (Bitcoin and Flickr).
|
||||
|
||||
type
|
||||
Base58Status* {.pure.} = enum
|
||||
Error,
|
||||
Success,
|
||||
Incorrect,
|
||||
Overrun
|
||||
|
||||
Base58Alphabet* = object
|
||||
decode*: array[128, int8]
|
||||
encode*: array[58, uint8]
|
||||
|
||||
BTCBase58* = object
|
||||
## Type to use Bitcoin alphabet
|
||||
FLCBase58* = object
|
||||
## Type to use Flickr alphabet
|
||||
Base58* = BtcBase58
|
||||
## By default we are using Bitcoin alphabet
|
||||
Base58C* = BTCBase58 | FLCBase58
|
||||
## Supported types
|
||||
|
||||
Base58Error* = object of Exception
|
||||
## Base58 specific exception type
|
||||
|
||||
proc newAlphabet58*(s: string): Base58Alphabet =
|
||||
doAssert(len(s) == 58)
|
||||
for i in 0..<len(s):
|
||||
result.encode[i] = cast[uint8](s[i])
|
||||
for i in 0..<len(result.decode):
|
||||
result.decode[i] = -1
|
||||
for i in 0..<len(result.encode):
|
||||
result.decode[int(result.encode[i])] = int8(i)
|
||||
|
||||
const
|
||||
BTCAlphabet* = newAlphabet58("123456789ABCDEFGHJKLMNPQRSTUV" &
|
||||
"WXYZabcdefghijkmnopqrstuvwxyz")
|
||||
FlickrAlphabet* = newAlphabet58("123456789abcdefghijkmnopqrstu" &
|
||||
"vwxyzABCDEFGHJKLMNPQRSTUVWXYZ")
|
||||
|
||||
proc encodedLength*(btype: typedesc[Base58C], length: int): int =
|
||||
## Return estimated length of BASE58 encoded value for plain length
|
||||
## ``length``.
|
||||
result = (length * 138) div 100 + 1
|
||||
|
||||
proc decodedLength*(btype: typedesc[Base58C], length: int): int =
|
||||
## Return estimated length of decoded value of BASE58 encoded value of length
|
||||
## ``length``.
|
||||
result = length + 4
|
||||
|
||||
proc encode*(btype: typedesc[Base58C], inbytes: openarray[byte],
|
||||
outstr: var openarray[char], outlen: var int): Base58Status =
|
||||
## Encode array of bytes ``inbytes`` using BASE58 encoding and store
|
||||
## result to ``outstr``. On success ``Base58Status.Success`` will be returned
|
||||
## and ``outlen`` will be set to number of characters stored inside of
|
||||
## ``outstr``. If length of ``outstr`` is not enough then
|
||||
## ``Base58Status.Overrun`` will be returned and ``outlen`` will be set to
|
||||
## number of characters required.
|
||||
when btype is BTCBase58:
|
||||
const alphabet = BTCAlphabet
|
||||
elif btype is FLCBase58:
|
||||
const alphabet = FlickrAlphabet
|
||||
|
||||
let binsz = len(inbytes)
|
||||
var zcount = 0
|
||||
|
||||
while zcount < binsz and inbytes[zcount] == 0x00'u8:
|
||||
inc(zcount)
|
||||
|
||||
let size = ((binsz - zcount) * 138) div 100 + 1
|
||||
var buffer = newSeq[uint8](size)
|
||||
|
||||
var hi = size - 1
|
||||
var i = zcount
|
||||
var j = size - 1
|
||||
while i < binsz:
|
||||
var carry = uint32(inbytes[i])
|
||||
j = size - 1
|
||||
while (j > hi) or (carry != 0'u32):
|
||||
carry = carry + uint32(256'u32 * buffer[j])
|
||||
buffer[j] = cast[byte](carry mod 58)
|
||||
carry = carry div 58
|
||||
dec(j)
|
||||
hi = j
|
||||
inc(i)
|
||||
|
||||
j = 0
|
||||
while (j < size) and (buffer[j] == 0x00'u8):
|
||||
inc(j)
|
||||
|
||||
let needed = zcount + size - j
|
||||
outlen = needed
|
||||
if len(outstr) < needed:
|
||||
result = Base58Status.Overrun
|
||||
else:
|
||||
for k in 0..<zcount:
|
||||
outstr[k] = cast[char](alphabet.encode[0])
|
||||
i = zcount
|
||||
while j < size:
|
||||
outstr[i] = cast[char](alphabet.encode[buffer[j]])
|
||||
inc(j)
|
||||
inc(i)
|
||||
result = Base58Status.Success
|
||||
|
||||
proc encode*(btype: typedesc[Base58C],
|
||||
inbytes: openarray[byte]): string {.inline.} =
|
||||
## Encode array of bytes ``inbytes`` using BASE58 encoding and return
|
||||
## encoded string.
|
||||
var size = (len(inbytes) * 138) div 100 + 1
|
||||
result = newString(size)
|
||||
if btype.encode(inbytes, result.toOpenArray(0, size - 1),
|
||||
size) == Base58Status.Success:
|
||||
result.setLen(size)
|
||||
else:
|
||||
result = ""
|
||||
|
||||
proc decode*[T: byte|char](btype: typedesc[Base58C], instr: openarray[T],
|
||||
outbytes: var openarray[byte], outlen: var int): Base58Status =
|
||||
## Decode BASE58 string and store array of bytes to ``outbytes``. On success
|
||||
## ``Base58Status.Success`` will be returned and ``outlen`` will be set
|
||||
## to number of bytes stored.
|
||||
##
|
||||
## Length of ``outbytes`` must be equal or more then ``len(instr) + 4``.
|
||||
##
|
||||
## If ``instr`` has characters which are not part of BASE58 alphabet, then
|
||||
## ``Base58Status.Incorrect`` will be returned and ``outlen`` will be set to
|
||||
## ``0``.
|
||||
##
|
||||
## If length of ``outbytes`` is not enough to store decoded bytes, then
|
||||
## ``Base58Status.Overrun`` will be returned and ``outlen`` will be set to
|
||||
## number of bytes required.
|
||||
when btype is BTCBase58:
|
||||
const alphabet = BTCAlphabet
|
||||
elif btype is FLCBase58:
|
||||
const alphabet = FlickrAlphabet
|
||||
|
||||
if len(instr) == 0:
|
||||
outlen = 0
|
||||
return Base58Status.Success
|
||||
|
||||
let binsz = len(instr) + 4
|
||||
if len(outbytes) < binsz:
|
||||
outlen = binsz
|
||||
return Base58Status.Overrun
|
||||
|
||||
var bytesleft = binsz mod 4
|
||||
var zeromask: uint32
|
||||
if bytesleft != 0:
|
||||
zeromask = cast[uint32](0xFFFF_FFFF'u32 shl (bytesleft * 8))
|
||||
|
||||
let size = (binsz + 3) div 4
|
||||
var buffer = newSeq[uint32](size)
|
||||
|
||||
var zcount = 0
|
||||
while zcount < len(instr) and instr[zcount] == cast[char](alphabet.encode[0]):
|
||||
inc(zcount)
|
||||
|
||||
for i in zcount..<len(instr):
|
||||
if (cast[byte](instr[i]) and 0x80'u8) != 0:
|
||||
outlen = 0
|
||||
result = Base58Status.Incorrect
|
||||
return
|
||||
let ch = alphabet.decode[int8(instr[i])]
|
||||
if ch == -1:
|
||||
outlen = 0
|
||||
result = Base58Status.Incorrect
|
||||
return
|
||||
var c = cast[uint32](ch)
|
||||
for j in countdown(size - 1, 0):
|
||||
let t = cast[uint64](buffer[j]) * 58 + c
|
||||
c = cast[uint32]((t and 0x3F_0000_0000'u64) shr 32)
|
||||
buffer[j] = cast[uint32](t and 0xFFFF_FFFF'u32)
|
||||
if c != 0:
|
||||
outlen = 0
|
||||
result = Base58Status.Incorrect
|
||||
return
|
||||
if (buffer[0] and zeromask) != 0:
|
||||
outlen = 0
|
||||
result = Base58Status.Incorrect
|
||||
return
|
||||
|
||||
var boffset = 0
|
||||
var joffset = 0
|
||||
if bytesleft == 3:
|
||||
outbytes[boffset] = cast[uint8]((buffer[0] and 0xFF_0000'u32) shr 16)
|
||||
inc(boffset)
|
||||
bytesleft = 2
|
||||
if bytesleft == 2:
|
||||
outbytes[boffset] = cast[uint8]((buffer[0] and 0xFF00'u32) shr 8)
|
||||
inc(boffset)
|
||||
bytesleft = 1
|
||||
if bytesleft == 1:
|
||||
outbytes[boffset] = cast[uint8]((buffer[0] and 0xFF'u32))
|
||||
inc(boffset)
|
||||
joffset = 1
|
||||
|
||||
while joffset < size:
|
||||
outbytes[boffset + 0] = cast[byte]((buffer[joffset] shr 0x18) and 0xFF)
|
||||
outbytes[boffset + 1] = cast[byte]((buffer[joffset] shr 0x10) and 0xFF)
|
||||
outbytes[boffset + 2] = cast[byte]((buffer[joffset] shr 0x8) and 0xFF)
|
||||
outbytes[boffset + 3] = cast[byte](buffer[joffset] and 0xFF)
|
||||
boffset += 4
|
||||
inc(joffset)
|
||||
|
||||
outlen = binsz
|
||||
var m = 0
|
||||
while m < binsz:
|
||||
if outbytes[m] != 0x00:
|
||||
if zcount > m:
|
||||
result = Base58Status.Overrun
|
||||
return
|
||||
break
|
||||
inc(m)
|
||||
dec(outlen)
|
||||
|
||||
if m < binsz:
|
||||
moveMem(addr outbytes[zcount], addr outbytes[binsz - outlen], outlen)
|
||||
outlen += zcount
|
||||
result = Base58Status.Success
|
||||
|
||||
proc decode*(btype: typedesc[Base58C], instr: string): seq[byte] =
|
||||
## Decode BASE58 string ``instr`` and return sequence of bytes as result.
|
||||
if len(instr) > 0:
|
||||
var size = len(instr) + 4
|
||||
result = newSeq[byte](size)
|
||||
if btype.decode(instr, result, size) == Base58Status.Success:
|
||||
result.setLen(size)
|
||||
else:
|
||||
raise newException(Base58Error, "Incorrect base58 string")
|
|
@ -0,0 +1,262 @@
|
|||
## 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 implementes CID (Content IDentifier).
|
||||
import tables
|
||||
import multibase, multicodec, multihash, vbuffer, varint, base58
|
||||
|
||||
type
|
||||
CidStatus* {.pure.} = enum
|
||||
Error, Success, Incorrect, Overrun
|
||||
|
||||
CidVersion* = enum
|
||||
CIDvIncorrect, CIDv0, CIDv1, CIDvReserved
|
||||
|
||||
Cid* = object
|
||||
cidver*: CidVersion
|
||||
mcodec*: MultiCodec
|
||||
hpos*: int
|
||||
data*: VBuffer
|
||||
|
||||
CidError* = object of Exception
|
||||
|
||||
const
|
||||
ContentIdsList = [
|
||||
multiCodec("raw"),
|
||||
multiCodec("dag-pb"),
|
||||
multiCodec("dag-cbor"),
|
||||
multiCodec("dag-json"),
|
||||
multiCodec("git-raw"),
|
||||
multiCodec("eth-block"),
|
||||
multiCodec("eth-block-list"),
|
||||
multiCodec("eth-tx-trie"),
|
||||
multiCodec("eth-tx"),
|
||||
multiCodec("eth-tx-receipt-trie"),
|
||||
multiCodec("eth-tx-receipt"),
|
||||
multiCodec("eth-state-trie"),
|
||||
multiCodec("eth-account-snapshot"),
|
||||
multiCodec("eth-storage-trie"),
|
||||
multiCodec("bitcoin-block"),
|
||||
multiCodec("bitcoin-tx"),
|
||||
multiCodec("zcash-block"),
|
||||
multiCodec("zcash-tx"),
|
||||
multiCodec("stellar-block"),
|
||||
multiCodec("stellar-tx"),
|
||||
multiCodec("decred-block"),
|
||||
multiCodec("decred-tx"),
|
||||
multiCodec("dash-block"),
|
||||
multiCodec("dash-tx"),
|
||||
multiCodec("torrent-info"),
|
||||
multiCodec("torrent-file"),
|
||||
multiCodec("ed25519-pub")
|
||||
]
|
||||
|
||||
proc initCidCodeTable(): Table[int, MultiCodec] {.compileTime.} =
|
||||
result = initTable[int, MultiCodec]()
|
||||
for item in ContentIdsList:
|
||||
result[int(item)] = item
|
||||
|
||||
const
|
||||
CodeContentIds = initCidCodeTable()
|
||||
|
||||
proc decode(data: openarray[byte], cid: var Cid): CidStatus =
|
||||
if len(data) == 34:
|
||||
if data[0] == 0x12'u8 and data[1] == 0x20'u8:
|
||||
cid.cidver = CIDv0
|
||||
cid.mcodec = multiCodec("dag-pb")
|
||||
cid.hpos = 0
|
||||
cid.data = initVBuffer(data)
|
||||
result = CidStatus.Success
|
||||
if cid.cidver == CIDvIncorrect:
|
||||
var version, codec: uint64
|
||||
var res, offset: int
|
||||
var vb = initVBuffer(data)
|
||||
if vb.isEmpty():
|
||||
return CidStatus.Incorrect
|
||||
res = vb.readVarint(version)
|
||||
if res == -1:
|
||||
return CidStatus.Incorrect
|
||||
offset += res
|
||||
if version != 1'u64:
|
||||
return CidStatus.Incorrect
|
||||
res = vb.readVarint(codec)
|
||||
if res == -1:
|
||||
return CidStatus.Incorrect
|
||||
offset += res
|
||||
var mcodec = CodeContentIds.getOrDefault(cast[int](codec),
|
||||
InvalidMultiCodec)
|
||||
if mcodec == InvalidMultiCodec:
|
||||
return CidStatus.Incorrect
|
||||
if not MultiHash.validate(vb.buffer.toOpenArray(vb.offset,
|
||||
len(vb.buffer) - 1)):
|
||||
return CidStatus.Incorrect
|
||||
vb.finish()
|
||||
cid.cidver = CIDv1
|
||||
cid.mcodec = mcodec
|
||||
cid.hpos = offset
|
||||
cid.data = vb
|
||||
result = CidStatus.Success
|
||||
|
||||
proc decode(data: openarray[char], cid: var Cid): CidStatus =
|
||||
var buffer: seq[byte]
|
||||
var plen = 0
|
||||
if len(data) < 2:
|
||||
return CidStatus.Incorrect
|
||||
if len(data) == 46:
|
||||
if data[0] == 'Q' and data[1] == 'm':
|
||||
buffer = newSeq[byte](BTCBase58.decodedLength(len(data)))
|
||||
if BTCBase58.decode(data, buffer, plen) != Base58Status.Success:
|
||||
return CidStatus.Incorrect
|
||||
buffer.setLen(plen)
|
||||
if len(buffer) == 0:
|
||||
let length = MultiBase.decodedLength(data[0], len(data))
|
||||
if length == -1:
|
||||
return CidStatus.Incorrect
|
||||
buffer = newSeq[byte](length)
|
||||
if MultiBase.decode(data, buffer, plen) != MultiBaseStatus.Success:
|
||||
return CidStatus.Incorrect
|
||||
buffer.setLen(plen)
|
||||
if buffer[0] == 0x12'u8:
|
||||
return CidStatus.Incorrect
|
||||
result = decode(buffer, cid)
|
||||
|
||||
proc validate*(ctype: typedesc[Cid], data: openarray[byte]): bool =
|
||||
## Returns ``true`` is data has valid binary CID representation.
|
||||
var version, codec: uint64
|
||||
var res: VarintStatus
|
||||
if len(data) < 2:
|
||||
return false
|
||||
let last = len(data) - 1
|
||||
if len(data) == 34:
|
||||
if data[0] == 0x12'u8 and data[1] == 0x20'u8:
|
||||
return true
|
||||
var offset = 0
|
||||
var length = 0
|
||||
res = LP.getUVarint(data.toOpenArray(offset, last), length, version)
|
||||
if res != VarintStatus.Success:
|
||||
return false
|
||||
if version != 1'u64:
|
||||
return false
|
||||
offset += length
|
||||
if offset >= len(data):
|
||||
return false
|
||||
res = LP.getUVarint(data.toOpenArray(offset, last), length, codec)
|
||||
if res != VarintStatus.Success:
|
||||
return false
|
||||
var mcodec = CodeContentIds.getOrDefault(cast[int](codec), InvalidMultiCodec)
|
||||
if mcodec == InvalidMultiCodec:
|
||||
return false
|
||||
if not MultiHash.validate(data.toOpenArray(offset, last)):
|
||||
return false
|
||||
result = true
|
||||
|
||||
proc mhash*(cid: Cid): MultiHash =
|
||||
## Returns MultiHash part of CID.
|
||||
if cid.cidver notin {CIDv0, CIDv1}:
|
||||
raise newException(CidError, "Incorrect CID!")
|
||||
result = MultiHash.init(cid.data.buffer.toOpenArray(cid.hpos,
|
||||
len(cid.data) - 1))
|
||||
|
||||
proc contentType*(cid: Cid): MultiCodec =
|
||||
## Returns content type part of CID
|
||||
if cid.cidver notin {CIDv0, CIDv1}:
|
||||
raise newException(CidError, "Incorrect CID!")
|
||||
result = cid.mcodec
|
||||
|
||||
proc version*(cid: Cid): CidVersion =
|
||||
## Returns CID version
|
||||
result = cid.cidver
|
||||
|
||||
proc init*[T: char|byte](ctype: typedesc[Cid], data: openarray[T]): Cid =
|
||||
## Create new content identifier using array of bytes or string ``data``.
|
||||
if decode(data, result) != CidStatus.Success:
|
||||
raise newException(CidError, "Incorrect CID!")
|
||||
|
||||
proc init*(ctype: typedesc[Cid], version: CidVersion, content: MultiCodec,
|
||||
hash: MultiHash): Cid =
|
||||
## Create new content identifier using content type ``content`` and
|
||||
## MultiHash ``hash`` using version ``version``.
|
||||
##
|
||||
## To create ``CIDv0`` you need to use:
|
||||
## Cid.init(CIDv0, multiCodec("dag-pb"), MultiHash.digest("sha2-256", data))
|
||||
##
|
||||
## All other encodings and hashes are not supported by CIDv0.
|
||||
result.cidver = version
|
||||
|
||||
if version == CIDv0:
|
||||
if content != multiCodec("dag-pb"):
|
||||
raise newException(CidError,
|
||||
"CIDv0 supports only `dag-pb` content type!")
|
||||
result.data = initVBuffer()
|
||||
if hash.mcodec != multiCodec("sha2-256"):
|
||||
raise newException(CidError,
|
||||
"CIDv0 supports only `sha2-256` hash digest!")
|
||||
result.mcodec = content
|
||||
result.data.write(hash)
|
||||
result.data.finish()
|
||||
elif version == CIDv1:
|
||||
let mcodec = CodeContentIds.getOrDefault(cast[int](content),
|
||||
InvalidMultiCodec)
|
||||
if mcodec == InvalidMultiCodec:
|
||||
raise newException(CidError, "Incorrect content type")
|
||||
result.mcodec = mcodec
|
||||
result.data = initVBuffer()
|
||||
result.data.writeVarint(cast[uint64](1))
|
||||
result.data.write(mcodec)
|
||||
result.hpos = len(result.data.buffer)
|
||||
result.data.write(hash)
|
||||
result.data.finish()
|
||||
else:
|
||||
raise newException(CidError, "CID version is not supported" & $version)
|
||||
|
||||
proc `==`*(a: Cid, b: Cid): bool =
|
||||
## Compares content identifiers ``a`` and ``b``, returns ``true`` if hashes
|
||||
## are equal, ``false`` otherwise.
|
||||
if a.mcodec == b.mcodec:
|
||||
var ah, bh: MultiHash
|
||||
if MultiHash.decode(a.data.buffer.toOpenArray(a.hpos,
|
||||
len(a.data) - 1), ah) == -1:
|
||||
return false
|
||||
if MultiHash.decode(b.data.buffer.toOpenArray(b.hpos,
|
||||
len(b.data) - 1), bh) == -1:
|
||||
return false
|
||||
result = (ah == bh)
|
||||
|
||||
proc base58*(cid: Cid): string =
|
||||
## Get BASE58 encoded string representation of content identifier ``cid``.
|
||||
result = BTCBase58.encode(cid.data.buffer)
|
||||
|
||||
proc hex*(cid: Cid): string =
|
||||
## Get hexadecimal string representation of content identifier ``cid``.
|
||||
result = $(cid.data)
|
||||
|
||||
proc repr*(cid: Cid): string =
|
||||
## Get string representation of content identifier ``cid``.
|
||||
result = $(cid.cidver)
|
||||
result.add("/")
|
||||
result.add($(cid.mcodec))
|
||||
result.add("/")
|
||||
result.add($(cid.mhash()))
|
||||
|
||||
proc write*(vb: var VBuffer, cid: Cid) {.inline.} =
|
||||
## Write CID value ``cid`` to buffer ``vb``.
|
||||
vb.writeArray(cid.data.buffer)
|
||||
|
||||
proc encode*(mbtype: typedesc[MultiBase], encoding: string,
|
||||
cid: Cid): string {.inline.} =
|
||||
## Get MultiBase encoded representation of ``cid`` using encoding
|
||||
## ``encoding``.
|
||||
result = MultiBase.encode(encoding, cid.data.buffer)
|
||||
|
||||
proc `$`*(cid: Cid): string =
|
||||
## Return official string representation of content identifier ``cid``.
|
||||
if cid.cidver == CIDv0:
|
||||
result = BTCBase58.encode(cid.data.buffer)
|
||||
elif cid.cidver == CIDv1:
|
||||
result = Multibase.encode("base58btc", cid.data.buffer)
|
|
@ -8,15 +8,17 @@
|
|||
## those terms.
|
||||
|
||||
## This module implementes API for `go-libp2p-daemon`.
|
||||
import os, osproc, strutils, tables, streams
|
||||
import os, osproc, strutils, tables, streams, strtabs
|
||||
import asyncdispatch2
|
||||
import ../varint, ../protobuf/minprotobuf, transpool
|
||||
import ../varint, ../multiaddress, ../base58, ../cid
|
||||
import ../protobuf/minprotobuf
|
||||
|
||||
when not defined(windows):
|
||||
import posix
|
||||
|
||||
const
|
||||
DefaultSocketPath* = "/tmp/p2pd.sock"
|
||||
DefaultSocketPattern* = "/tmp/p2pd-$1.sock"
|
||||
DefaultDaemonFile* = "p2pd"
|
||||
|
||||
type
|
||||
|
@ -29,6 +31,7 @@ type
|
|||
LIST_PEERS = 5,
|
||||
CONNMANAGER = 6,
|
||||
DISCONNECT = 7
|
||||
PUBSUB = 8
|
||||
|
||||
DHTRequestType* {.pure.} = enum
|
||||
FIND_PEER = 0,
|
||||
|
@ -46,6 +49,12 @@ type
|
|||
UNTAG_PEER = 1,
|
||||
TRIM = 2
|
||||
|
||||
PSRequestType* {.pure.} = enum
|
||||
GET_TOPICS = 0,
|
||||
LIST_PEERS = 1,
|
||||
PUBLISH = 2,
|
||||
SUBSCRIBE = 3
|
||||
|
||||
ResponseKind* = enum
|
||||
Malformed,
|
||||
Error,
|
||||
|
@ -57,6 +66,7 @@ type
|
|||
IDENTITY = 4,
|
||||
DHT = 5,
|
||||
PEERINFO = 6
|
||||
PUBSUB = 7
|
||||
|
||||
DHTResponseType* {.pure.} = enum
|
||||
BEGIN = 0,
|
||||
|
@ -65,16 +75,24 @@ type
|
|||
|
||||
PeerID* = seq[byte]
|
||||
MultiProtocol* = string
|
||||
MultiAddress* = seq[byte]
|
||||
CID* = seq[byte]
|
||||
LibP2PPublicKey* = seq[byte]
|
||||
DHTValue* = seq[byte]
|
||||
|
||||
P2PStreamFlags* {.pure.} = enum
|
||||
None, Closed, Inbound, Outbound
|
||||
|
||||
P2PDaemonFlags* {.pure.} = enum
|
||||
DHTClient, DHTFull, Bootstrap
|
||||
P2PDaemonFlags* = enum
|
||||
DHTClient, ## Start daemon in DHT client mode
|
||||
DHTFull, ## Start daemon with full DHT support
|
||||
Bootstrap, ## Start daemon with bootstrap
|
||||
WaitBootstrap, ## Start daemon with bootstrap and wait until daemon
|
||||
## establish connection to at least 2 peers
|
||||
Logging, ## Enable capture daemon `stderr`
|
||||
Verbose, ## Set daemon logging to DEBUG level
|
||||
PSFloodSub, ## Enable `FloodSub` protocol in daemon
|
||||
PSGossipSub, ## Enable `GossipSub` protocol in daemon
|
||||
PSNoSign, ## Disable pubsub message signing (default true)
|
||||
PSStrictSign ## Force strict checking pubsub message signature
|
||||
|
||||
P2PStream* = ref object
|
||||
flags*: set[P2PStreamFlags]
|
||||
|
@ -83,8 +101,12 @@ type
|
|||
protocol*: string
|
||||
transp*: StreamTransport
|
||||
|
||||
P2PServer = object
|
||||
server*: StreamServer
|
||||
address*: TransportAddress
|
||||
|
||||
DaemonAPI* = ref object
|
||||
pool*: TransportPool
|
||||
# pool*: TransportPool
|
||||
flags*: set[P2PDaemonFlags]
|
||||
address*: TransportAddress
|
||||
sockname*: string
|
||||
|
@ -92,18 +114,39 @@ type
|
|||
ucounter*: int
|
||||
process*: Process
|
||||
handlers*: Table[string, P2PStreamCallback]
|
||||
servers*: seq[StreamServer]
|
||||
servers*: seq[P2PServer]
|
||||
log*: string
|
||||
loggerFut*: Future[void]
|
||||
|
||||
PeerInfo* = object
|
||||
peer*: PeerID
|
||||
addresses*: seq[MultiAddress]
|
||||
|
||||
PubsubTicket* = ref object
|
||||
topic*: string
|
||||
handler*: P2PPubSubCallback
|
||||
transp*: StreamTransport
|
||||
|
||||
PubSubMessage* = object
|
||||
peer*: PeerID
|
||||
data*: seq[byte]
|
||||
seqno*: seq[byte]
|
||||
topics*: seq[string]
|
||||
signature*: seq[byte]
|
||||
key*: seq[byte]
|
||||
|
||||
P2PStreamCallback* = proc(api: DaemonAPI,
|
||||
stream: P2PStream): Future[void] {.gcsafe.}
|
||||
P2PPubSubCallback* = proc(api: DaemonAPI,
|
||||
ticket: PubsubTicket,
|
||||
message: PubSubMessage): Future[bool] {.gcsafe.}
|
||||
|
||||
DaemonRemoteError* = object of Exception
|
||||
DaemonLocalError* = object of Exception
|
||||
|
||||
|
||||
var daemonsCount {.threadvar.}: int
|
||||
|
||||
proc requestIdentity(): ProtoBuffer =
|
||||
## https://github.com/libp2p/go-libp2p-daemon/blob/master/conn.go
|
||||
## Processing function `doIdentify(req *pb.Request)`.
|
||||
|
@ -112,14 +155,17 @@ proc requestIdentity(): ProtoBuffer =
|
|||
result.finish()
|
||||
|
||||
proc requestConnect(peerid: PeerID,
|
||||
addresses: openarray[MultiAddress]): ProtoBuffer =
|
||||
addresses: openarray[MultiAddress],
|
||||
timeout = 0): ProtoBuffer =
|
||||
## https://github.com/libp2p/go-libp2p-daemon/blob/master/conn.go
|
||||
## Processing function `doConnect(req *pb.Request)`.
|
||||
result = initProtoBuffer({WithVarintLength})
|
||||
var msg = initProtoBuffer()
|
||||
msg.write(initProtoField(1, peerid))
|
||||
for item in addresses:
|
||||
msg.write(initProtoField(2, item))
|
||||
msg.write(initProtoField(2, item.data.buffer))
|
||||
if timeout > 0:
|
||||
msg.write(initProtoField(3, timeout))
|
||||
result.write(initProtoField(1, cast[uint](RequestType.CONNECT)))
|
||||
result.write(initProtoField(2, msg))
|
||||
result.finish()
|
||||
|
@ -135,7 +181,8 @@ proc requestDisconnect(peerid: PeerID): ProtoBuffer =
|
|||
result.finish()
|
||||
|
||||
proc requestStreamOpen(peerid: PeerID,
|
||||
protocols: openarray[string]): ProtoBuffer =
|
||||
protocols: openarray[string],
|
||||
timeout = 0): ProtoBuffer =
|
||||
## https://github.com/libp2p/go-libp2p-daemon/blob/master/conn.go
|
||||
## Processing function `doStreamOpen(req *pb.Request)`.
|
||||
result = initProtoBuffer({WithVarintLength})
|
||||
|
@ -143,6 +190,8 @@ proc requestStreamOpen(peerid: PeerID,
|
|||
msg.write(initProtoField(1, peerid))
|
||||
for item in protocols:
|
||||
msg.write(initProtoField(2, item))
|
||||
if timeout > 0:
|
||||
msg.write(initProtoField(3, timeout))
|
||||
result.write(initProtoField(1, cast[uint](RequestType.STREAM_OPEN)))
|
||||
result.write(initProtoField(3, msg))
|
||||
result.finish()
|
||||
|
@ -198,7 +247,7 @@ proc requestDHTFindPeersConnectedToPeer(peer: PeerID,
|
|||
result.write(initProtoField(5, msg))
|
||||
result.finish()
|
||||
|
||||
proc requestDHTFindProviders(cid: CID,
|
||||
proc requestDHTFindProviders(cid: Cid,
|
||||
count: uint32, timeout = 0): ProtoBuffer =
|
||||
## https://github.com/libp2p/go-libp2p-daemon/blob/master/dht.go
|
||||
## Processing function `doDHTFindProviders(req *pb.DHTRequest)`.
|
||||
|
@ -206,7 +255,7 @@ proc requestDHTFindProviders(cid: CID,
|
|||
result = initProtoBuffer({WithVarintLength})
|
||||
var msg = initProtoBuffer()
|
||||
msg.write(initProtoField(1, msgid))
|
||||
msg.write(initProtoField(3, cid))
|
||||
msg.write(initProtoField(3, cid.data.buffer))
|
||||
msg.write(initProtoField(6, count))
|
||||
if timeout > 0:
|
||||
msg.write(initProtoField(7, uint(timeout)))
|
||||
|
@ -292,14 +341,14 @@ proc requestDHTPutValue(key: string, value: openarray[byte],
|
|||
result.write(initProtoField(5, msg))
|
||||
result.finish()
|
||||
|
||||
proc requestDHTProvide(cid: CID, timeout = 0): ProtoBuffer =
|
||||
proc requestDHTProvide(cid: Cid, timeout = 0): ProtoBuffer =
|
||||
## https://github.com/libp2p/go-libp2p-daemon/blob/master/dht.go
|
||||
## Processing function `doDHTProvide(req *pb.DHTRequest)`.
|
||||
let msgid = cast[uint](DHTRequestType.PROVIDE)
|
||||
result = initProtoBuffer({WithVarintLength})
|
||||
var msg = initProtoBuffer()
|
||||
msg.write(initProtoField(1, msgid))
|
||||
msg.write(initProtoField(3, cid))
|
||||
msg.write(initProtoField(3, cid.data.buffer))
|
||||
if timeout > 0:
|
||||
msg.write(initProtoField(7, uint(timeout)))
|
||||
msg.finish()
|
||||
|
@ -345,6 +394,58 @@ proc requestCMTrim(): ProtoBuffer =
|
|||
result.write(initProtoField(6, msg))
|
||||
result.finish()
|
||||
|
||||
proc requestPSGetTopics(): ProtoBuffer =
|
||||
## https://github.com/libp2p/go-libp2p-daemon/blob/master/pubsub.go
|
||||
## Processing function `doPubsubGetTopics(req *pb.PSRequest)`.
|
||||
let msgid = cast[uint](PSRequestType.GET_TOPICS)
|
||||
result = initProtoBuffer({WithVarintLength})
|
||||
var msg = initProtoBuffer()
|
||||
msg.write(initProtoField(1, msgid))
|
||||
msg.finish()
|
||||
result.write(initProtoField(1, cast[uint](RequestType.PUBSUB)))
|
||||
result.write(initProtoField(8, msg))
|
||||
result.finish()
|
||||
|
||||
proc requestPSListPeers(topic: string): ProtoBuffer =
|
||||
## https://github.com/libp2p/go-libp2p-daemon/blob/master/pubsub.go
|
||||
## Processing function `doPubsubListPeers(req *pb.PSRequest)`.
|
||||
let msgid = cast[uint](PSRequestType.LIST_PEERS)
|
||||
result = initProtoBuffer({WithVarintLength})
|
||||
var msg = initProtoBuffer()
|
||||
msg.write(initProtoField(1, msgid))
|
||||
msg.write(initProtoField(2, topic))
|
||||
msg.finish()
|
||||
result.write(initProtoField(1, cast[uint](RequestType.PUBSUB)))
|
||||
result.write(initProtoField(8, msg))
|
||||
result.finish()
|
||||
|
||||
proc requestPSPublish(topic: string, data: openarray[byte]): ProtoBuffer =
|
||||
## https://github.com/libp2p/go-libp2p-daemon/blob/master/pubsub.go
|
||||
## Processing function `doPubsubPublish(req *pb.PSRequest)`.
|
||||
let msgid = cast[uint](PSRequestType.PUBLISH)
|
||||
result = initProtoBuffer({WithVarintLength})
|
||||
var msg = initProtoBuffer()
|
||||
msg.write(initProtoField(1, msgid))
|
||||
msg.write(initProtoField(2, topic))
|
||||
msg.write(initProtoField(3, data))
|
||||
msg.finish()
|
||||
result.write(initProtoField(1, cast[uint](RequestType.PUBSUB)))
|
||||
result.write(initProtoField(8, msg))
|
||||
result.finish()
|
||||
|
||||
proc requestPSSubscribe(topic: string): ProtoBuffer =
|
||||
## https://github.com/libp2p/go-libp2p-daemon/blob/master/pubsub.go
|
||||
## Processing function `doPubsubSubscribe(req *pb.PSRequest)`.
|
||||
let msgid = cast[uint](PSRequestType.SUBSCRIBE)
|
||||
result = initProtoBuffer({WithVarintLength})
|
||||
var msg = initProtoBuffer()
|
||||
msg.write(initProtoField(1, msgid))
|
||||
msg.write(initProtoField(2, topic))
|
||||
msg.finish()
|
||||
result.write(initProtoField(1, cast[uint](RequestType.PUBSUB)))
|
||||
result.write(initProtoField(8, msg))
|
||||
result.finish()
|
||||
|
||||
proc checkResponse(pb: var ProtoBuffer): ResponseKind {.inline.} =
|
||||
result = ResponseKind.Malformed
|
||||
var value: uint64
|
||||
|
@ -365,57 +466,140 @@ proc recvMessage(conn: StreamTransport): Future[seq[byte]] {.async.} =
|
|||
length: int
|
||||
res: VarintStatus
|
||||
var buffer = newSeq[byte](10)
|
||||
try:
|
||||
for i in 0..<len(buffer):
|
||||
await conn.readExactly(addr buffer[i], 1)
|
||||
res = PB.getUVarint(buffer.toOpenArray(0, i), length, size)
|
||||
if res == VarintStatus.Success:
|
||||
break
|
||||
if res != VarintStatus.Success or size > MaxMessageSize:
|
||||
raise newException(ValueError, "Invalid message size")
|
||||
buffer.setLen(0)
|
||||
buffer.setLen(size)
|
||||
await conn.readExactly(addr buffer[0], int(size))
|
||||
except TransportIncompleteError:
|
||||
buffer.setLen(0)
|
||||
|
||||
result = buffer
|
||||
|
||||
proc newConnection*(api: DaemonAPI): Future[StreamTransport] =
|
||||
# echo "Establish new connection to daemon [", $api.address, "]"
|
||||
result = connect(api.address)
|
||||
|
||||
proc closeConnection*(api: DaemonAPI, transp: StreamTransport) {.async.} =
|
||||
# echo "Close connection with daemon [", $api.address, "]"
|
||||
transp.close()
|
||||
await transp.join()
|
||||
|
||||
when not defined(windows):
|
||||
proc socketExists(filename: string): bool =
|
||||
var res: Stat
|
||||
result = stat(filename, res) >= 0'i32
|
||||
|
||||
proc loggingHandler(api: DaemonAPI): Future[void] =
|
||||
var retFuture = newFuture[void]("logging.handler")
|
||||
var loop = getGlobalDispatcher()
|
||||
let pfd = SocketHandle(api.process.outputHandle)
|
||||
var fd = AsyncFD(pfd)
|
||||
if not setSocketBlocking(pfd, false):
|
||||
discard close(cint(pfd))
|
||||
retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
|
||||
|
||||
proc readOutputLoop(udata: pointer) {.gcsafe.} =
|
||||
var buffer: array[2048, char]
|
||||
let res = posix.read(cint(fd), addr buffer[0], 2000)
|
||||
if res == -1 or res == 0:
|
||||
removeReader(fd)
|
||||
retFuture.complete()
|
||||
else:
|
||||
var cstr = cast[cstring](addr buffer[0])
|
||||
api.log.add(cstr)
|
||||
register(AsyncFD(pfd))
|
||||
addReader(fd, readOutputLoop, nil)
|
||||
result = retFuture
|
||||
else:
|
||||
proc socketExists(filename: string): bool = false
|
||||
|
||||
proc loggingHandler(api: DaemonAPI): Future[void] =
|
||||
# Not ready yet.
|
||||
discard
|
||||
|
||||
# This is forward declaration needed for newDaemonApi()
|
||||
proc listPeers*(api: DaemonAPI): Future[seq[PeerInfo]] {.async.}
|
||||
|
||||
proc newDaemonApi*(flags: set[P2PDaemonFlags] = {},
|
||||
bootstrapNodes: seq[string] = @[],
|
||||
id: string = "",
|
||||
daemon = DefaultDaemonFile,
|
||||
sockpath = DefaultSocketPath,
|
||||
pattern = "/tmp/nim-p2pd-$1.sock",
|
||||
poolSize = 10): Future[DaemonAPI] {.async.} =
|
||||
## Initialize connections to `go-libp2p-daemon` control socket.
|
||||
result = new DaemonAPI
|
||||
result.flags = flags
|
||||
result.servers = newSeq[StreamServer]()
|
||||
result.address = initTAddress(sockpath)
|
||||
result.pattern = pattern
|
||||
result.ucounter = 1
|
||||
result.handlers = initTable[string, P2PStreamCallback]()
|
||||
poolSize = 10,
|
||||
gossipsubHeartbeatInterval = 0,
|
||||
gossipsubHeartbeatDelay = 0,
|
||||
peersRequired = 2): Future[DaemonAPI] {.async.} =
|
||||
## Initialize connection to `go-libp2p-daemon` control socket.
|
||||
var api = new DaemonAPI
|
||||
var args = newSeq[string]()
|
||||
var env: StringTableRef
|
||||
|
||||
api.flags = flags
|
||||
api.servers = newSeq[P2PServer]()
|
||||
api.pattern = pattern
|
||||
api.ucounter = 1
|
||||
api.handlers = initTable[string, P2PStreamCallback]()
|
||||
api.sockname = sockpath
|
||||
|
||||
if api.sockname == DefaultSocketPath:
|
||||
# If client not specify `sockpath` but tries to spawn many daemons, we will
|
||||
# replace sockname.
|
||||
if daemonsCount != 0:
|
||||
api.sockname = DefaultSocketPattern % [$daemonsCount]
|
||||
|
||||
api.address = initTAddress(api.sockname)
|
||||
inc(daemonsCount)
|
||||
|
||||
# We will start daemon process only when control socket path is not default or
|
||||
# options are specified.
|
||||
if flags == {} and sockpath == DefaultSocketPath:
|
||||
result.pool = await newPool(initTAddress(sockpath), poolsize = poolSize)
|
||||
if flags == {} and api.sockname == DefaultSocketPath:
|
||||
discard
|
||||
else:
|
||||
var args = newSeq[string]()
|
||||
# DHTFull and DHTClient could not be present at the same time
|
||||
if P2PDaemonFlags.DHTFull in flags and P2PDaemonFlags.DHTClient in flags:
|
||||
result.flags.excl(DHTClient)
|
||||
if P2PDaemonFlags.DHTFull in result.flags:
|
||||
if DHTFull in flags and DHTClient in flags:
|
||||
api.flags.excl(DHTClient)
|
||||
# PSGossipSub and PSFloodSub could not be present at the same time
|
||||
if PSGossipSub in flags and PSFloodSub in flags:
|
||||
api.flags.excl(PSFloodSub)
|
||||
if DHTFull in api.flags:
|
||||
args.add("-dht")
|
||||
if P2PDaemonFlags.DHTClient in result.flags:
|
||||
if DHTClient in api.flags:
|
||||
args.add("-dhtClient")
|
||||
if P2PDaemonFlags.Bootstrap in result.flags:
|
||||
if {Bootstrap, WaitBootstrap} * api.flags != {}:
|
||||
args.add("-b")
|
||||
if Verbose in api.flags:
|
||||
env = newStringTable("IPFS_LOGGING", "debug", modeCaseSensitive)
|
||||
if PSGossipSub in api.flags:
|
||||
args.add("-pubsub")
|
||||
args.add("-pubsubRouter=gossipsub")
|
||||
if gossipsubHeartbeatInterval != 0:
|
||||
let param = $gossipsubHeartbeatInterval & "ms"
|
||||
args.add("-gossipsubHeartbeatInterval=" & param)
|
||||
if gossipsubHeartbeatDelay != 0:
|
||||
let param = $gossipsubHeartbeatDelay & "ms"
|
||||
args.add("-gossipsubHeartbeatInitialDelay=" & param)
|
||||
if PSFloodSub in api.flags:
|
||||
args.add("-pubsub")
|
||||
args.add("-pubsubRouter=floodsub")
|
||||
if api.flags * {PSFloodSub, PSGossipSub} != {}:
|
||||
if PSNoSign in api.flags:
|
||||
args.add("-pubsubSign=false")
|
||||
if PSStrictSign in api.flags:
|
||||
args.add("-pubsubSignStrict=true")
|
||||
if len(bootstrapNodes) > 0:
|
||||
args.add("-bootstrapPeers=" & bootstrapNodes.join(","))
|
||||
if len(id) != 0:
|
||||
args.add("-id=" & id)
|
||||
if sockpath != DefaultSocketPath:
|
||||
args.add("-sock=" & sockpath)
|
||||
if api.sockname != DefaultSocketPath:
|
||||
args.add("-sock=" & api.sockname)
|
||||
|
||||
# We are trying to get absolute daemon path.
|
||||
let cmd = findExe(daemon)
|
||||
if len(cmd) == 0:
|
||||
|
@ -424,23 +608,36 @@ proc newDaemonApi*(flags: set[P2PDaemonFlags] = {},
|
|||
# if its not able to create new socket control file.
|
||||
# We can't use `existsFile()` because it do not support unix-domain socket
|
||||
# endpoints.
|
||||
if socketExists(sockpath):
|
||||
discard tryRemoveFile(sockpath)
|
||||
if socketExists(api.sockname):
|
||||
if not tryRemoveFile(api.sockname):
|
||||
if api.sockname != sockpath:
|
||||
raise newException(DaemonLocalError, "Socket is already bound!")
|
||||
# Starting daemon process
|
||||
result.process = startProcess(cmd, "", args, options = {poStdErrToStdOut})
|
||||
api.process = startProcess(cmd, "", args, env, {poStdErrToStdOut})
|
||||
# Waiting until daemon will not be bound to control socket.
|
||||
while true:
|
||||
if not result.process.running():
|
||||
echo result.process.errorStream.readAll()
|
||||
if not api.process.running():
|
||||
echo api.process.errorStream.readAll()
|
||||
raise newException(DaemonLocalError,
|
||||
"Daemon executable could not be started!")
|
||||
if socketExists(sockpath):
|
||||
if socketExists(api.sockname):
|
||||
break
|
||||
await sleepAsync(100)
|
||||
result.sockname = sockpath
|
||||
result.pool = await newPool(initTAddress(sockpath), poolsize = poolSize)
|
||||
# api.pool = await newPool(api.address, poolsize = poolSize)
|
||||
if Logging in api.flags:
|
||||
api.loggerFut = loggingHandler(api)
|
||||
|
||||
proc close*(api: DaemonAPI, stream: P2PStream) {.async.} =
|
||||
if WaitBootstrap in api.flags:
|
||||
while true:
|
||||
var peers = await listPeers(api)
|
||||
echo len(peers)
|
||||
if len(peers) >= peersRequired:
|
||||
break
|
||||
await sleepAsync(1000)
|
||||
|
||||
result = api
|
||||
|
||||
proc close*(stream: P2PStream) {.async.} =
|
||||
## Close ``stream``.
|
||||
if P2PStreamFlags.Closed notin stream.flags:
|
||||
stream.transp.close()
|
||||
|
@ -452,21 +649,27 @@ proc close*(api: DaemonAPI, stream: P2PStream) {.async.} =
|
|||
|
||||
proc close*(api: DaemonAPI) {.async.} =
|
||||
## Shutdown connections to `go-libp2p-daemon` control socket.
|
||||
await api.pool.close()
|
||||
# await api.pool.close()
|
||||
# Closing all pending servers.
|
||||
if len(api.servers) > 0:
|
||||
var pending = newSeq[Future[void]]()
|
||||
for server in api.servers:
|
||||
server.stop()
|
||||
server.close()
|
||||
pending.add(server.join())
|
||||
server.server.stop()
|
||||
server.server.close()
|
||||
pending.add(server.server.join())
|
||||
await all(pending)
|
||||
for server in api.servers:
|
||||
discard tryRemoveFile($(server.address))
|
||||
api.servers.setLen(0)
|
||||
# Closing daemon's process.
|
||||
if api.flags != {}:
|
||||
api.process.terminate()
|
||||
api.process.kill()
|
||||
discard api.process.waitForExit()
|
||||
# Waiting for logger loop to exit
|
||||
if not isNil(api.loggerFut):
|
||||
await api.loggerFut
|
||||
# Attempt to delete control socket endpoint.
|
||||
# if socketExists(api.sockname):
|
||||
# discard tryRemoveFile(api.sockname)
|
||||
if socketExists(api.sockname):
|
||||
discard tryRemoveFile(api.sockname)
|
||||
|
||||
template withMessage(m, body: untyped): untyped =
|
||||
let kind = m.checkResponse()
|
||||
|
@ -484,6 +687,8 @@ proc transactMessage(transp: StreamTransport,
|
|||
if res != length:
|
||||
raise newException(DaemonLocalError, "Could not send message to daemon!")
|
||||
var message = await transp.recvMessage()
|
||||
if len(message) == 0:
|
||||
raise newException(DaemonLocalError, "Incorrect or empty message received!")
|
||||
result = initProtoBuffer(message)
|
||||
|
||||
proc getPeerInfo(pb: var ProtoBuffer): PeerInfo =
|
||||
|
@ -495,12 +700,13 @@ proc getPeerInfo(pb: var ProtoBuffer): PeerInfo =
|
|||
var address = newSeq[byte]()
|
||||
while pb.getBytes(2, address) != -1:
|
||||
if len(address) != 0:
|
||||
result.addresses.add(address)
|
||||
var copyaddr = address
|
||||
result.addresses.add(MultiAddress.init(copyaddr))
|
||||
address.setLen(0)
|
||||
|
||||
proc identity*(api: DaemonAPI): Future[PeerInfo] {.async.} =
|
||||
## Get Node identity information
|
||||
var transp = await api.pool.acquire()
|
||||
var transp = await api.newConnection()
|
||||
try:
|
||||
var pb = await transactMessage(transp, requestIdentity())
|
||||
pb.withMessage() do:
|
||||
|
@ -508,55 +714,59 @@ proc identity*(api: DaemonAPI): Future[PeerInfo] {.async.} =
|
|||
if res == cast[int](ResponseType.IDENTITY):
|
||||
result = pb.getPeerInfo()
|
||||
finally:
|
||||
api.pool.release(transp)
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc connect*(api: DaemonAPI, peer: PeerID,
|
||||
addresses: seq[MultiAddress]) {.async.} =
|
||||
addresses: seq[MultiAddress],
|
||||
timeout = 0) {.async.} =
|
||||
## Connect to remote peer with id ``peer`` and addresses ``addresses``.
|
||||
var transp = await api.pool.acquire()
|
||||
var transp = await api.newConnection()
|
||||
try:
|
||||
var pb = await transp.transactMessage(requestConnect(peer, addresses))
|
||||
var pb = await transp.transactMessage(requestConnect(peer, addresses,
|
||||
timeout))
|
||||
pb.withMessage() do:
|
||||
discard
|
||||
finally:
|
||||
api.pool.release(transp)
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc disconnect*(api: DaemonAPI, peer: PeerID) {.async.} =
|
||||
## Disconnect from remote peer with id ``peer``.
|
||||
var transp = await api.pool.acquire()
|
||||
var transp = await api.newConnection()
|
||||
try:
|
||||
var pb = await transp.transactMessage(requestDisconnect(peer))
|
||||
pb.withMessage() do:
|
||||
discard
|
||||
finally:
|
||||
api.pool.release(transp)
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc openStream*(api: DaemonAPI, peer: PeerID,
|
||||
protocols: seq[string]): Future[P2PStream] {.async.} =
|
||||
protocols: seq[string],
|
||||
timeout = 0): Future[P2PStream] {.async.} =
|
||||
## Open new stream to peer ``peer`` using one of the protocols in
|
||||
## ``protocols``. Returns ``StreamTransport`` for the stream.
|
||||
var transp = await connect(api.address)
|
||||
var transp = await api.newConnection()
|
||||
var stream = new P2PStream
|
||||
try:
|
||||
var pb = await transp.transactMessage(requestStreamOpen(peer, protocols))
|
||||
var pb = await transp.transactMessage(requestStreamOpen(peer, protocols,
|
||||
timeout))
|
||||
pb.withMessage() do:
|
||||
var res = pb.enterSubmessage()
|
||||
if res == cast[int](ResponseType.STREAMINFO):
|
||||
stream.peer = newSeq[byte]()
|
||||
stream.raddress = newSeq[byte]()
|
||||
var raddress = newSeq[byte]()
|
||||
stream.protocol = ""
|
||||
if pb.getLengthValue(1, stream.peer) == -1:
|
||||
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!")
|
||||
stream.raddress = MultiAddress.init(raddress)
|
||||
if pb.getLengthValue(3, stream.protocol) == -1:
|
||||
raise newException(DaemonLocalError, "Missing `proto` field!")
|
||||
stream.flags.incl(Outbound)
|
||||
stream.transp = transp
|
||||
result = stream
|
||||
except:
|
||||
transp.close()
|
||||
await transp.join()
|
||||
await api.closeConnection(transp)
|
||||
raise getCurrentException()
|
||||
|
||||
proc streamHandler(server: StreamServer, transp: StreamTransport) {.async.} =
|
||||
|
@ -565,12 +775,13 @@ proc streamHandler(server: StreamServer, transp: StreamTransport) {.async.} =
|
|||
var pb = initProtoBuffer(message)
|
||||
var stream = new P2PStream
|
||||
stream.peer = newSeq[byte]()
|
||||
stream.raddress = newSeq[byte]()
|
||||
var raddress = newSeq[byte]()
|
||||
stream.protocol = ""
|
||||
if pb.getLengthValue(1, stream.peer) == -1:
|
||||
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!")
|
||||
stream.raddress = MultiAddress.init(raddress)
|
||||
if pb.getLengthValue(3, stream.protocol) == -1:
|
||||
raise newException(DaemonLocalError, "Missing `proto` field!")
|
||||
stream.flags.incl(Inbound)
|
||||
|
@ -583,7 +794,7 @@ proc streamHandler(server: StreamServer, transp: StreamTransport) {.async.} =
|
|||
proc addHandler*(api: DaemonAPI, protocols: seq[string],
|
||||
handler: P2PStreamCallback) {.async.} =
|
||||
## Add stream handler ``handler`` for set of protocols ``protocols``.
|
||||
var transp = await api.pool.acquire()
|
||||
var transp = await api.newConnection()
|
||||
var sockname = api.pattern % [$api.ucounter]
|
||||
var localaddr = initTAddress(sockname)
|
||||
inc(api.ucounter)
|
||||
|
@ -595,7 +806,7 @@ proc addHandler*(api: DaemonAPI, protocols: seq[string],
|
|||
var pb = await transp.transactMessage(requestStreamHandler(sockname,
|
||||
protocols))
|
||||
pb.withMessage() do:
|
||||
api.servers.add(server)
|
||||
api.servers.add(P2PServer(server: server, address: localaddr))
|
||||
except:
|
||||
for item in protocols:
|
||||
api.handlers.del(item)
|
||||
|
@ -604,11 +815,11 @@ proc addHandler*(api: DaemonAPI, protocols: seq[string],
|
|||
await server.join()
|
||||
raise getCurrentException()
|
||||
finally:
|
||||
api.pool.release(transp)
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc listPeers*(api: DaemonAPI): Future[seq[PeerInfo]] {.async.} =
|
||||
## Get list of remote peers to which we are currently connected.
|
||||
var transp = await api.pool.acquire()
|
||||
var transp = await api.newConnection()
|
||||
try:
|
||||
var pb = await transp.transactMessage(requestListPeers())
|
||||
pb.withMessage() do:
|
||||
|
@ -623,38 +834,38 @@ proc listPeers*(api: DaemonAPI): Future[seq[PeerInfo]] {.async.} =
|
|||
pb.skipSubmessage()
|
||||
res = pb.enterSubmessage()
|
||||
finally:
|
||||
api.pool.release(transp)
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc cmTagPeer*(api: DaemonAPI, peer: PeerID, tag: string,
|
||||
weight: int) {.async.} =
|
||||
## Tag peer with id ``peer`` using ``tag`` and ``weight``.
|
||||
var transp = await api.pool.acquire()
|
||||
var transp = await api.newConnection()
|
||||
try:
|
||||
var pb = await transp.transactMessage(requestCMTagPeer(peer, tag, weight))
|
||||
withMessage(pb) do:
|
||||
discard
|
||||
finally:
|
||||
api.pool.release(transp)
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc cmUntagPeer*(api: DaemonAPI, peer: PeerID, tag: string) {.async.} =
|
||||
## Remove tag ``tag`` from peer with id ``peer``.
|
||||
var transp = await api.pool.acquire()
|
||||
var transp = await api.newConnection()
|
||||
try:
|
||||
var pb = await transp.transactMessage(requestCMUntagPeer(peer, tag))
|
||||
withMessage(pb) do:
|
||||
discard
|
||||
finally:
|
||||
api.pool.release(transp)
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc cmTrimPeers*(api: DaemonAPI) {.async.} =
|
||||
## Trim all connections.
|
||||
var transp = await api.pool.acquire()
|
||||
var transp = await api.newConnection()
|
||||
try:
|
||||
var pb = await transp.transactMessage(requestCMTrim())
|
||||
withMessage(pb) do:
|
||||
discard
|
||||
finally:
|
||||
api.pool.release(transp)
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc dhtGetSinglePeerInfo(pb: var ProtoBuffer): PeerInfo =
|
||||
if pb.enterSubmessage() == 2:
|
||||
|
@ -678,6 +889,11 @@ proc enterDhtMessage(pb: var ProtoBuffer, rt: DHTResponseType) {.inline.} =
|
|||
else:
|
||||
raise newException(DaemonLocalError, "Wrong message type!")
|
||||
|
||||
proc enterPsMessage(pb: var ProtoBuffer) {.inline.} =
|
||||
var res = pb.enterSubmessage()
|
||||
if res != cast[int](ResponseType.PUBSUB):
|
||||
raise newException(DaemonLocalError, "Wrong message type!")
|
||||
|
||||
proc getDhtMessageType(pb: var ProtoBuffer): DHTResponseType {.inline.} =
|
||||
var dtype: uint
|
||||
if pb.getVarintValue(1, dtype) == 0:
|
||||
|
@ -695,14 +911,14 @@ proc dhtFindPeer*(api: DaemonAPI, peer: PeerID,
|
|||
##
|
||||
## You can specify timeout for DHT request with ``timeout`` value. ``0`` value
|
||||
## means no timeout.
|
||||
var transp = await api.pool.acquire()
|
||||
var transp = await api.newConnection()
|
||||
try:
|
||||
var pb = await transp.transactMessage(requestDHTFindPeer(peer, timeout))
|
||||
withMessage(pb) do:
|
||||
pb.enterDhtMessage(DHTResponseType.VALUE)
|
||||
result = pb.dhtGetSinglePeerInfo()
|
||||
finally:
|
||||
api.pool.release(transp)
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc dhtGetPublicKey*(api: DaemonAPI, peer: PeerID,
|
||||
timeout = 0): Future[LibP2PPublicKey] {.async.} =
|
||||
|
@ -710,14 +926,14 @@ proc dhtGetPublicKey*(api: DaemonAPI, peer: PeerID,
|
|||
##
|
||||
## You can specify timeout for DHT request with ``timeout`` value. ``0`` value
|
||||
## means no timeout.
|
||||
var transp = await api.pool.acquire()
|
||||
var transp = await api.newConnection()
|
||||
try:
|
||||
var pb = await transp.transactMessage(requestDHTGetPublicKey(peer, timeout))
|
||||
withMessage(pb) do:
|
||||
pb.enterDhtMessage(DHTResponseType.VALUE)
|
||||
result = pb.dhtGetSingleValue()
|
||||
finally:
|
||||
api.pool.release(transp)
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc dhtGetValue*(api: DaemonAPI, key: string,
|
||||
timeout = 0): Future[seq[byte]] {.async.} =
|
||||
|
@ -725,14 +941,14 @@ proc dhtGetValue*(api: DaemonAPI, key: string,
|
|||
##
|
||||
## You can specify timeout for DHT request with ``timeout`` value. ``0`` value
|
||||
## means no timeout.
|
||||
var transp = await api.pool.acquire()
|
||||
var transp = await api.newConnection()
|
||||
try:
|
||||
var pb = await transp.transactMessage(requestDHTGetValue(key, timeout))
|
||||
withMessage(pb) do:
|
||||
pb.enterDhtMessage(DHTResponseType.VALUE)
|
||||
result = pb.dhtGetSingleValue()
|
||||
finally:
|
||||
api.pool.release(transp)
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc dhtPutValue*(api: DaemonAPI, key: string, value: seq[byte],
|
||||
timeout = 0) {.async.} =
|
||||
|
@ -740,27 +956,27 @@ proc dhtPutValue*(api: DaemonAPI, key: string, value: seq[byte],
|
|||
##
|
||||
## You can specify timeout for DHT request with ``timeout`` value. ``0`` value
|
||||
## means no timeout.
|
||||
var transp = await api.pool.acquire()
|
||||
var transp = await api.newConnection()
|
||||
try:
|
||||
var pb = await transp.transactMessage(requestDHTPutValue(key, value,
|
||||
timeout))
|
||||
withMessage(pb) do:
|
||||
discard
|
||||
finally:
|
||||
api.pool.release(transp)
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc dhtProvide*(api: DaemonAPI, cid: CID, timeout = 0) {.async.} =
|
||||
proc dhtProvide*(api: DaemonAPI, cid: Cid, timeout = 0) {.async.} =
|
||||
## Provide content with id ``cid``.
|
||||
##
|
||||
## You can specify timeout for DHT request with ``timeout`` value. ``0`` value
|
||||
## means no timeout.
|
||||
var transp = await api.pool.acquire()
|
||||
var transp = await api.newConnection()
|
||||
try:
|
||||
var pb = await transp.transactMessage(requestDHTProvide(cid, timeout))
|
||||
withMessage(pb) do:
|
||||
discard
|
||||
finally:
|
||||
api.pool.release(transp)
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc dhtFindPeersConnectedToPeer*(api: DaemonAPI, peer: PeerID,
|
||||
timeout = 0): Future[seq[PeerInfo]] {.async.} =
|
||||
|
@ -768,7 +984,7 @@ proc dhtFindPeersConnectedToPeer*(api: DaemonAPI, peer: PeerID,
|
|||
##
|
||||
## You can specify timeout for DHT request with ``timeout`` value. ``0`` value
|
||||
## means no timeout.
|
||||
var transp = await api.pool.acquire()
|
||||
var transp = await api.newConnection()
|
||||
var list = newSeq[PeerInfo]()
|
||||
try:
|
||||
let spb = requestDHTFindPeersConnectedToPeer(peer, timeout)
|
||||
|
@ -777,13 +993,15 @@ proc dhtFindPeersConnectedToPeer*(api: DaemonAPI, peer: PeerID,
|
|||
pb.enterDhtMessage(DHTResponseType.BEGIN)
|
||||
while true:
|
||||
var message = await transp.recvMessage()
|
||||
if len(message) == 0:
|
||||
break
|
||||
var cpb = initProtoBuffer(message)
|
||||
if cpb.getDhtMessageType() == DHTResponseType.END:
|
||||
break
|
||||
list.add(cpb.dhtGetSinglePeerInfo())
|
||||
result = list
|
||||
finally:
|
||||
api.pool.release(transp)
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc dhtGetClosestPeers*(api: DaemonAPI, key: string,
|
||||
timeout = 0): Future[seq[PeerID]] {.async.} =
|
||||
|
@ -791,7 +1009,7 @@ proc dhtGetClosestPeers*(api: DaemonAPI, key: string,
|
|||
##
|
||||
## You can specify timeout for DHT request with ``timeout`` value. ``0`` value
|
||||
## means no timeout.
|
||||
var transp = await api.pool.acquire()
|
||||
var transp = await api.newConnection()
|
||||
var list = newSeq[PeerID]()
|
||||
try:
|
||||
let spb = requestDHTGetClosestPeers(key, timeout)
|
||||
|
@ -800,21 +1018,23 @@ proc dhtGetClosestPeers*(api: DaemonAPI, key: string,
|
|||
pb.enterDhtMessage(DHTResponseType.BEGIN)
|
||||
while true:
|
||||
var message = await transp.recvMessage()
|
||||
if len(message) == 0:
|
||||
break
|
||||
var cpb = initProtoBuffer(message)
|
||||
if cpb.getDhtMessageType() == DHTResponseType.END:
|
||||
break
|
||||
list.add(cpb.dhtGetSingleValue())
|
||||
result = list
|
||||
finally:
|
||||
api.pool.release(transp)
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc dhtFindProviders*(api: DaemonAPI, cid: CID, count: uint32,
|
||||
proc dhtFindProviders*(api: DaemonAPI, cid: Cid, count: uint32,
|
||||
timeout = 0): Future[seq[PeerInfo]] {.async.} =
|
||||
## Get ``count`` providers for content with id ``cid``.
|
||||
##
|
||||
## You can specify timeout for DHT request with ``timeout`` value. ``0`` value
|
||||
## means no timeout.
|
||||
var transp = await api.pool.acquire()
|
||||
var transp = await api.newConnection()
|
||||
var list = newSeq[PeerInfo]()
|
||||
try:
|
||||
let spb = requestDHTFindProviders(cid, count, timeout)
|
||||
|
@ -823,13 +1043,15 @@ proc dhtFindProviders*(api: DaemonAPI, cid: CID, count: uint32,
|
|||
pb.enterDhtMessage(DHTResponseType.BEGIN)
|
||||
while true:
|
||||
var message = await transp.recvMessage()
|
||||
if len(message) == 0:
|
||||
break
|
||||
var cpb = initProtoBuffer(message)
|
||||
if cpb.getDhtMessageType() == DHTResponseType.END:
|
||||
break
|
||||
list.add(cpb.dhtGetSinglePeerInfo())
|
||||
result = list
|
||||
finally:
|
||||
api.pool.release(transp)
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc dhtSearchValue*(api: DaemonAPI, key: string,
|
||||
timeout = 0): Future[seq[seq[byte]]] {.async.} =
|
||||
|
@ -837,7 +1059,7 @@ proc dhtSearchValue*(api: DaemonAPI, key: string,
|
|||
##
|
||||
## You can specify timeout for DHT request with ``timeout`` value. ``0`` value
|
||||
## means no timeout.
|
||||
var transp = await api.pool.acquire()
|
||||
var transp = await api.newConnection()
|
||||
var list = newSeq[seq[byte]]()
|
||||
try:
|
||||
var pb = await transp.transactMessage(requestDHTSearchValue(key, timeout))
|
||||
|
@ -845,10 +1067,129 @@ proc dhtSearchValue*(api: DaemonAPI, key: string,
|
|||
pb.enterDhtMessage(DHTResponseType.BEGIN)
|
||||
while true:
|
||||
var message = await transp.recvMessage()
|
||||
if len(message) == 0:
|
||||
break
|
||||
var cpb = initProtoBuffer(message)
|
||||
if cpb.getDhtMessageType() == DHTResponseType.END:
|
||||
break
|
||||
list.add(cpb.dhtGetSingleValue())
|
||||
result = list
|
||||
finally:
|
||||
api.pool.release(transp)
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc pubsubGetTopics*(api: DaemonAPI): Future[seq[string]] {.async.} =
|
||||
## Get list of topics this node is subscribed to.
|
||||
var transp = await api.newConnection()
|
||||
try:
|
||||
var pb = await transp.transactMessage(requestPSGetTopics())
|
||||
withMessage(pb) do:
|
||||
pb.enterPsMessage()
|
||||
var topics = newSeq[string]()
|
||||
var topic = ""
|
||||
while pb.getString(1, topic) != -1:
|
||||
topics.add(topic)
|
||||
topic.setLen(0)
|
||||
result = topics
|
||||
finally:
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc pubsubListPeers*(api: DaemonAPI,
|
||||
topic: string): Future[seq[PeerID]] {.async.} =
|
||||
## Get list of peers we are connected to and which also subscribed to topic
|
||||
## ``topic``.
|
||||
var transp = await api.newConnection()
|
||||
try:
|
||||
var pb = await transp.transactMessage(requestPSListPeers(topic))
|
||||
withMessage(pb) do:
|
||||
pb.enterPsMessage()
|
||||
var peers = newSeq[PeerID]()
|
||||
var peer = newSeq[byte]()
|
||||
while pb.getBytes(2, peer) != -1:
|
||||
peers.add(peer)
|
||||
peer.setLen(0)
|
||||
result = peers
|
||||
finally:
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc pubsubPublish*(api: DaemonAPI, topic: string,
|
||||
value: seq[byte]) {.async.} =
|
||||
## Get list of peer identifiers which are subscribed to topic ``topic``.
|
||||
var transp = await api.newConnection()
|
||||
try:
|
||||
var pb = await transp.transactMessage(requestPSPublish(topic, value))
|
||||
withMessage(pb) do:
|
||||
discard
|
||||
finally:
|
||||
await api.closeConnection(transp)
|
||||
|
||||
proc getPubsubMessage*(pb: var ProtoBuffer): PubSubMessage =
|
||||
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)
|
||||
|
||||
proc pubsubLoop(api: DaemonAPI, ticket: PubsubTicket) {.async.} =
|
||||
while true:
|
||||
var pbmessage = await ticket.transp.recvMessage()
|
||||
if len(pbmessage) == 0:
|
||||
break
|
||||
var pb = initProtoBuffer(pbmessage)
|
||||
var message = pb.getPubsubMessage()
|
||||
## We can do here `await` too
|
||||
let res = await ticket.handler(api, ticket, message)
|
||||
if not res:
|
||||
ticket.transp.close()
|
||||
await ticket.transp.join()
|
||||
break
|
||||
|
||||
proc pubsubSubscribe*(api: DaemonAPI, topic: string,
|
||||
handler: P2PPubSubCallback): Future[PubsubTicket] {.async.} =
|
||||
## Subscribe to topic ``topic``.
|
||||
var transp = await api.newConnection()
|
||||
try:
|
||||
var pb = await transp.transactMessage(requestPSSubscribe(topic))
|
||||
pb.withMessage() do:
|
||||
var ticket = new PubsubTicket
|
||||
ticket.topic = topic
|
||||
ticket.handler = handler
|
||||
ticket.transp = transp
|
||||
asyncCheck pubsubLoop(api, ticket)
|
||||
result = ticket
|
||||
except:
|
||||
await api.closeConnection(transp)
|
||||
raise getCurrentException()
|
||||
|
||||
proc `$`*(pinfo: PeerInfo): string =
|
||||
## Get string representation of ``PeerInfo`` object.
|
||||
result = newStringOfCap(128)
|
||||
result.add("{PeerID: '")
|
||||
result.add(Base58.encode(pinfo.peer))
|
||||
result.add("' Addresses: [")
|
||||
let length = len(pinfo.addresses)
|
||||
for i in 0..<length:
|
||||
result.add("'")
|
||||
result.add($pinfo.addresses[i])
|
||||
result.add("'")
|
||||
if i < length - 1:
|
||||
result.add(", ")
|
||||
result.add("]}")
|
||||
if len(pinfo.addresses) > 0:
|
||||
result = result
|
||||
|
|
|
@ -52,11 +52,11 @@ proc newPool*(address: TransportAddress, poolsize: int = DefaultPoolSize,
|
|||
): Future[TransportPool] {.async.} =
|
||||
## Establish pool of connections to address ``address`` with size
|
||||
## ``poolsize``.
|
||||
result = new TransportPool
|
||||
result.bufferSize = bufferSize
|
||||
result.transports = newSeq[PoolItem](poolsize)
|
||||
var pool = new TransportPool
|
||||
pool.bufferSize = bufferSize
|
||||
pool.transports = newSeq[PoolItem](poolsize)
|
||||
var conns = newSeq[Future[StreamTransport]](poolsize)
|
||||
result.state = Connecting
|
||||
pool.state = Connecting
|
||||
for i in 0..<poolsize:
|
||||
conns[i] = connect(address, bufferSize)
|
||||
# Waiting for all connections to be established.
|
||||
|
@ -68,10 +68,11 @@ proc newPool*(address: TransportAddress, poolsize: int = DefaultPoolSize,
|
|||
else:
|
||||
let transp = conns[i].read()
|
||||
let item = PoolItem(transp: transp)
|
||||
result.transports[i] = item
|
||||
pool.transports[i] = item
|
||||
# Setup available connections event
|
||||
result.event = newAsyncEvent()
|
||||
result.state = Connected
|
||||
pool.event = newAsyncEvent()
|
||||
pool.state = Connected
|
||||
result = pool
|
||||
|
||||
proc acquire*(pool: TransportPool): Future[StreamTransport] {.async.} =
|
||||
## Acquire non-busy connection from pool ``pool``.
|
||||
|
|
|
@ -0,0 +1,674 @@
|
|||
## 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 multicodec, multihash, multibase, transcoder, base58, base32, vbuffer
|
||||
|
||||
{.deadCodeElim:on.}
|
||||
|
||||
type
|
||||
MAKind* = enum
|
||||
None, Fixed, Length, Path, Marker
|
||||
|
||||
MAProtocol* = object
|
||||
mcodec*: MultiCodec
|
||||
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 >= 0) and (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)
|
||||
var mh: MultiHash
|
||||
if MultiHash.decode(data, mh) >= 0:
|
||||
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:
|
||||
var mh: MultiHash
|
||||
if MultiHash.decode(address, mh) >= 0:
|
||||
s = Base58.encode(address)
|
||||
result = true
|
||||
|
||||
proc p2pVB(vb: var VBuffer): bool =
|
||||
## P2P address validateBuffer() implementation.
|
||||
var address = newSeq[byte]()
|
||||
if vb.readSeq(address) > 0:
|
||||
var mh: MultiHash
|
||||
if MultiHash.decode(address, mh) >= 0:
|
||||
result = true
|
||||
|
||||
proc onionStB(s: string, vb: var VBuffer): bool =
|
||||
try:
|
||||
var parts = s.split(':')
|
||||
if len(parts) != 2:
|
||||
return false
|
||||
if len(parts[0]) != 16:
|
||||
return false
|
||||
var address = Base32Lower.decode(parts[0].toLowerAscii())
|
||||
var nport = parseInt(parts[1])
|
||||
if (nport > 0 and nport < 65536) and len(address) == 10:
|
||||
address.setLen(12)
|
||||
address[10] = cast[byte]((nport shr 8) and 0xFF)
|
||||
address[11] = cast[byte](nport and 0xFF)
|
||||
vb.writeArray(address)
|
||||
result = true
|
||||
except:
|
||||
discard
|
||||
|
||||
proc onionBtS(vb: var VBuffer, s: var string): bool =
|
||||
## ONION address bufferToString() implementation.
|
||||
var buf: array[12, byte]
|
||||
if vb.readArray(buf) == 12:
|
||||
var nport = (cast[uint16](buf[10]) shl 8) or cast[uint16](buf[11])
|
||||
s = Base32Lower.encode(buf.toOpenArray(0, 9))
|
||||
s.add(":")
|
||||
s.add($nport)
|
||||
result = true
|
||||
|
||||
proc onionVB(vb: var VBuffer): bool =
|
||||
## ONION address validateBuffer() implementation.
|
||||
var buf: array[12, byte]
|
||||
if vb.readArray(buf) == 12:
|
||||
result = true
|
||||
|
||||
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(
|
||||
mcodec: multiCodec("ip4"), kind: Fixed, size: 4,
|
||||
coder: TranscoderIP4
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("tcp"), kind: Fixed, size: 2,
|
||||
coder: TranscoderPort
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("udp"), kind: Fixed, size: 2,
|
||||
coder: TranscoderPort
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("ip6"), kind: Fixed, size: 16,
|
||||
coder: TranscoderIP6
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("dccp"), kind: Fixed, size: 2,
|
||||
coder: TranscoderPort
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("sctp"), kind: Fixed, size: 2,
|
||||
coder: TranscoderPort
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("udt"), kind: Marker, size: 0
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("utp"), kind: Marker, size: 0
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("http"), kind: Marker, size: 0
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("https"), kind: Marker, size: 0
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("quic"), kind: Marker, size: 0
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("ip6zone"), kind: Length, size: 0,
|
||||
coder: TranscoderIP6Zone
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("onion"), kind: Fixed, size: 10,
|
||||
coder: TranscoderOnion
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("ws"), kind: Marker, size: 0
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("ipfs"), kind: Length, size: 0,
|
||||
coder: TranscoderP2P
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("p2p"), kind: Length, size: 0,
|
||||
coder: TranscoderP2P
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("unix"), kind: Path, size: 0,
|
||||
coder: TranscoderUnix
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("dns4"), kind: Length, size: 0,
|
||||
coder: TranscoderDNS
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("dns6"), kind: Length, size: 0,
|
||||
coder: TranscoderDNS
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("dnsaddr"), kind: Length, size: 0,
|
||||
coder: TranscoderDNS
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("p2p-circuit"), kind: Marker, size: 0
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("p2p-websocket-star"), kind: Marker, size: 0
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("p2p-webrtc-star"), kind: Marker, size: 0
|
||||
),
|
||||
MAProtocol(
|
||||
mcodec: multiCodec("p2p-webrtc-direct"), kind: Marker, size: 0
|
||||
)
|
||||
]
|
||||
|
||||
proc initMultiAddressCodeTable(): Table[MultiCodec,
|
||||
MAProtocol] {.compileTime.} =
|
||||
result = initTable[MultiCodec, MAProtocol]()
|
||||
for item in ProtocolsList:
|
||||
result[item.mcodec] = item
|
||||
|
||||
const
|
||||
CodeAddresses = initMultiAddressCodeTable()
|
||||
|
||||
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 shcopy*(m1: var MultiAddress, m2: MultiAddress) =
|
||||
shallowCopy(m1.data.buffer, m2.data.buffer)
|
||||
m1.data.offset = m2.data.offset
|
||||
m1.data.length = m2.data.length
|
||||
|
||||
proc protoCode*(ma: MultiAddress): MultiCodec =
|
||||
## Returns MultiAddress ``ma`` protocol code.
|
||||
var header: uint64
|
||||
var vb: MultiAddress
|
||||
shcopy(vb, ma)
|
||||
if vb.data.readVarint(header) == -1:
|
||||
raise newException(MultiAddressError, "Malformed binary address!")
|
||||
let proto = CodeAddresses.getOrDefault(MultiCodec(header))
|
||||
if proto.kind == None:
|
||||
raise newException(MultiAddressError,
|
||||
"Unsupported protocol '" & $header & "'")
|
||||
result = proto.mcodec
|
||||
|
||||
proc protoName*(ma: MultiAddress): string =
|
||||
## Returns MultiAddress ``ma`` protocol name.
|
||||
var header: uint64
|
||||
var vb: MultiAddress
|
||||
shcopy(vb, ma)
|
||||
if vb.data.readVarint(header) == -1:
|
||||
raise newException(MultiAddressError, "Malformed binary address!")
|
||||
let proto = CodeAddresses.getOrDefault(MultiCodec(header))
|
||||
if proto.kind == None:
|
||||
raise newException(MultiAddressError,
|
||||
"Unsupported protocol '" & $header & "'")
|
||||
result = $(proto.mcodec)
|
||||
|
||||
proc protoArgument*(ma: MultiAddress, value: var openarray[byte]): int =
|
||||
## Returns MultiAddress ``ma`` protocol argument value.
|
||||
##
|
||||
## If current MultiAddress do not have argument value, then result will be
|
||||
## ``0``.
|
||||
var header: uint64
|
||||
var vb: MultiAddress
|
||||
var buffer: seq[byte]
|
||||
shcopy(vb, ma)
|
||||
if vb.data.readVarint(header) == -1:
|
||||
raise newException(MultiAddressError, "Malformed binary address!")
|
||||
let proto = CodeAddresses.getOrDefault(MultiCodec(header))
|
||||
if proto.kind == None:
|
||||
raise newException(MultiAddressError,
|
||||
"Unsupported protocol '" & $header & "'")
|
||||
if proto.kind == Fixed:
|
||||
result = proto.size
|
||||
if len(value) >= result:
|
||||
if vb.data.readArray(value) != proto.size:
|
||||
raise newException(MultiAddressError, "Decoding protocol error")
|
||||
elif proto.kind in {Length, Path}:
|
||||
if vb.data.readSeq(buffer) == -1:
|
||||
raise newException(MultiAddressError, "Decoding protocol error")
|
||||
result = len(vb.data.buffer)
|
||||
if len(value) >= result:
|
||||
copyMem(addr value[0], addr vb.data.buffer[0], result)
|
||||
|
||||
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(MultiCodec(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.} =
|
||||
## Returns part with index ``i`` of MultiAddress ``ma``.
|
||||
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(MultiCodec(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(MultiCodec(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.mcodec) & "' coder")
|
||||
if not proto.coder.bufferToString(vb.data, part):
|
||||
raise newException(MultiAddressError, "Decoding protocol error")
|
||||
parts.add($(proto.mcodec))
|
||||
parts.add(part)
|
||||
elif proto.kind == Marker:
|
||||
parts.add($(proto.mcodec))
|
||||
if len(parts) > 0:
|
||||
result = "/" & parts.join("/")
|
||||
|
||||
proc hex*(value: MultiAddress): string =
|
||||
## Return hexadecimal string representation of MultiAddress ``value``.
|
||||
result = $(value.data)
|
||||
|
||||
proc write*(vb: var VBuffer, ma: MultiAddress) {.inline.} =
|
||||
## Write MultiAddress value ``ma`` to buffer ``vb``.
|
||||
vb.writeArray(ma.data.buffer)
|
||||
|
||||
proc encode*(mbtype: typedesc[MultiBase], encoding: string,
|
||||
ma: MultiAddress): string {.inline.} =
|
||||
## Get MultiBase encoded representation of ``ma`` using encoding
|
||||
## ``encoding``.
|
||||
result = MultiBase.encode(encoding, ma.data.buffer)
|
||||
|
||||
proc validate*(ma: MultiAddress): bool =
|
||||
## Returns ``true`` if MultiAddress ``ma`` is valid.
|
||||
var header: uint64
|
||||
var vb: MultiAddress
|
||||
shcopy(vb, ma)
|
||||
while true:
|
||||
if vb.data.isEmpty():
|
||||
break
|
||||
if vb.data.readVarint(header) == -1:
|
||||
return false
|
||||
let proto = CodeAddresses.getOrDefault(MultiCodec(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 getProtocol(name: string): MAProtocol {.inline.} =
|
||||
let mc = MultiCodec.codec(name)
|
||||
if mc != InvalidMultiCodec:
|
||||
result = CodeAddresses.getOrDefault(mc)
|
||||
|
||||
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 = getProtocol(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.write(proto.mcodec)
|
||||
let res = proto.coder.stringToBuffer(parts[offset + 1], result.data)
|
||||
if not res:
|
||||
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.write(proto.mcodec)
|
||||
if not proto.coder.stringToBuffer(path, result.data):
|
||||
raise newException(MultiAddressError,
|
||||
"Error encoding `$1/$2`" % [part, path])
|
||||
break
|
||||
elif proto.kind == Marker:
|
||||
result.data.write(proto.mcodec)
|
||||
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,429 @@
|
|||
## 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 MultiBase.
|
||||
##
|
||||
## TODO:
|
||||
## 1. base64
|
||||
## 2. base32z
|
||||
import tables, strutils
|
||||
import base32, base58
|
||||
|
||||
type
|
||||
MultibaseStatus* {.pure.} = enum
|
||||
Error, Success, Overrun, Incorrect, BadCodec, NotSupported
|
||||
|
||||
MultiBase* = object
|
||||
|
||||
MBDecoder = proc(inbytes: openarray[char],
|
||||
outbytes: var openarray[byte],
|
||||
outlen: var int): MultibaseStatus {.nimcall.}
|
||||
MBEncoder = proc(inbytes: openarray[byte],
|
||||
outbytes: var openarray[char],
|
||||
outlen: var int): MultibaseStatus {.nimcall.}
|
||||
MBCodeSize = proc(length: int): int {.nimcall.}
|
||||
|
||||
MBCodec = object
|
||||
code: char
|
||||
name: string
|
||||
encr: MBEncoder
|
||||
decr: proc(inbytes: openarray[char],
|
||||
outbytes: var openarray[byte],
|
||||
outlen: var int): MultibaseStatus {.nimcall.}
|
||||
encl: MBCodeSize
|
||||
decl: MBCodeSize
|
||||
|
||||
MultiBaseError* = object of Exception
|
||||
|
||||
proc idd(inbytes: openarray[char], outbytes: var openarray[byte],
|
||||
outlen: var int): MultibaseStatus =
|
||||
let length = len(inbytes)
|
||||
if length > len(outbytes):
|
||||
outlen = length
|
||||
result = MultibaseStatus.Overrun
|
||||
else:
|
||||
copyMem(addr outbytes[0], unsafeAddr inbytes[0], length)
|
||||
outlen = length
|
||||
result = MultibaseStatus.Success
|
||||
|
||||
proc ide(inbytes: openarray[byte],
|
||||
outbytes: var openarray[char],
|
||||
outlen: var int): MultibaseStatus =
|
||||
let length = len(inbytes)
|
||||
if length > len(outbytes):
|
||||
outlen = length
|
||||
result = MultibaseStatus.Overrun
|
||||
else:
|
||||
copyMem(addr outbytes[0], unsafeAddr inbytes[0], length)
|
||||
outlen = length
|
||||
result = MultibaseStatus.Success
|
||||
|
||||
proc idel(length: int): int = length
|
||||
proc iddl(length: int): int = length
|
||||
|
||||
proc b16d(inbytes: openarray[char],
|
||||
outbytes: var openarray[byte],
|
||||
outlen: var int): MultibaseStatus =
|
||||
discard
|
||||
|
||||
proc b16e(inbytes: openarray[byte],
|
||||
outbytes: var openarray[char],
|
||||
outlen: var int): MultibaseStatus =
|
||||
discard
|
||||
|
||||
proc b16ud(inbytes: openarray[char],
|
||||
outbytes: var openarray[byte],
|
||||
outlen: var int): MultibaseStatus =
|
||||
discard
|
||||
|
||||
proc b16ue(inbytes: openarray[byte],
|
||||
outbytes: var openarray[char],
|
||||
outlen: var int): MultibaseStatus =
|
||||
discard
|
||||
|
||||
proc b16el(length: int): int = length shl 1
|
||||
proc b16dl(length: int): int = (length + 1) div 2
|
||||
|
||||
proc b32ce(r: Base32Status): MultibaseStatus {.inline.} =
|
||||
result = MultibaseStatus.Error
|
||||
if r == Base32Status.Incorrect:
|
||||
result = MultibaseStatus.Incorrect
|
||||
elif r == Base32Status.Overrun:
|
||||
result = MultibaseStatus.Overrun
|
||||
elif r == Base32Status.Success:
|
||||
result = MultibaseStatus.Success
|
||||
|
||||
proc b58ce(r: Base58Status): MultibaseStatus {.inline.} =
|
||||
result = MultibaseStatus.Error
|
||||
if r == Base58Status.Incorrect:
|
||||
result = MultibaseStatus.Incorrect
|
||||
elif r == Base58Status.Overrun:
|
||||
result = MultibaseStatus.Overrun
|
||||
elif r == Base58Status.Success:
|
||||
result = MultibaseStatus.Success
|
||||
|
||||
proc b32hd(inbytes: openarray[char],
|
||||
outbytes: var openarray[byte],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b32ce(HexBase32Lower.decode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b32he(inbytes: openarray[byte],
|
||||
outbytes: var openarray[char],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b32ce(HexBase32Lower.encode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b32hud(inbytes: openarray[char],
|
||||
outbytes: var openarray[byte],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b32ce(HexBase32Upper.decode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b32hue(inbytes: openarray[byte],
|
||||
outbytes: var openarray[char],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b32ce(HexBase32Upper.encode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b32hpd(inbytes: openarray[char],
|
||||
outbytes: var openarray[byte],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b32ce(HexBase32LowerPad.decode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b32hpe(inbytes: openarray[byte],
|
||||
outbytes: var openarray[char],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b32ce(HexBase32LowerPad.encode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b32hpud(inbytes: openarray[char],
|
||||
outbytes: var openarray[byte],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b32ce(HexBase32UpperPad.decode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b32hpue(inbytes: openarray[byte],
|
||||
outbytes: var openarray[char],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b32ce(HexBase32UpperPad.encode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b32d(inbytes: openarray[char],
|
||||
outbytes: var openarray[byte],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b32ce(Base32Lower.decode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b32e(inbytes: openarray[byte],
|
||||
outbytes: var openarray[char],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b32ce(Base32Lower.encode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b32ud(inbytes: openarray[char],
|
||||
outbytes: var openarray[byte],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b32ce(Base32Upper.decode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b32ue(inbytes: openarray[byte],
|
||||
outbytes: var openarray[char],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b32ce(Base32Upper.encode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b32pd(inbytes: openarray[char],
|
||||
outbytes: var openarray[byte],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b32ce(Base32LowerPad.decode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b32pe(inbytes: openarray[byte],
|
||||
outbytes: var openarray[char],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b32ce(Base32LowerPad.encode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b32pud(inbytes: openarray[char],
|
||||
outbytes: var openarray[byte],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b32ce(Base32UpperPad.decode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b32pue(inbytes: openarray[byte],
|
||||
outbytes: var openarray[char],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b32ce(Base32UpperPad.encode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b32el(length: int): int = Base32Lower.encodedLength(length)
|
||||
proc b32dl(length: int): int = Base32Lower.decodedLength(length)
|
||||
proc b32pel(length: int): int = Base32LowerPad.encodedLength(length)
|
||||
proc b32pdl(length: int): int = Base32LowerPad.decodedLength(length)
|
||||
|
||||
proc b58fd(inbytes: openarray[char],
|
||||
outbytes: var openarray[byte],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b58ce(FLCBase58.decode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b58fe(inbytes: openarray[byte],
|
||||
outbytes: var openarray[char],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b58ce(FLCBase58.encode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b58bd(inbytes: openarray[char],
|
||||
outbytes: var openarray[byte],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b58ce(BTCBase58.decode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b58be(inbytes: openarray[byte],
|
||||
outbytes: var openarray[char],
|
||||
outlen: var int): MultibaseStatus =
|
||||
result = b58ce(BTCBase58.encode(inbytes, outbytes, outlen))
|
||||
|
||||
proc b58el(length: int): int = Base58.encodedLength(length)
|
||||
proc b58dl(length: int): int = Base58.decodedLength(length)
|
||||
|
||||
const
|
||||
MultibaseCodecs = [
|
||||
MBCodec(name: "identity", code: chr(0x00),
|
||||
decr: idd, encr: ide, decl: iddl, encl: idel
|
||||
),
|
||||
MBCodec(name: "base1", code: '1'),
|
||||
MBCodec(name: "base2", code: '0'),
|
||||
MBCodec(name: "base8", code: '7'),
|
||||
MBCodec(name: "base10", code: '9'),
|
||||
MBCodec(name: "base16", code: 'f',
|
||||
decr: b16d, encr: b16e, decl: b16dl, encl: b16el
|
||||
),
|
||||
MBCodec(name: "base16upper", code: 'F',
|
||||
decr: b16ud, encr: b16ue, decl: b16dl, encl: b16el
|
||||
),
|
||||
MBCodec(name: "base32hex", code: 'v',
|
||||
decr: b32hd, encr: b32he, decl: b32dl, encl: b32el
|
||||
),
|
||||
MBCodec(name: "base32hexupper", code: 'V',
|
||||
decr: b32hud, encr: b32hue, decl: b32dl, encl: b32el
|
||||
),
|
||||
MBCodec(name: "base32hexpad", code: 't',
|
||||
decr: b32hpd, encr: b32hpe, decl: b32pdl, encl: b32pel
|
||||
),
|
||||
MBCodec(name: "base32hexpadupper", code: 'T',
|
||||
decr: b32hpud, encr: b32hpue, decl: b32pdl, encl: b32pel
|
||||
),
|
||||
MBCodec(name: "base32", code: 'b',
|
||||
decr: b32d, encr: b32e, decl: b32dl, encl: b32el
|
||||
),
|
||||
MBCodec(name: "base32upper", code: 'B',
|
||||
decr: b32ud, encr: b32ue, decl: b32dl, encl: b32el
|
||||
),
|
||||
MBCodec(name: "base32pad", code: 'c',
|
||||
decr: b32pd, encr: b32pe, decl: b32pdl, encl: b32pel
|
||||
),
|
||||
MBCodec(name: "base32padupper", code: 'C',
|
||||
decr: b32pud, encr: b32pue, decl: b32pdl, encl: b32pel
|
||||
),
|
||||
MBCodec(name: "base32z", code: 'h'),
|
||||
MBCodec(name: "base58flickr", code: 'Z',
|
||||
decr: b58fd, encr: b58fe, decl: b58dl, encl: b58el
|
||||
),
|
||||
MBCodec(name: "base58btc", code: 'z',
|
||||
decr: b58bd, encr: b58be, decl: b58dl, encl: b58el
|
||||
),
|
||||
MBCodec(name: "base64", code: 'm'),
|
||||
MBCodec(name: "base64pad", code: 'M'),
|
||||
MBCodec(name: "base64url", code: 'u'),
|
||||
MBCodec(name: "base64urlpad", code: 'U')
|
||||
]
|
||||
|
||||
proc initMultiBaseCodeTable(): Table[char, MBCodec] {.compileTime.} =
|
||||
result = initTable[char, MBCodec]()
|
||||
for item in MultibaseCodecs:
|
||||
result[item.code] = item
|
||||
|
||||
proc initMultiBaseNameTable(): Table[string, MBCodec] {.compileTime.} =
|
||||
result = initTable[string, MBCodec]()
|
||||
for item in MultibaseCodecs:
|
||||
result[item.name] = item
|
||||
|
||||
const
|
||||
CodeMultibases = initMultiBaseCodeTable()
|
||||
NameMultibases = initMultiBaseNameTable()
|
||||
|
||||
proc encodedLength*(mbtype: typedesc[MultiBase], encoding: string,
|
||||
length: int): int =
|
||||
## Return estimated size of buffer to store MultiBase encoded value with
|
||||
## encoding ``encoding`` of length ``length``.
|
||||
##
|
||||
## Procedure returns ``-1`` if ``encoding`` scheme is not supported or
|
||||
## not present.
|
||||
let mb = NameMultibases.getOrDefault(encoding)
|
||||
if len(mb.name) == 0 or isNil(mb.encl):
|
||||
result = -1
|
||||
else:
|
||||
if length == 0:
|
||||
result = 1
|
||||
else:
|
||||
result = mb.encl(length) + 1
|
||||
|
||||
proc decodedLength*(mbtype: typedesc[MultiBase], encoding: char,
|
||||
length: int): int =
|
||||
## Return estimated size of buffer to store MultiBase decoded value with
|
||||
## encoding character ``encoding`` of length ``length``.
|
||||
let mb = CodeMultibases.getOrDefault(encoding)
|
||||
if len(mb.name) == 0 or isNil(mb.decl) or length == 0:
|
||||
result = -1
|
||||
else:
|
||||
if length == 1:
|
||||
result = 0
|
||||
else:
|
||||
result = mb.decl(length - 1)
|
||||
|
||||
proc encode*(mbtype: typedesc[MultiBase], encoding: string,
|
||||
inbytes: openarray[byte], outbytes: var openarray[char],
|
||||
outlen: var int): MultibaseStatus =
|
||||
## Encode array ``inbytes`` using MultiBase encoding scheme ``encoding`` and
|
||||
## store encoded value to ``outbytes``.
|
||||
##
|
||||
## If ``encoding`` is not supported ``MultiBaseStatus.NotSupported`` will be
|
||||
## returned.
|
||||
##
|
||||
## If ``encoding`` is not correct string, then ``MultBaseStatus.BadCodec``
|
||||
## will be returned.
|
||||
##
|
||||
## If length of ``outbytes`` is not enough to store encoded result, then
|
||||
## ``MultiBaseStatus.Overrun`` error will be returned and ``outlen`` will be
|
||||
## set to length required.
|
||||
##
|
||||
## On successfull encoding ``MultiBaseStatus.Success`` will be returned and
|
||||
## ``outlen`` will be set to number of encoded octets (bytes).
|
||||
let mb = NameMultibases.getOrDefault(encoding)
|
||||
if len(mb.name) == 0:
|
||||
return MultibaseStatus.BadCodec
|
||||
if isNil(mb.encr) or isNil(mb.encl):
|
||||
return MultibaseStatus.NotSupported
|
||||
if len(outbytes) > 1:
|
||||
result = mb.encr(inbytes, outbytes.toOpenArray(1, len(outbytes) - 1),
|
||||
outlen)
|
||||
if result == MultiBaseStatus.Overrun:
|
||||
outlen += 1
|
||||
elif result == MultiBaseStatus.Success:
|
||||
outlen += 1
|
||||
outbytes[0] = mb.code
|
||||
else:
|
||||
if len(inbytes) == 0 and len(outbytes) == 1:
|
||||
result = MultiBaseStatus.Success
|
||||
outlen = 1
|
||||
outbytes[0] = mb.code
|
||||
else:
|
||||
result = MultiBaseStatus.Overrun
|
||||
outlen = mb.encl(len(inbytes)) + 1
|
||||
|
||||
proc decode*(mbtype: typedesc[MultiBase], inbytes: openarray[char],
|
||||
outbytes: var openarray[byte], outlen: var int): MultibaseStatus =
|
||||
## Decode array ``inbytes`` using MultiBase encoding and store decoded value
|
||||
## to ``outbytes``.
|
||||
##
|
||||
## If ``inbytes`` is not correct MultiBase string, then
|
||||
## ``MultiBaseStatus.BadCodec`` if first character is wrong, or
|
||||
## ``MultiBaseStatus.Incorrect`` if string has incorrect characters for
|
||||
## such encoding.
|
||||
##
|
||||
## If length of ``outbytes`` is not enough to store decoded result, then
|
||||
## ``MultiBaseStatus.Overrun`` error will be returned and ``outlen`` will be
|
||||
## set to length required.
|
||||
##
|
||||
## On successfull decoding ``MultiBaseStatus.Success`` will be returned and
|
||||
## ``outlen`` will be set to number of encoded octets (bytes).
|
||||
let length = len(inbytes)
|
||||
if length == 0:
|
||||
return MultibaseStatus.Incorrect
|
||||
let mb = CodeMultibases.getOrDefault(inbytes[0])
|
||||
if len(mb.name) == 0:
|
||||
return MultibaseStatus.BadCodec
|
||||
if isNil(mb.decr) or isNil(mb.decl):
|
||||
return MultibaseStatus.NotSupported
|
||||
if length == 1:
|
||||
outlen = 0
|
||||
result = MultibaseStatus.Success
|
||||
else:
|
||||
result = mb.decr(inbytes.toOpenArray(1, length - 1), outbytes, outlen)
|
||||
|
||||
proc encode*(mbtype: typedesc[MultiBase], encoding: string,
|
||||
inbytes: openarray[byte]): string =
|
||||
## Encode array ``inbytes`` using MultiBase encoding scheme ``encoding`` and
|
||||
## return encoded string.
|
||||
let length = len(inbytes)
|
||||
let mb = NameMultibases.getOrDefault(encoding)
|
||||
if len(mb.name) == 0:
|
||||
raise newException(MultiBaseError, "Encoding scheme is incorrect!")
|
||||
if isNil(mb.encr) or isNil(mb.encl):
|
||||
raise newException(MultiBaseError, "Encoding scheme is not supported!")
|
||||
var buffer: string
|
||||
if length > 0:
|
||||
buffer = newString(mb.encl(length) + 1)
|
||||
var outlen = 0
|
||||
let res = mb.encr(inbytes, buffer.toOpenArray(1, len(buffer) - 1), outlen)
|
||||
if res != MultiBaseStatus.Success:
|
||||
raise newException(MultiBaseError, "Encoding error [" & $res & "]")
|
||||
buffer.setLen(outlen + 1)
|
||||
buffer[0] = mb.code
|
||||
else:
|
||||
buffer = newString(1)
|
||||
buffer[0] = mb.code
|
||||
result = buffer
|
||||
|
||||
proc decode*(mbtype: typedesc[MultiBase], inbytes: openarray[char]): seq[byte] =
|
||||
## Decode MultiBase encoded array ``inbytes`` and return decoded sequence of
|
||||
## bytes.
|
||||
let length = len(inbytes)
|
||||
if length == 0:
|
||||
raise newException(MultiBaseError, "Could not decode zero-length string")
|
||||
let mb = CodeMultibases.getOrDefault(inbytes[0])
|
||||
if len(mb.name) == 0:
|
||||
raise newException(MultiBaseError, "MultiBase scheme is incorrect!")
|
||||
if isNil(mb.decr) or isNil(mb.decl):
|
||||
raise newException(MultiBaseError, "MultiBase scheme is not supported!")
|
||||
if length == 1:
|
||||
result = newSeq[byte]()
|
||||
else:
|
||||
var buffer = newSeq[byte](mb.decl(length - 1))
|
||||
var outlen = 0
|
||||
let res = mb.decr(inbytes.toOpenArray(1, length - 1),
|
||||
buffer, outlen)
|
||||
if res != MultiBaseStatus.Success:
|
||||
raise newException(MultiBaseError, "Decoding error [" & $res & "]")
|
||||
result = buffer
|
||||
result.setLen(outlen)
|
|
@ -0,0 +1,316 @@
|
|||
## 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 MultiCodec.
|
||||
import tables, hashes
|
||||
import varint, vbuffer
|
||||
|
||||
{.deadCodeElim: on.}
|
||||
|
||||
## List of officially supported codecs can BE found here
|
||||
## https://github.com/multiformats/multicodec/blob/master/table.csv
|
||||
const MultiCodecList = [
|
||||
("raw", 0x55),
|
||||
# serialization formats
|
||||
("cbor", 0x51),
|
||||
("protobuf", 0x50),
|
||||
("rlp", 0x60),
|
||||
("bencode", 0x63),
|
||||
# multiformats
|
||||
("multicodec", 0x30),
|
||||
("multihash", 0x31),
|
||||
("multiaddr", 0x32),
|
||||
("multibase", 0x33),
|
||||
# multihashes
|
||||
("identity", 0x00),
|
||||
("md4", 0xD4),
|
||||
("md5", 0xD5),
|
||||
("sha1", 0x11),
|
||||
("sha2-256", 0x12),
|
||||
("sha2-512", 0x13),
|
||||
("dbl-sha2-256", 0x56),
|
||||
("sha3-224", 0x17),
|
||||
("sha3-256", 0x16),
|
||||
("sha3-384", 0x15),
|
||||
("sha3-512", 0x14),
|
||||
("shake-128", 0x18),
|
||||
("shake-256", 0x19),
|
||||
("keccak-224", 0x1A),
|
||||
("keccak-256", 0x1B),
|
||||
("keccak-384", 0x1C),
|
||||
("keccak-512", 0x1D),
|
||||
("murmur3", 0x22),
|
||||
("blake2b-8", 0xB201), ("blake2b-16", 0xB202), ("blake2b-24", 0xB203),
|
||||
("blake2b-32", 0xB204), ("blake2b-40", 0xB205), ("blake2b-48", 0xB206),
|
||||
("blake2b-56", 0xB207), ("blake2b-64", 0xB208), ("blake2b-72", 0xB209),
|
||||
("blake2b-80", 0xB20A), ("blake2b-88", 0xB20B), ("blake2b-96", 0xB20C),
|
||||
("blake2b-104", 0xB20D), ("blake2b-112", 0xB20E), ("blake2b-120", 0xB20F),
|
||||
("blake2b-128", 0xB210), ("blake2b-136", 0xB211), ("blake2b-144", 0xB212),
|
||||
("blake2b-152", 0xB213), ("blake2b-160", 0xB214), ("blake2b-168", 0xB215),
|
||||
("blake2b-176", 0xB216), ("blake2b-184", 0xB217), ("blake2b-192", 0xB218),
|
||||
("blake2b-200", 0xB219), ("blake2b-208", 0xB21A), ("blake2b-216", 0xB21B),
|
||||
("blake2b-224", 0xB21C), ("blake2b-232", 0xB21D), ("blake2b-240", 0xB21E),
|
||||
("blake2b-248", 0xB21F), ("blake2b-256", 0xB220), ("blake2b-264", 0xB221),
|
||||
("blake2b-272", 0xB222), ("blake2b-280", 0xB223), ("blake2b-288", 0xB224),
|
||||
("blake2b-296", 0xB225), ("blake2b-304", 0xB226), ("blake2b-312", 0xB227),
|
||||
("blake2b-320", 0xB228), ("blake2b-328", 0xB229), ("blake2b-336", 0xB22A),
|
||||
("blake2b-344", 0xB22B), ("blake2b-352", 0xB22C), ("blake2b-360", 0xB22D),
|
||||
("blake2b-368", 0xB22E), ("blake2b-376", 0xB22F), ("blake2b-384", 0xB230),
|
||||
("blake2b-392", 0xB231), ("blake2b-400", 0xB232), ("blake2b-408", 0xB233),
|
||||
("blake2b-416", 0xB234), ("blake2b-424", 0xB235), ("blake2b-432", 0xB236),
|
||||
("blake2b-440", 0xB237), ("blake2b-448", 0xB238), ("blake2b-456", 0xB239),
|
||||
("blake2b-464", 0xB23A), ("blake2b-472", 0xB23B), ("blake2b-480", 0xB23C),
|
||||
("blake2b-488", 0xB23D), ("blake2b-496", 0xB23E), ("blake2b-504", 0xB23F),
|
||||
("blake2b-512", 0xB240), ("blake2s-8", 0xB241), ("blake2s-16", 0xB242),
|
||||
("blake2s-24", 0xB243), ("blake2s-32", 0xB244), ("blake2s-40", 0xB245),
|
||||
("blake2s-48", 0xB246), ("blake2s-56", 0xB247), ("blake2s-64", 0xB248),
|
||||
("blake2s-72", 0xB249), ("blake2s-80", 0xB24A), ("blake2s-88", 0xB24B),
|
||||
("blake2s-96", 0xB24C), ("blake2s-104", 0xB24D), ("blake2s-112", 0xB24E),
|
||||
("blake2s-120", 0xB24F), ("blake2s-128", 0xB250), ("blake2s-136", 0xB251),
|
||||
("blake2s-144", 0xB252), ("blake2s-152", 0xB253), ("blake2s-160", 0xB254),
|
||||
("blake2s-168", 0xB255), ("blake2s-176", 0xB256), ("blake2s-184", 0xB257),
|
||||
("blake2s-192", 0xB258), ("blake2s-200", 0xB259), ("blake2s-208", 0xB25A),
|
||||
("blake2s-216", 0xB25B), ("blake2s-224", 0xB25C), ("blake2s-232", 0xB25D),
|
||||
("blake2s-240", 0xB25E), ("blake2s-248", 0xB25F), ("blake2s-256", 0xB260),
|
||||
("skein256-8", 0xB301), ("skein256-16", 0xB302), ("skein256-24", 0xB303),
|
||||
("skein256-32", 0xB304), ("skein256-40", 0xB305), ("skein256-48", 0xB306),
|
||||
("skein256-56", 0xB307), ("skein256-64", 0xB308), ("skein256-72", 0xB309),
|
||||
("skein256-80", 0xB30A), ("skein256-88", 0xB30B), ("skein256-96", 0xB30C),
|
||||
("skein256-104", 0xB30D), ("skein256-112", 0xB30E), ("skein256-120", 0xB30F),
|
||||
("skein256-128", 0xB310), ("skein256-136", 0xB311), ("skein256-144", 0xB312),
|
||||
("skein256-152", 0xB313), ("skein256-160", 0xB314), ("skein256-168", 0xB315),
|
||||
("skein256-176", 0xB316), ("skein256-184", 0xB317), ("skein256-192", 0xB318),
|
||||
("skein256-200", 0xB319), ("skein256-208", 0xB31A), ("skein256-216", 0xB31B),
|
||||
("skein256-224", 0xB31C), ("skein256-232", 0xB31D), ("skein256-240", 0xB31E),
|
||||
("skein256-248", 0xB31F), ("skein256-256", 0xB320),
|
||||
("skein512-8", 0xB321), ("skein512-16", 0xB322), ("skein512-24", 0xB323),
|
||||
("skein512-32", 0xB324), ("skein512-40", 0xB325), ("skein512-48", 0xB326),
|
||||
("skein512-56", 0xB327), ("skein512-64", 0xB328), ("skein512-72", 0xB329),
|
||||
("skein512-80", 0xB32A), ("skein512-88", 0xB32B), ("skein512-96", 0xB32C),
|
||||
("skein512-104", 0xB32D), ("skein512-112", 0xB32E), ("skein512-120", 0xB32F),
|
||||
("skein512-128", 0xB330), ("skein512-136", 0xB331), ("skein512-144", 0xB332),
|
||||
("skein512-152", 0xB333), ("skein512-160", 0xB334), ("skein512-168", 0xB335),
|
||||
("skein512-176", 0xB336), ("skein512-184", 0xB337), ("skein512-192", 0xB338),
|
||||
("skein512-200", 0xB339), ("skein512-208", 0xB33A), ("skein512-216", 0xB33B),
|
||||
("skein512-224", 0xB33C), ("skein512-232", 0xB33D), ("skein512-240", 0xB33E),
|
||||
("skein512-248", 0xB33F), ("skein512-256", 0xB340), ("skein512-264", 0xB341),
|
||||
("skein512-272", 0xB342), ("skein512-280", 0xB343), ("skein512-288", 0xB344),
|
||||
("skein512-296", 0xB345), ("skein512-304", 0xB346), ("skein512-312", 0xB347),
|
||||
("skein512-320", 0xB348), ("skein512-328", 0xB349), ("skein512-336", 0xB34A),
|
||||
("skein512-344", 0xB34B), ("skein512-352", 0xB34C), ("skein512-360", 0xB34D),
|
||||
("skein512-368", 0xB34E), ("skein512-376", 0xB34F), ("skein512-384", 0xB350),
|
||||
("skein512-392", 0xB351), ("skein512-400", 0xB352), ("skein512-408", 0xB353),
|
||||
("skein512-416", 0xB354), ("skein512-424", 0xB355), ("skein512-432", 0xB356),
|
||||
("skein512-440", 0xB357), ("skein512-448", 0xB358), ("skein512-456", 0xB359),
|
||||
("skein512-464", 0xB35A), ("skein512-472", 0xB35B), ("skein512-480", 0xB35C),
|
||||
("skein512-488", 0xB35D), ("skein512-496", 0xB35E), ("skein512-504", 0xB35F),
|
||||
("skein512-512", 0xB360), ("skein1024-8", 0xB361), ("skein1024-16", 0xB362),
|
||||
("skein1024-24", 0xB363), ("skein1024-32", 0xB364), ("skein1024-40", 0xB365),
|
||||
("skein1024-48", 0xB366), ("skein1024-56", 0xB367), ("skein1024-64", 0xB368),
|
||||
("skein1024-72", 0xB369), ("skein1024-80", 0xB36A), ("skein1024-88", 0xB36B),
|
||||
("skein1024-96", 0xB36C), ("skein1024-104", 0xB36D),
|
||||
("skein1024-112", 0xB36E), ("skein1024-120", 0xB36F),
|
||||
("skein1024-128", 0xB370), ("skein1024-136", 0xB371),
|
||||
("skein1024-144", 0xB372), ("skein1024-152", 0xB373),
|
||||
("skein1024-160", 0xB374), ("skein1024-168", 0xB375),
|
||||
("skein1024-176", 0xB376), ("skein1024-184", 0xB377),
|
||||
("skein1024-192", 0xB378), ("skein1024-200", 0xB379),
|
||||
("skein1024-208", 0xB37A), ("skein1024-216", 0xB37B),
|
||||
("skein1024-224", 0xB37C), ("skein1024-232", 0xB37D),
|
||||
("skein1024-240", 0xB37E), ("skein1024-248", 0xB37F),
|
||||
("skein1024-256", 0xB380), ("skein1024-264", 0xB381),
|
||||
("skein1024-272", 0xB382), ("skein1024-280", 0xB383),
|
||||
("skein1024-288", 0xB384), ("skein1024-296", 0xB385),
|
||||
("skein1024-304", 0xB386), ("skein1024-312", 0xB387),
|
||||
("skein1024-320", 0xB388), ("skein1024-328", 0xB389),
|
||||
("skein1024-336", 0xB38A), ("skein1024-344", 0xB38B),
|
||||
("skein1024-352", 0xB38C), ("skein1024-360", 0xB38D),
|
||||
("skein1024-368", 0xB38E), ("skein1024-376", 0xB38F),
|
||||
("skein1024-384", 0xB390), ("skein1024-392", 0xB391),
|
||||
("skein1024-400", 0xB392), ("skein1024-408", 0xB393),
|
||||
("skein1024-416", 0xB394), ("skein1024-424", 0xB395),
|
||||
("skein1024-432", 0xB396), ("skein1024-440", 0xB397),
|
||||
("skein1024-448", 0xB398), ("skein1024-456", 0xB399),
|
||||
("skein1024-464", 0xB39A), ("skein1024-472", 0xB39B),
|
||||
("skein1024-480", 0xB39C), ("skein1024-488", 0xB39D),
|
||||
("skein1024-496", 0xB39E), ("skein1024-504", 0xB39F),
|
||||
("skein1024-512", 0xB3A0), ("skein1024-520", 0xB3A1),
|
||||
("skein1024-528", 0xB3A2), ("skein1024-536", 0xB3A3),
|
||||
("skein1024-544", 0xB3A4), ("skein1024-552", 0xB3A5),
|
||||
("skein1024-560", 0xB3A6), ("skein1024-568", 0xB3A7),
|
||||
("skein1024-576", 0xB3A8), ("skein1024-584", 0xB3A9),
|
||||
("skein1024-592", 0xB3AA), ("skein1024-600", 0xB3AB),
|
||||
("skein1024-608", 0xB3AC), ("skein1024-616", 0xB3AD),
|
||||
("skein1024-624", 0xB3AE), ("skein1024-632", 0xB3AF),
|
||||
("skein1024-640", 0xB3B0), ("skein1024-648", 0xB3B1),
|
||||
("skein1024-656", 0xB3B2), ("skein1024-664", 0xB3B3),
|
||||
("skein1024-672", 0xB3B4), ("skein1024-680", 0xB3B5),
|
||||
("skein1024-688", 0xB3B6), ("skein1024-696", 0xB3B7),
|
||||
("skein1024-704", 0xB3B8), ("skein1024-712", 0xB3B9),
|
||||
("skein1024-720", 0xB3BA), ("skein1024-728", 0xB3BB),
|
||||
("skein1024-736", 0xB3BC), ("skein1024-744", 0xB3BD),
|
||||
("skein1024-752", 0xB3BE), ("skein1024-760", 0xB3BF),
|
||||
("skein1024-768", 0xB3C0), ("skein1024-776", 0xB3C1),
|
||||
("skein1024-784", 0xB3C2), ("skein1024-792", 0xB3C3),
|
||||
("skein1024-800", 0xB3C4), ("skein1024-808", 0xB3C5),
|
||||
("skein1024-816", 0xB3C6), ("skein1024-824", 0xB3C7),
|
||||
("skein1024-832", 0xB3C8), ("skein1024-840", 0xB3C9),
|
||||
("skein1024-848", 0xB3CA), ("skein1024-856", 0xB3CB),
|
||||
("skein1024-864", 0xB3CC), ("skein1024-872", 0xB3CD),
|
||||
("skein1024-880", 0xB3CE), ("skein1024-888", 0xB3CF),
|
||||
("skein1024-896", 0xB3D0), ("skein1024-904", 0xB3D1),
|
||||
("skein1024-912", 0xB3D2), ("skein1024-920", 0xB3D3),
|
||||
("skein1024-928", 0xB3D4), ("skein1024-936", 0xB3D5),
|
||||
("skein1024-944", 0xB3D6), ("skein1024-952", 0xB3D7),
|
||||
("skein1024-960", 0xB3D8), ("skein1024-968", 0xB3D9),
|
||||
("skein1024-976", 0xB3DA), ("skein1024-984", 0xB3DB),
|
||||
("skein1024-992", 0xB3DC), ("skein1024-1000", 0xB3DD),
|
||||
("skein1024-1008", 0xB3DE), ("skein1024-1016", 0xB3DF),
|
||||
("skein1024-1024", 0xB3E0),
|
||||
# multiaddrs
|
||||
("ip4", 0x04),
|
||||
("ip6", 0x29),
|
||||
("ip6zone", 0x2A),
|
||||
("tcp", 0x06),
|
||||
("udp", 0x0111),
|
||||
("dccp", 0x21),
|
||||
("sctp", 0x84),
|
||||
("udt", 0x012D),
|
||||
("utp", 0x012E),
|
||||
("unix", 0x0190), # not in multicodec list
|
||||
("ipfs", 0x01A5),
|
||||
("p2p", 0x01A5),
|
||||
("http", 0x01E0),
|
||||
("https", 0x01BB),
|
||||
("quic", 0x01CC),
|
||||
("ws", 0x01DD),
|
||||
("wss", 0x01DE), # not in multicodec list
|
||||
("p2p-websocket-star", 0x01DF), # not in multicodec list
|
||||
("p2p-webrtc-star", 0x0113), # not in multicodec list
|
||||
("p2p-webrtc-direct", 0x0114), # not in multicodec list
|
||||
("onion", 0x01BC),
|
||||
("p2p-circuit", 0x0122),
|
||||
("dns4", 0x36),
|
||||
("dns6", 0x37),
|
||||
("dnsaddr", 0x38),
|
||||
# IPLD formats
|
||||
("dag-pb", 0x70),
|
||||
("dag-cbor", 0x71),
|
||||
("dag-json", 0x129),
|
||||
("git-raw", 0x78),
|
||||
("eth-block", 0x90),
|
||||
("eth-block-list", 0x91),
|
||||
("eth-tx-trie", 0x92),
|
||||
("eth-tx", 0x93),
|
||||
("eth-tx-receipt-trie", 0x94),
|
||||
("eth-tx-receipt", 0x95),
|
||||
("eth-state-trie", 0x96),
|
||||
("eth-account-snapshot", 0x97),
|
||||
("eth-storage-trie", 0x98),
|
||||
("bitcoin-block", 0xB0),
|
||||
("bitcoin-tx", 0xB1),
|
||||
("zcash-block", 0xC0),
|
||||
("zcash-tx", 0xC1),
|
||||
("stellar-block", 0xD0),
|
||||
("stellar-tx", 0xD1),
|
||||
("decred-block", 0xE0),
|
||||
("decred-tx", 0xE1),
|
||||
("dash-block", 0xF0),
|
||||
("dash-tx", 0xF1),
|
||||
("torrent-info", 0x7B),
|
||||
("torrent-file", 0x7C),
|
||||
("ed25519-pub", 0xED)
|
||||
]
|
||||
|
||||
type
|
||||
MultiCodec* = distinct int
|
||||
MultiCodecError* = object of Exception
|
||||
|
||||
const
|
||||
InvalidMultiCodec* = MultiCodec(-1)
|
||||
|
||||
proc initMultiCodecNameTable(): Table[string, int] {.compileTime.} =
|
||||
result = initTable[string, int]()
|
||||
for item in MultiCodecList:
|
||||
result[item[0]] = item[1]
|
||||
|
||||
proc initMultiCodecCodeTable(): Table[int, string] {.compileTime.} =
|
||||
result = initTable[int, string]()
|
||||
for item in MultiCodecList:
|
||||
result[item[1]] = item[0]
|
||||
|
||||
const
|
||||
CodeCodecs = initMultiCodecCodeTable()
|
||||
NameCodecs = initMultiCodecNameTable()
|
||||
|
||||
proc multiCodec*(name: string): MultiCodec {.compileTime.} =
|
||||
## Generate MultiCodec from string ``name`` at compile time.
|
||||
var code = NameCodecs.getOrDefault(name, -1)
|
||||
if code == -1:
|
||||
raise newException(MultiCodecError,
|
||||
"MultiCodec `" & name & "` not supported!")
|
||||
result = MultiCodec(code)
|
||||
|
||||
proc multiCodec*(code: int): MultiCodec {.compileTime.} =
|
||||
## Generate MultiCodec from integer ``code`` at compile time.
|
||||
var name = CodeCodecs.getOrDefault(code, "")
|
||||
if name == "":
|
||||
raise newException(MultiCodecError,
|
||||
"MultiCodec with code " & $code & " not supported!")
|
||||
result = MultiCodec(code)
|
||||
|
||||
proc `$`*(mc: MultiCodec): string =
|
||||
## Returns string representation of MultiCodec ``mc``.
|
||||
result = CodeCodecs.getOrDefault(int(mc), "")
|
||||
if result == "":
|
||||
raise newException(MultiCodecError,
|
||||
"MultiCodec with code " & $int(mc) & " not supported!")
|
||||
|
||||
proc `==`*(mc: MultiCodec, name: string): bool {.inline.} =
|
||||
## Compares MultiCodec ``mc`` with string ``name``.
|
||||
var mcname = CodeCodecs.getOrDefault(int(mc), "")
|
||||
if mcname == "":
|
||||
return false
|
||||
result = (mcname == name)
|
||||
|
||||
proc `==`*(mc: MultiCodec, code: int): bool {.inline.} =
|
||||
## Compares MultiCodec ``mc`` with integer ``code``.
|
||||
result = (int(mc) == code)
|
||||
|
||||
proc `==`*(a, b: MultiCodec): bool =
|
||||
## Returns ``true`` if MultiCodecs ``a`` and ``b`` are equal.
|
||||
int(a) == int(b)
|
||||
|
||||
proc `!=`*(a, b: MultiCodec): bool =
|
||||
## Returns ``true`` if MultiCodecs ``a`` and ``b`` are not equal.
|
||||
int(a) != int(b)
|
||||
|
||||
proc hash*(m: MultiCodec): Hash {.inline.} =
|
||||
## Hash procedure for tables.
|
||||
result = hash(int(m))
|
||||
|
||||
proc codec*(mt: typedesc[MultiCodec], name: string): MultiCodec {.inline.} =
|
||||
## Return MultiCodec from string representation ``name``.
|
||||
## If ``name`` is not valid multicodec name, then ``InvalidMultiCodec`` will
|
||||
## be returned.
|
||||
result = MultiCodec(NameCodecs.getOrDefault(name, -1))
|
||||
|
||||
proc codec*(mt: typedesc[MultiCodec], code: int): MultiCodec {.inline.} =
|
||||
## Return MultiCodec from integer representation ``code``.
|
||||
## If ``code`` is not valid multicodec code, then ``InvalidMultiCodec`` will
|
||||
## be returned.
|
||||
let res = CodeCodecs.getOrDefault(code, "")
|
||||
if res == "":
|
||||
result = InvalidMultiCodec
|
||||
else:
|
||||
result = MultiCodec(code)
|
||||
|
||||
proc write*(vb: var VBuffer, mc: MultiCodec) {.inline.} =
|
||||
## Write MultiCodec to buffer ``vb``.
|
||||
vb.writeVarint(cast[uint](mc))
|
|
@ -0,0 +1,550 @@
|
|||
## 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 MultiHash.
|
||||
## Supported hashes are:
|
||||
## 1. IDENTITY
|
||||
## 2. SHA2-256/SHA2-512
|
||||
## 3. DBL-SHA2-256
|
||||
## 4. SHA3/KECCAK
|
||||
## 5. SHAKE-128/SHAKE-256
|
||||
## 6. BLAKE2s/BLAKE2s
|
||||
##
|
||||
## Hashes which are not yet supported
|
||||
## 1. SHA1
|
||||
## 2. SKEIN
|
||||
## 3. MURMUR
|
||||
import tables
|
||||
import nimcrypto/[sha2, keccak, blake2, hash, utils]
|
||||
import varint, vbuffer, base58, multicodec, multibase
|
||||
|
||||
const
|
||||
MaxHashSize* = 128
|
||||
|
||||
type
|
||||
MHashCoderProc* = proc(data: openarray[byte],
|
||||
output: var openarray[byte]) {.nimcall, gcsafe.}
|
||||
MHash* = object
|
||||
mcodec*: MultiCodec
|
||||
size*: int
|
||||
coder*: MHashCoderProc
|
||||
|
||||
MultiHash* = object
|
||||
data*: VBuffer
|
||||
mcodec*: MultiCodec
|
||||
size*: int
|
||||
dpos*: int
|
||||
|
||||
MultiHashError* = object of Exception
|
||||
|
||||
proc identhash(data: openarray[byte], output: var openarray[byte]) =
|
||||
if len(output) > 0:
|
||||
var length = if len(data) > len(output): len(output)
|
||||
else: len(data)
|
||||
copyMem(addr output[0], unsafeAddr data[0], length)
|
||||
|
||||
proc dblsha2_256hash(data: openarray[byte], output: var openarray[byte]) =
|
||||
if len(output) > 0:
|
||||
var digest1 = sha256.digest(data)
|
||||
var digest2 = sha256.digest(digest1.data)
|
||||
var length = if sha256.sizeDigest > len(output): len(output)
|
||||
else: sha256.sizeDigest
|
||||
copyMem(addr output[0], addr digest2.data[0], length)
|
||||
|
||||
proc blake2Bhash(data: openarray[byte], output: var openarray[byte]) =
|
||||
if len(output) > 0:
|
||||
var digest = blake2_512.digest(data)
|
||||
var length = if blake2_512.sizeDigest > len(output): len(output)
|
||||
else: blake2_512.sizeDigest
|
||||
copyMem(addr output[0], addr digest.data[0], length)
|
||||
|
||||
proc blake2Shash(data: openarray[byte], output: var openarray[byte]) =
|
||||
if len(output) > 0:
|
||||
var digest = blake2_256.digest(data)
|
||||
var length = if blake2_256.sizeDigest > len(output): len(output)
|
||||
else: blake2_256.sizeDigest
|
||||
copyMem(addr output[0], addr digest.data[0], length)
|
||||
|
||||
proc sha2_256hash(data: openarray[byte], output: var openarray[byte]) =
|
||||
if len(output) > 0:
|
||||
var digest = sha256.digest(data)
|
||||
var length = if sha256.sizeDigest > len(output): len(output)
|
||||
else: sha256.sizeDigest
|
||||
copyMem(addr output[0], addr digest.data[0], length)
|
||||
|
||||
proc sha2_512hash(data: openarray[byte], output: var openarray[byte]) =
|
||||
if len(output) > 0:
|
||||
var digest = sha512.digest(data)
|
||||
var length = if sha512.sizeDigest > len(output): len(output)
|
||||
else: sha512.sizeDigest
|
||||
copyMem(addr output[0], addr digest.data[0], length)
|
||||
|
||||
proc sha3_224hash(data: openarray[byte], output: var openarray[byte]) =
|
||||
if len(output) > 0:
|
||||
var digest = sha3_224.digest(data)
|
||||
var length = if sha3_224.sizeDigest > len(output): len(output)
|
||||
else: sha3_224.sizeDigest
|
||||
copyMem(addr output[0], addr digest.data[0], length)
|
||||
|
||||
proc sha3_256hash(data: openarray[byte], output: var openarray[byte]) =
|
||||
if len(output) > 0:
|
||||
var digest = sha3_256.digest(data)
|
||||
var length = if sha3_256.sizeDigest > len(output): len(output)
|
||||
else: sha3_256.sizeDigest
|
||||
copyMem(addr output[0], addr digest.data[0], length)
|
||||
|
||||
proc sha3_384hash(data: openarray[byte], output: var openarray[byte]) =
|
||||
if len(output) > 0:
|
||||
var digest = sha3_384.digest(data)
|
||||
var length = if sha3_384.sizeDigest > len(output): len(output)
|
||||
else: sha3_384.sizeDigest
|
||||
copyMem(addr output[0], addr digest.data[0], length)
|
||||
|
||||
proc sha3_512hash(data: openarray[byte], output: var openarray[byte]) =
|
||||
if len(output) > 0:
|
||||
var digest = sha3_512.digest(data)
|
||||
var length = if sha3_512.sizeDigest > len(output): len(output)
|
||||
else: sha3_512.sizeDigest
|
||||
copyMem(addr output[0], addr digest.data[0], length)
|
||||
|
||||
proc keccak_224hash(data: openarray[byte], output: var openarray[byte]) =
|
||||
if len(output) > 0:
|
||||
var digest = keccak224.digest(data)
|
||||
var length = if keccak224.sizeDigest > len(output): len(output)
|
||||
else: keccak224.sizeDigest
|
||||
copyMem(addr output[0], addr digest.data[0], length)
|
||||
|
||||
proc keccak_256hash(data: openarray[byte], output: var openarray[byte]) =
|
||||
if len(output) > 0:
|
||||
var digest = keccak256.digest(data)
|
||||
var length = if keccak256.sizeDigest > len(output): len(output)
|
||||
else: keccak256.sizeDigest
|
||||
copyMem(addr output[0], addr digest.data[0], length)
|
||||
|
||||
proc keccak_384hash(data: openarray[byte], output: var openarray[byte]) =
|
||||
if len(output) > 0:
|
||||
var digest = keccak384.digest(data)
|
||||
var length = if keccak384.sizeDigest > len(output): len(output)
|
||||
else: keccak384.sizeDigest
|
||||
copyMem(addr output[0], addr digest.data[0], length)
|
||||
|
||||
proc keccak_512hash(data: openarray[byte], output: var openarray[byte]) =
|
||||
if len(output) > 0:
|
||||
var digest = keccak512.digest(data)
|
||||
var length = if keccak512.sizeDigest > len(output): len(output)
|
||||
else: keccak512.sizeDigest
|
||||
copyMem(addr output[0], addr digest.data[0], length)
|
||||
|
||||
proc shake_128hash(data: openarray[byte], output: var openarray[byte]) =
|
||||
var sctx: shake128
|
||||
if len(output) > 0:
|
||||
sctx.init()
|
||||
sctx.update(cast[ptr uint8](unsafeAddr data[0]), uint(len(data)))
|
||||
sctx.xof()
|
||||
discard sctx.output(addr output[0], uint(len(output)))
|
||||
sctx.clear()
|
||||
|
||||
proc shake_256hash(data: openarray[byte], output: var openarray[byte]) =
|
||||
var sctx: shake256
|
||||
if len(output) > 0:
|
||||
sctx.init()
|
||||
sctx.update(cast[ptr uint8](unsafeAddr data[0]), uint(len(data)))
|
||||
sctx.xof()
|
||||
discard sctx.output(addr output[0], uint(len(output)))
|
||||
sctx.clear()
|
||||
|
||||
const
|
||||
HashesList = [
|
||||
MHash(mcodec: multiCodec("identity"), size: 0,
|
||||
coder: identhash),
|
||||
MHash(mcodec: multiCodec("dbl-sha2-256"), size: sha256.sizeDigest,
|
||||
coder: dblsha2_256hash
|
||||
),
|
||||
MHash(mcodec: multiCodec("sha2-256"), size: sha256.sizeDigest,
|
||||
coder: sha2_256hash
|
||||
),
|
||||
MHash(mcodec: multiCodec("sha2-512"), size: sha512.sizeDigest,
|
||||
coder: sha2_512hash
|
||||
),
|
||||
MHash(mcodec: multiCodec("sha3-224"), size: sha3_224.sizeDigest,
|
||||
coder: sha3_224hash
|
||||
),
|
||||
MHash(mcodec: multiCodec("sha3-256"), size: sha3_256.sizeDigest,
|
||||
coder: sha3_256hash
|
||||
),
|
||||
MHash(mcodec: multiCodec("sha3-384"), size: sha3_384.sizeDigest,
|
||||
coder: sha3_384hash
|
||||
),
|
||||
MHash(mcodec: multiCodec("sha3-512"), size: sha3_512.sizeDigest,
|
||||
coder: sha3_512hash
|
||||
),
|
||||
MHash(mcodec: multiCodec("shake-128"), size: 32, coder: shake_128hash),
|
||||
MHash(mcodec: multiCodec("shake-256"), size: 64, coder: shake_256hash),
|
||||
MHash(mcodec: multiCodec("keccak-224"), size: keccak_224.sizeDigest,
|
||||
coder: keccak_224hash
|
||||
),
|
||||
MHash(mcodec: multiCodec("keccak-256"), size: keccak_256.sizeDigest,
|
||||
coder: keccak_256hash
|
||||
),
|
||||
MHash(mcodec: multiCodec("keccak-384"), size: keccak_384.sizeDigest,
|
||||
coder: keccak_384hash
|
||||
),
|
||||
MHash(mcodec: multiCodec("keccak-512"), size: keccak_512.sizeDigest,
|
||||
coder: keccak_512hash
|
||||
),
|
||||
MHash(mcodec: multiCodec("blake2b-8"), size: 1, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-16"), size: 2, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-24"), size: 3, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-32"), size: 4, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-40"), size: 5, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-48"), size: 6, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-56"), size: 7, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-64"), size: 8, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-72"), size: 9, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-80"), size: 10, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-88"), size: 11, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-96"), size: 12, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-104"), size: 13, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-112"), size: 14, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-120"), size: 15, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-128"), size: 16, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-136"), size: 17, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-144"), size: 18, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-152"), size: 19, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-160"), size: 20, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-168"), size: 21, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-176"), size: 22, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-184"), size: 23, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-192"), size: 24, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-200"), size: 25, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-208"), size: 26, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-216"), size: 27, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-224"), size: 28, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-232"), size: 29, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-240"), size: 30, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-248"), size: 31, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-256"), size: 32, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-264"), size: 33, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-272"), size: 34, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-280"), size: 35, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-288"), size: 36, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-296"), size: 37, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-304"), size: 38, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-312"), size: 39, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-320"), size: 40, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-328"), size: 41, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-336"), size: 42, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-344"), size: 43, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-352"), size: 44, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-360"), size: 45, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-368"), size: 46, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-376"), size: 47, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-384"), size: 48, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-392"), size: 49, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-400"), size: 50, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-408"), size: 51, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-416"), size: 52, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-424"), size: 53, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-432"), size: 54, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-440"), size: 55, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-448"), size: 56, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-456"), size: 57, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-464"), size: 58, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-472"), size: 59, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-480"), size: 60, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-488"), size: 61, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-496"), size: 62, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-504"), size: 63, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2b-512"), size: 64, coder: blake2Bhash),
|
||||
MHash(mcodec: multiCodec("blake2s-8"), size: 1, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-16"), size: 2, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-24"), size: 3, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-32"), size: 4, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-40"), size: 5, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-48"), size: 6, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-56"), size: 7, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-64"), size: 8, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-72"), size: 9, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-80"), size: 10, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-88"), size: 11, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-96"), size: 12, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-104"), size: 13, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-112"), size: 14, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-120"), size: 15, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-128"), size: 16, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-136"), size: 17, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-144"), size: 18, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-152"), size: 19, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-160"), size: 20, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-168"), size: 21, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-176"), size: 22, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-184"), size: 23, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-192"), size: 24, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-200"), size: 25, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-208"), size: 26, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-216"), size: 27, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-224"), size: 28, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-232"), size: 29, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-240"), size: 30, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-248"), size: 31, coder: blake2Shash),
|
||||
MHash(mcodec: multiCodec("blake2s-256"), size: 32, coder: blake2Shash)
|
||||
]
|
||||
|
||||
proc initMultiHashCodeTable(): Table[MultiCodec, MHash] {.compileTime.} =
|
||||
result = initTable[MultiCodec, MHash]()
|
||||
for item in HashesList:
|
||||
result[item.mcodec] = item
|
||||
|
||||
const
|
||||
CodeHashes = initMultiHashCodeTable()
|
||||
|
||||
proc digestImplWithHash(hash: MHash, data: openarray[byte]): MultiHash =
|
||||
var buffer: array[MaxHashSize, byte]
|
||||
result.data = initVBuffer()
|
||||
result.mcodec = hash.mcodec
|
||||
result.data.write(hash.mcodec)
|
||||
if hash.size == 0:
|
||||
result.data.writeVarint(uint(len(data)))
|
||||
result.dpos = len(result.data.buffer)
|
||||
result.data.writeArray(data)
|
||||
result.size = len(data)
|
||||
else:
|
||||
result.data.writeVarint(uint(hash.size))
|
||||
result.dpos = len(result.data.buffer)
|
||||
hash.coder(data, buffer.toOpenArray(0, hash.size - 1))
|
||||
result.data.writeArray(buffer.toOpenArray(0, hash.size - 1))
|
||||
result.size = hash.size
|
||||
result.data.finish()
|
||||
|
||||
proc digestImplWithoutHash(hash: MHash, data: openarray[byte]): MultiHash =
|
||||
result.data = initVBuffer()
|
||||
result.mcodec = hash.mcodec
|
||||
result.size = len(data)
|
||||
result.data.write(hash.mcodec)
|
||||
result.data.writeVarint(uint(len(data)))
|
||||
result.dpos = len(result.data.buffer)
|
||||
result.data.writeArray(data)
|
||||
result.data.finish()
|
||||
|
||||
proc digest*(mhtype: typedesc[MultiHash], hashname: string,
|
||||
data: openarray[byte]): MultiHash {.inline.} =
|
||||
## Perform digest calculation using hash algorithm with name ``hashname`` on
|
||||
## data array ``data``.
|
||||
let mc = MultiCodec.codec(hashname)
|
||||
if mc == InvalidMultiCodec:
|
||||
raise newException(MultihashError, "Incorrect hash name")
|
||||
let hash = CodeHashes.getOrDefault(mc)
|
||||
if isNil(hash.coder):
|
||||
raise newException(MultihashError, "Hash not supported")
|
||||
result = digestImplWithHash(hash, data)
|
||||
|
||||
proc digest*(mhtype: typedesc[MultiHash], hashcode: int,
|
||||
data: openarray[byte]): MultiHash {.inline.} =
|
||||
## Perform digest calculation using hash algorithm with code ``hashcode`` on
|
||||
## data array ``data``.
|
||||
let hash = CodeHashes.getOrDefault(hashcode)
|
||||
if isNil(hash.coder):
|
||||
raise newException(MultihashError, "Hash not supported")
|
||||
result = digestImplWithHash(hash, data)
|
||||
|
||||
proc init*[T](mhtype: typedesc[MultiHash], hashname: string,
|
||||
mdigest: MDigest[T]): MultiHash {.inline.} =
|
||||
## Create MultiHash from nimcrypto's `MDigest` object and hash algorithm name
|
||||
## ``hashname``.
|
||||
let mc = MultiCodec.codec(hashname)
|
||||
if mc == InvalidMultiCodec:
|
||||
raise newException(MultihashError, "Incorrect hash name")
|
||||
let hash = CodeHashes.getOrDefault(mc)
|
||||
if isNil(hash.coder):
|
||||
raise newException(MultihashError, "Hash not supported")
|
||||
if hash.size != len(mdigest.data):
|
||||
raise newException(MultiHashError, "Incorrect MDigest[T] size")
|
||||
result = digestImplWithoutHash(hash, mdigest.data)
|
||||
|
||||
proc init*[T](mhtype: typedesc[MultiHash], hashcode: MultiCodec,
|
||||
mdigest: MDigest[T]): MultiHash {.inline.} =
|
||||
## Create MultiHash from nimcrypto's `MDigest` and hash algorithm code
|
||||
## ``hashcode``.
|
||||
let hash = CodeHashes.getOrDefault(hashcode)
|
||||
if isNil(hash.coder):
|
||||
raise newException(MultihashError, "Hash not supported")
|
||||
if (hash.size != 0) and (hash.size != len(mdigest.data)):
|
||||
raise newException(MultiHashError, "Incorrect MDigest[T] size")
|
||||
result = digestImplWithoutHash(hash, mdigest.data)
|
||||
|
||||
proc init*(mhtype: typedesc[MultiHash], hashname: string,
|
||||
bdigest: openarray[byte]): MultiHash {.inline.} =
|
||||
## Create MultiHash from array of bytes ``bdigest`` and hash algorithm code
|
||||
## ``hashcode``.
|
||||
let mc = MultiCodec.codec(hashname)
|
||||
if mc == InvalidMultiCodec:
|
||||
raise newException(MultihashError, "Incorrect hash name")
|
||||
let hash = CodeHashes.getOrDefault(mc)
|
||||
if isNil(hash.coder):
|
||||
raise newException(MultihashError, "Hash not supported")
|
||||
if (hash.size != 0) and (hash.size != len(bdigest)):
|
||||
raise newException(MultiHashError, "Incorrect bdigest size")
|
||||
result = digestImplWithoutHash(hash, bdigest)
|
||||
|
||||
proc init*(mhtype: typedesc[MultiHash], hashcode: MultiCodec,
|
||||
bdigest: openarray[byte]): MultiHash {.inline.} =
|
||||
## Create MultiHash from array of bytes ``bdigest`` and hash algorithm code
|
||||
## ``hashcode``.
|
||||
let hash = CodeHashes.getOrDefault(hashcode)
|
||||
if isNil(hash.coder):
|
||||
raise newException(MultihashError, "Hash not supported")
|
||||
if (hash.size != 0) and (hash.size != len(bdigest)):
|
||||
raise newException(MultiHashError, "Incorrect bdigest size")
|
||||
result = digestImplWithoutHash(hash, bdigest)
|
||||
|
||||
proc decode*(mhtype: typedesc[MultiHash], data: openarray[byte],
|
||||
mhash: var MultiHash): int =
|
||||
## Decode MultiHash value from array of bytes ``data``.
|
||||
##
|
||||
## On success decoded MultiHash will be stored into ``mhash`` and number of
|
||||
## bytes consumed will be returned.
|
||||
##
|
||||
## On error ``-1`` will be returned.
|
||||
var code, size: uint64
|
||||
var res, dpos: int
|
||||
if len(data) < 2:
|
||||
return -1
|
||||
var vb = initVBuffer(data)
|
||||
if vb.isEmpty():
|
||||
return -1
|
||||
res = vb.readVarint(code)
|
||||
if res == -1:
|
||||
return -1
|
||||
dpos += res
|
||||
res = vb.readVarint(size)
|
||||
if res == -1:
|
||||
return -1
|
||||
dpos += res
|
||||
if size > 0x7FFF_FFFF'u64:
|
||||
return -1
|
||||
let hash = CodeHashes.getOrDefault(MultiCodec(code))
|
||||
if isNil(hash.coder):
|
||||
return -1
|
||||
if (hash.size != 0) and (hash.size != int(size)):
|
||||
return -1
|
||||
if not vb.isEnough(int(size)):
|
||||
return -1
|
||||
mhash = MultiHash.init(MultiCodec(code),
|
||||
vb.buffer.toOpenArray(vb.offset,
|
||||
vb.offset + int(size) - 1))
|
||||
result = vb.offset + int(size)
|
||||
|
||||
proc validate*(mhtype: typedesc[MultiHash], data: openarray[byte]): bool =
|
||||
## Returns ``true`` if array of bytes ``data`` has correct MultiHash inside.
|
||||
var code, size: uint64
|
||||
var res: VarintStatus
|
||||
if len(data) < 2:
|
||||
return false
|
||||
let last = len(data) - 1
|
||||
var offset = 0
|
||||
var length = 0
|
||||
res = LP.getUVarint(data.toOpenArray(offset, last), length, code)
|
||||
if res != VarintStatus.Success:
|
||||
return false
|
||||
offset += length
|
||||
if offset >= len(data):
|
||||
return false
|
||||
res = LP.getUVarint(data.toOpenArray(offset, last), length, size)
|
||||
if res != VarintStatus.Success:
|
||||
return false
|
||||
offset += length
|
||||
if size > 0x7FFF_FFFF'u64:
|
||||
return false
|
||||
let hash = CodeHashes.getOrDefault(cast[MultiCodec](code))
|
||||
if isNil(hash.coder):
|
||||
return false
|
||||
if (hash.size != 0) and (hash.size != int(size)):
|
||||
return false
|
||||
if offset + int(size) > len(data):
|
||||
return false
|
||||
result = true
|
||||
|
||||
proc init*(mhtype: typedesc[MultiHash],
|
||||
data: openarray[byte]): MultiHash {.inline.} =
|
||||
## Create MultiHash from bytes array ``data``.
|
||||
if MultiHash.decode(data, result) == -1:
|
||||
raise newException(MultihashError, "Incorrect MultiHash binary format")
|
||||
|
||||
proc init*(mhtype: typedesc[MultiHash], data: string): MultiHash {.inline.} =
|
||||
## Create MultiHash from hexadecimal string representation ``data``.
|
||||
if MultiHash.decode(fromHex(data), result) == -1:
|
||||
raise newException(MultihashError, "Incorrect MultiHash binary format")
|
||||
|
||||
proc init58*(mhtype: typedesc[MultiHash],
|
||||
data: string): MultiHash {.inline.} =
|
||||
## Create MultiHash from BASE58 encoded string representation ``data``.
|
||||
if MultiHash.decode(Base58.decode(data), result) == -1:
|
||||
raise newException(MultihashError, "Incorrect MultiHash binary format")
|
||||
|
||||
proc cmp(a: openarray[byte], b: openarray[byte]): bool {.inline.} =
|
||||
if len(a) != len(b):
|
||||
return false
|
||||
var n = len(a)
|
||||
var res, diff: int
|
||||
while n > 0:
|
||||
dec(n)
|
||||
diff = int(a[n]) - int(b[n])
|
||||
res = (res and -not(diff)) or diff
|
||||
result = (res == 0)
|
||||
|
||||
proc `==`*[T](mh: MultiHash, mdigest: MDigest[T]): bool =
|
||||
## Compares MultiHash with nimcrypto's MDigest[T], returns ``true`` if
|
||||
## hashes are equal, ``false`` otherwise.
|
||||
if mh.dpos == 0:
|
||||
return false
|
||||
if len(mdigest.data) != mh.size:
|
||||
return false
|
||||
result = cmp(mh.data.buffer.toOpenArray(mh.dpos, mh.dpos + mh.size - 1),
|
||||
mdigest.data.toOpenArray(0, len(mdigest.data) - 1))
|
||||
|
||||
proc `==`*[T](mdigest: MDigest[T], mh: MultiHash): bool {.inline.} =
|
||||
## Compares MultiHash with nimcrypto's MDigest[T], returns ``true`` if
|
||||
## hashes are equal, ``false`` otherwise.
|
||||
result = `==`(mh, mdigest)
|
||||
|
||||
proc `==`*(a: MultiHash, b: MultiHash): bool =
|
||||
## Compares MultiHashes ``a`` and ``b``, returns ``true`` if hashes are equal,
|
||||
## ``false`` otherwise.
|
||||
if a.dpos == 0 and b.dpos == 0:
|
||||
return true
|
||||
if a.mcodec != b.mcodec:
|
||||
return false
|
||||
if a.size != b.size:
|
||||
return false
|
||||
result = cmp(a.data.buffer.toOpenArray(a.dpos, a.dpos + a.size - 1),
|
||||
b.data.buffer.toOpenArray(b.dpos, b.dpos + b.size - 1))
|
||||
|
||||
proc hex*(value: MultiHash): string =
|
||||
## Return hexadecimal string representation of MultiHash ``value``.
|
||||
result = $(value.data)
|
||||
|
||||
proc base58*(value: MultiHash): string =
|
||||
## Return Base58 encoded string representation of MultiHash ``value``.
|
||||
result = Base58.encode(value.data.buffer)
|
||||
|
||||
proc `$`*(mh: MultiHash): string =
|
||||
## Return string representation of MultiHash ``value``.
|
||||
let digest = toHex(mh.data.buffer.toOpenArray(mh.dpos,
|
||||
mh.dpos + mh.size - 1))
|
||||
result = $(mh.mcodec) & "/" & digest
|
||||
|
||||
proc write*(vb: var VBuffer, mh: MultiHash) {.inline.} =
|
||||
## Write MultiHash value ``mh`` to buffer ``vb``.
|
||||
vb.writeArray(mh.data.buffer)
|
||||
|
||||
proc encode*(mbtype: typedesc[MultiBase], encoding: string,
|
||||
mh: MultiHash): string {.inline.} =
|
||||
## Get MultiBase encoded representation of ``mh`` using encoding
|
||||
## ``encoding``.
|
||||
result = MultiBase.encode(encoding, mh.data.buffer)
|
|
@ -167,6 +167,7 @@ proc write*(pb: var ProtoBuffer, field: ProtoField) =
|
|||
assert(res == VarintStatus.Success)
|
||||
pb.offset += length
|
||||
assert(pb.isEnough(len(field.vbuffer)))
|
||||
if len(field.vbuffer) > 0:
|
||||
copyMem(addr pb.buffer[pb.offset], unsafeAddr field.vbuffer[0],
|
||||
len(field.vbuffer))
|
||||
pb.offset += len(field.vbuffer)
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
## 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, gcsafe.}
|
||||
bufferToString*: proc(vb: var VBuffer,
|
||||
s: var string): bool {.nimcall, gcsafe.}
|
||||
validateBuffer*: proc(vb: var VBuffer): bool {.nimcall, gcsafe.}
|
|
@ -16,7 +16,7 @@
|
|||
import bitops
|
||||
|
||||
type
|
||||
VarintStatus* = enum
|
||||
VarintStatus* {.pure.} = enum
|
||||
Error,
|
||||
Success,
|
||||
Overflow,
|
||||
|
|
|
@ -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,407 @@
|
|||
import unittest
|
||||
import ../libp2p/base32
|
||||
|
||||
const TVBaseUpperPadding = [
|
||||
["f", "MY======"],
|
||||
["fo", "MZXQ===="],
|
||||
["foo", "MZXW6==="],
|
||||
["foob", "MZXW6YQ="],
|
||||
["fooba", "MZXW6YTB"],
|
||||
["foobar", "MZXW6YTBOI======"]
|
||||
]
|
||||
|
||||
const TVBaseUpperNoPadding = [
|
||||
["f", "MY"],
|
||||
["fo", "MZXQ"],
|
||||
["foo", "MZXW6"],
|
||||
["foob", "MZXW6YQ"],
|
||||
["fooba", "MZXW6YTB"],
|
||||
["foobar", "MZXW6YTBOI"]
|
||||
]
|
||||
|
||||
const TVBaseLowerPadding = [
|
||||
["f", "my======"],
|
||||
["fo", "mzxq===="],
|
||||
["foo", "mzxw6==="],
|
||||
["foob", "mzxw6yq="],
|
||||
["fooba", "mzxw6ytb"],
|
||||
["foobar", "mzxw6ytboi======"]
|
||||
]
|
||||
|
||||
const TVBaseLowerNoPadding = [
|
||||
["f", "my"],
|
||||
["fo", "mzxq"],
|
||||
["foo", "mzxw6"],
|
||||
["foob", "mzxw6yq"],
|
||||
["fooba", "mzxw6ytb"],
|
||||
["foobar", "mzxw6ytboi"]
|
||||
]
|
||||
|
||||
const TVHexUpperPadding = [
|
||||
["f", "CO======"],
|
||||
["fo", "CPNG===="],
|
||||
["foo", "CPNMU==="],
|
||||
["foob", "CPNMUOG="],
|
||||
["fooba", "CPNMUOJ1"],
|
||||
["foobar", "CPNMUOJ1E8======"]
|
||||
]
|
||||
|
||||
const TVHexUpperNoPadding = [
|
||||
["f", "CO"],
|
||||
["fo", "CPNG"],
|
||||
["foo", "CPNMU"],
|
||||
["foob", "CPNMUOG"],
|
||||
["fooba", "CPNMUOJ1"],
|
||||
["foobar", "CPNMUOJ1E8"]
|
||||
]
|
||||
|
||||
const TVHexLowerPadding = [
|
||||
["f", "co======"],
|
||||
["fo", "cpng===="],
|
||||
["foo", "cpnmu==="],
|
||||
["foob", "cpnmuog="],
|
||||
["fooba", "cpnmuoj1"],
|
||||
["foobar", "cpnmuoj1e8======"]
|
||||
]
|
||||
|
||||
const TVHexLowerNoPadding = [
|
||||
["f", "co"],
|
||||
["fo", "cpng"],
|
||||
["foo", "cpnmu"],
|
||||
["foob", "cpnmuog"],
|
||||
["fooba", "cpnmuoj1"],
|
||||
["foobar", "cpnmuoj1e8"]
|
||||
]
|
||||
|
||||
suite "BASE32 encoding test suite":
|
||||
test "Empty seq/string test":
|
||||
var empty1 = newSeq[byte]()
|
||||
var empty2 = ""
|
||||
var encoded = newString(16)
|
||||
var decoded = newSeq[byte](16)
|
||||
|
||||
var o1, o2, o3, o4, o5, o6, o7, o8: int
|
||||
var e1 = Base32Upper.encode(empty1)
|
||||
var e2 = Base32Lower.encode(empty1)
|
||||
var e3 = Base32UpperPad.encode(empty1)
|
||||
var e4 = Base32LowerPad.encode(empty1)
|
||||
var e5 = HexBase32Upper.encode(empty1)
|
||||
var e6 = HexBase32Lower.encode(empty1)
|
||||
var e7 = HexBase32UpperPad.encode(empty1)
|
||||
var e8 = HexBase32LowerPad.encode(empty1)
|
||||
check:
|
||||
Base32Upper.encode(empty1, encoded, o1) == Base32Status.Success
|
||||
Base32Lower.encode(empty1, encoded, o2) == Base32Status.Success
|
||||
Base32UpperPad.encode(empty1, encoded, o3) == Base32Status.Success
|
||||
Base32LowerPad.encode(empty1, encoded, o4) == Base32Status.Success
|
||||
HexBase32Upper.encode(empty1, encoded, o5) == Base32Status.Success
|
||||
HexBase32Lower.encode(empty1, encoded, o6) == Base32Status.Success
|
||||
HexBase32UpperPad.encode(empty1, encoded, o7) == Base32Status.Success
|
||||
HexBase32LowerPad.encode(empty1, encoded, o8) == Base32Status.Success
|
||||
len(e1) == 0
|
||||
len(e2) == 0
|
||||
len(e3) == 0
|
||||
len(e4) == 0
|
||||
len(e5) == 0
|
||||
len(e6) == 0
|
||||
len(e7) == 0
|
||||
len(e8) == 0
|
||||
o1 == 0
|
||||
o2 == 0
|
||||
o3 == 0
|
||||
o4 == 0
|
||||
o5 == 0
|
||||
o6 == 0
|
||||
o7 == 0
|
||||
o8 == 0
|
||||
var d1 = Base32Upper.decode("")
|
||||
var d2 = Base32Lower.decode("")
|
||||
var d3 = Base32UpperPad.decode("")
|
||||
var d4 = Base32LowerPad.decode("")
|
||||
var d5 = HexBase32Upper.decode("")
|
||||
var d6 = HexBase32Lower.decode("")
|
||||
var d7 = HexBase32UpperPad.decode("")
|
||||
var d8 = HexBase32LowerPad.decode("")
|
||||
check:
|
||||
Base32Upper.decode(empty2, decoded, o1) == Base32Status.Success
|
||||
Base32Lower.decode(empty2, decoded, o2) == Base32Status.Success
|
||||
Base32UpperPad.decode(empty2, decoded, o3) == Base32Status.Success
|
||||
Base32LowerPad.decode(empty2, decoded, o4) == Base32Status.Success
|
||||
HexBase32Upper.decode(empty2, decoded, o5) == Base32Status.Success
|
||||
HexBase32Lower.decode(empty2, decoded, o6) == Base32Status.Success
|
||||
HexBase32UpperPad.decode(empty2, decoded, o7) == Base32Status.Success
|
||||
HexBase32LowerPad.decode(empty2, decoded, o8) == Base32Status.Success
|
||||
len(d1) == 0
|
||||
len(d2) == 0
|
||||
len(d3) == 0
|
||||
len(d4) == 0
|
||||
len(d5) == 0
|
||||
len(d6) == 0
|
||||
len(d7) == 0
|
||||
len(d8) == 0
|
||||
o1 == 0
|
||||
o2 == 0
|
||||
o3 == 0
|
||||
o4 == 0
|
||||
o5 == 0
|
||||
o6 == 0
|
||||
o7 == 0
|
||||
o8 == 0
|
||||
|
||||
test "Zero test":
|
||||
var s = newString(256)
|
||||
for i in 0..255:
|
||||
s[i] = 'A'
|
||||
var buffer: array[256, byte]
|
||||
for i in 0..255:
|
||||
var a = Base32.encode(buffer.toOpenArray(0, i))
|
||||
var b = Base32.decode(a)
|
||||
check b == buffer[0..i]
|
||||
|
||||
test "Leading zero test":
|
||||
var buffer: array[256, byte]
|
||||
for i in 0..255:
|
||||
buffer[255] = byte(i)
|
||||
var a = Base32.encode(buffer)
|
||||
var b = Base32.decode(a)
|
||||
check:
|
||||
equalMem(addr buffer[0], addr b[0], 256) == true
|
||||
|
||||
test "BASE32 uppercase padding test vectors":
|
||||
for item in TVBaseUpperPadding:
|
||||
let plain = cast[seq[byte]](item[0])
|
||||
let expect = item[1]
|
||||
var elen = 0
|
||||
var dlen = 0
|
||||
|
||||
var e1 = Base32UpperPad.encode(plain)
|
||||
var e2 = newString(Base32UpperPad.encodedLength(len(plain)))
|
||||
check:
|
||||
Base32UpperPad.encode(plain, e2, elen) == Base32Status.Success
|
||||
e2.setLen(elen)
|
||||
check:
|
||||
e1 == expect
|
||||
e2 == expect
|
||||
|
||||
var d1 = Base32UpperPad.decode(expect)
|
||||
var d2 = newSeq[byte](Base32UpperPad.decodedLength(len(expect)))
|
||||
check:
|
||||
Base32UpperPad.decode(expect, d2, dlen) == Base32Status.Success
|
||||
d2.setLen(dlen)
|
||||
check:
|
||||
d1 == plain
|
||||
d2 == plain
|
||||
|
||||
test "BASE32 lowercase padding test vectors":
|
||||
for item in TVBaseLowerPadding:
|
||||
let plain = cast[seq[byte]](item[0])
|
||||
let expect = item[1]
|
||||
var elen = 0
|
||||
var dlen = 0
|
||||
|
||||
var e1 = Base32LowerPad.encode(plain)
|
||||
var e2 = newString(Base32LowerPad.encodedLength(len(plain)))
|
||||
check:
|
||||
Base32LowerPad.encode(plain, e2, elen) == Base32Status.Success
|
||||
e2.setLen(elen)
|
||||
check:
|
||||
e1 == expect
|
||||
e2 == expect
|
||||
|
||||
var d1 = Base32LowerPad.decode(expect)
|
||||
var d2 = newSeq[byte](Base32LowerPad.decodedLength(len(expect)))
|
||||
check:
|
||||
Base32LowerPad.decode(expect, d2, dlen) == Base32Status.Success
|
||||
d2.setLen(dlen)
|
||||
check:
|
||||
d1 == plain
|
||||
d2 == plain
|
||||
|
||||
test "BASE32 uppercase no-padding test vectors":
|
||||
for item in TVBaseUpperNoPadding:
|
||||
let plain = cast[seq[byte]](item[0])
|
||||
let expect = item[1]
|
||||
var elen = 0
|
||||
var dlen = 0
|
||||
|
||||
var e1 = Base32Upper.encode(plain)
|
||||
var e2 = newString(Base32Upper.encodedLength(len(plain)))
|
||||
check:
|
||||
Base32Upper.encode(plain, e2, elen) == Base32Status.Success
|
||||
e2.setLen(elen)
|
||||
check:
|
||||
e1 == expect
|
||||
e2 == expect
|
||||
|
||||
var d1 = Base32Upper.decode(expect)
|
||||
var d2 = newSeq[byte](Base32Upper.decodedLength(len(expect)))
|
||||
check:
|
||||
Base32Upper.decode(expect, d2, dlen) == Base32Status.Success
|
||||
d2.setLen(dlen)
|
||||
check:
|
||||
d1 == plain
|
||||
d2 == plain
|
||||
|
||||
test "BASE32 lowercase no-padding test vectors":
|
||||
for item in TVBaseLowerNoPadding:
|
||||
let plain = cast[seq[byte]](item[0])
|
||||
let expect = item[1]
|
||||
var elen = 0
|
||||
var dlen = 0
|
||||
|
||||
var e1 = Base32Lower.encode(plain)
|
||||
var e2 = newString(Base32Lower.encodedLength(len(plain)))
|
||||
check:
|
||||
Base32Lower.encode(plain, e2, elen) == Base32Status.Success
|
||||
e2.setLen(elen)
|
||||
check:
|
||||
e1 == expect
|
||||
e2 == expect
|
||||
|
||||
var d1 = Base32Lower.decode(expect)
|
||||
var d2 = newSeq[byte](Base32Lower.decodedLength(len(expect)))
|
||||
check:
|
||||
Base32Lower.decode(expect, d2, dlen) == Base32Status.Success
|
||||
d2.setLen(dlen)
|
||||
check:
|
||||
d1 == plain
|
||||
d2 == plain
|
||||
|
||||
test "HEX-BASE32 uppercase padding test vectors":
|
||||
for item in TVHexUpperPadding:
|
||||
let plain = cast[seq[byte]](item[0])
|
||||
let expect = item[1]
|
||||
var elen = 0
|
||||
var dlen = 0
|
||||
|
||||
var e1 = HexBase32UpperPad.encode(plain)
|
||||
var e2 = newString(HexBase32UpperPad.encodedLength(len(plain)))
|
||||
check:
|
||||
HexBase32UpperPad.encode(plain, e2, elen) == Base32Status.Success
|
||||
e2.setLen(elen)
|
||||
check:
|
||||
e1 == expect
|
||||
e2 == expect
|
||||
|
||||
var d1 = HexBase32UpperPad.decode(expect)
|
||||
var d2 = newSeq[byte](HexBase32UpperPad.decodedLength(len(expect)))
|
||||
check:
|
||||
HexBase32UpperPad.decode(expect, d2, dlen) == Base32Status.Success
|
||||
d2.setLen(dlen)
|
||||
check:
|
||||
d1 == plain
|
||||
d2 == plain
|
||||
|
||||
test "HEX-BASE32 lowercase padding test vectors":
|
||||
for item in TVHexLowerPadding:
|
||||
let plain = cast[seq[byte]](item[0])
|
||||
let expect = item[1]
|
||||
var elen = 0
|
||||
var dlen = 0
|
||||
|
||||
var e1 = HexBase32LowerPad.encode(plain)
|
||||
var e2 = newString(HexBase32LowerPad.encodedLength(len(plain)))
|
||||
check:
|
||||
HexBase32LowerPad.encode(plain, e2, elen) == Base32Status.Success
|
||||
e2.setLen(elen)
|
||||
check:
|
||||
e1 == expect
|
||||
e2 == expect
|
||||
|
||||
var d1 = HexBase32LowerPad.decode(expect)
|
||||
var d2 = newSeq[byte](HexBase32LowerPad.decodedLength(len(expect)))
|
||||
check:
|
||||
HexBase32LowerPad.decode(expect, d2, dlen) == Base32Status.Success
|
||||
d2.setLen(dlen)
|
||||
check:
|
||||
d1 == plain
|
||||
d2 == plain
|
||||
|
||||
test "HEX-BASE32 uppercase no-padding test vectors":
|
||||
for item in TVHexUpperNoPadding:
|
||||
let plain = cast[seq[byte]](item[0])
|
||||
let expect = item[1]
|
||||
var elen = 0
|
||||
var dlen = 0
|
||||
|
||||
var e1 = HexBase32Upper.encode(plain)
|
||||
var e2 = newString(HexBase32Upper.encodedLength(len(plain)))
|
||||
check:
|
||||
HexBase32Upper.encode(plain, e2, elen) == Base32Status.Success
|
||||
e2.setLen(elen)
|
||||
check:
|
||||
e1 == expect
|
||||
e2 == expect
|
||||
|
||||
var d1 = HexBase32Upper.decode(expect)
|
||||
var d2 = newSeq[byte](HexBase32Upper.decodedLength(len(expect)))
|
||||
check:
|
||||
HexBase32Upper.decode(expect, d2, dlen) == Base32Status.Success
|
||||
d2.setLen(dlen)
|
||||
check:
|
||||
d1 == plain
|
||||
d2 == plain
|
||||
|
||||
test "HEX-BASE32 lowercase no-padding test vectors":
|
||||
for item in TVHexLowerNoPadding:
|
||||
let plain = cast[seq[byte]](item[0])
|
||||
let expect = item[1]
|
||||
var elen = 0
|
||||
var dlen = 0
|
||||
|
||||
var e1 = HexBase32Lower.encode(plain)
|
||||
var e2 = newString(HexBase32Lower.encodedLength(len(plain)))
|
||||
check:
|
||||
HexBase32Lower.encode(plain, e2, elen) == Base32Status.Success
|
||||
e2.setLen(elen)
|
||||
check:
|
||||
e1 == expect
|
||||
e2 == expect
|
||||
|
||||
var d1 = HexBase32Lower.decode(expect)
|
||||
var d2 = newSeq[byte](HexBase32Lower.decodedLength(len(expect)))
|
||||
check:
|
||||
HexBase32Lower.decode(expect, d2, dlen) == Base32Status.Success
|
||||
d2.setLen(dlen)
|
||||
check:
|
||||
d1 == plain
|
||||
d2 == plain
|
||||
|
||||
test "Buffer Overrun test":
|
||||
var encres = ""
|
||||
var encsize = 0
|
||||
var decres: seq[byte] = @[]
|
||||
var decsize = 0
|
||||
check:
|
||||
Base32.encode([0'u8], encres, encsize) == Base32Status.Overrun
|
||||
encsize == Base32.encodedLength(1)
|
||||
Base32.decode("AA", decres, decsize) == Base32Status.Overrun
|
||||
decsize == Base32.decodedLength(2)
|
||||
|
||||
test "Incorrect test":
|
||||
var decres = newSeq[byte](10)
|
||||
var decsize = 0
|
||||
check:
|
||||
Base32.decode("A", decres, decsize) == Base32Status.Incorrect
|
||||
decsize == 0
|
||||
Base32.decode("AAA", decres, decsize) == Base32Status.Incorrect
|
||||
decsize == 0
|
||||
Base32.decode("AAAAAA", decres, decsize) == Base32Status.Incorrect
|
||||
decsize == 0
|
||||
Base32Upper.decode("aa", decres, decsize) == Base32Status.Incorrect
|
||||
decsize == 0
|
||||
Base32Upper.decode("11", decres, decsize) == Base32Status.Incorrect
|
||||
decsize == 0
|
||||
Base32Lower.decode("AA", decres, decsize) == Base32Status.Incorrect
|
||||
decsize == 0
|
||||
Base32Lower.decode("11", decres, decsize) == Base32Status.Incorrect
|
||||
decsize == 0
|
||||
HexBase32Upper.decode("aa", decres, decsize) == Base32Status.Incorrect
|
||||
decsize == 0
|
||||
HexBase32Upper.decode("WW", decres, decsize) == Base32Status.Incorrect
|
||||
decsize == 0
|
||||
HexBase32Lower.decode("AA", decres, decsize) == Base32Status.Incorrect
|
||||
decsize == 0
|
||||
HexBase32Lower.decode("ww", decres, decsize) == Base32Status.Incorrect
|
||||
decsize == 0
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
import unittest
|
||||
import ../libp2p/base58
|
||||
|
||||
proc hexToBytes*(a: string, result: var openarray[byte]) =
|
||||
doAssert(len(a) == 2 * len(result))
|
||||
var i = 0
|
||||
var k = 0
|
||||
var r = 0
|
||||
if len(a) > 0:
|
||||
while i < len(a):
|
||||
let c = a[i]
|
||||
if i != 0 and i %% 2 == 0:
|
||||
result[k] = r.byte
|
||||
r = 0
|
||||
inc(k)
|
||||
else:
|
||||
r = r shl 4
|
||||
case c
|
||||
of 'a'..'f':
|
||||
r = r or (10 + ord(c) - ord('a'))
|
||||
of 'A'..'F':
|
||||
r = r or (10 + ord(c) - ord('A'))
|
||||
of '0'..'9':
|
||||
r = r or (ord(c) - ord('0'))
|
||||
else:
|
||||
doAssert(false)
|
||||
inc(i)
|
||||
result[k] = r.byte
|
||||
|
||||
proc fromHex*(a: string): seq[byte] =
|
||||
doAssert(len(a) %% 2 == 0)
|
||||
if len(a) == 0:
|
||||
result = newSeq[byte]()
|
||||
else:
|
||||
result = newSeq[byte](len(a) div 2)
|
||||
hexToBytes(a, result)
|
||||
|
||||
const TestVectors = [
|
||||
["", ""],
|
||||
["61", "2g"],
|
||||
["626262", "a3gV"],
|
||||
["636363", "aPEr"],
|
||||
["73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2"],
|
||||
["00eb15231dfceb60925886b67d065299925915aeb172c06647", "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L"],
|
||||
["516b6fcd0f", "ABnLTmg"],
|
||||
["bf4f89001e670274dd", "3SEo3LWLoPntC"],
|
||||
["572e4794", "3EFU7m"],
|
||||
["ecac89cad93923c02321", "EJDM8drfXA6uyA"],
|
||||
["10c8511e", "Rt5zm"],
|
||||
["00000000000000000000", "1111111111"],
|
||||
["000111d38e5fc9071ffcd20b4a763cc9ae4f252bb4e48fd66a835e252ada93ff480d6dd43dc62a641155a5", "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"],
|
||||
["000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", "1cWB5HCBdLjAuqGGReWE3R3CguuwSjw6RHn39s2yuDRTS5NsBgNiFpWgAnEx6VQi8csexkgYw3mdYrMHr8x9i7aEwP8kZ7vccXWqKDvGv3u1GxFKPuAkn8JCPPGDMf3vMMnbzm6Nh9zh1gcNsMvH3ZNLmP5fSG6DGbbi2tuwMWPthr4boWwCxf7ewSgNQeacyozhKDDQQ1qL5fQFUW52QKUZDZ5fw3KXNQJMcNTcaB723LchjeKun7MuGW5qyCBZYzA1KjofN1gYBV3NqyhQJ3Ns746GNuf9N2pQPmHz4xpnSrrfCvy6TVVz5d4PdrjeshsWQwpZsZGzvbdAdN8MKV5QsBDY"]
|
||||
]
|
||||
|
||||
suite "BASE58 encoding test suite":
|
||||
test "Empty seq/string test":
|
||||
var a = Base58.encode([])
|
||||
check len(a) == 0
|
||||
var b = Base58.decode("")
|
||||
check len(b) == 0
|
||||
test "Zero test":
|
||||
var s = newString(256)
|
||||
for i in 0..255:
|
||||
s[i] = '1'
|
||||
var buffer: array[256, byte]
|
||||
for i in 0..255:
|
||||
var a = Base58.encode(buffer.toOpenArray(0, i))
|
||||
check a == s[0..i]
|
||||
var b = Base58.decode(a)
|
||||
check b == buffer[0..i]
|
||||
test "Leading zero test":
|
||||
var buffer: array[256, byte]
|
||||
for i in 0..255:
|
||||
buffer[255] = byte(i)
|
||||
var a = Base58.encode(buffer)
|
||||
var b = Base58.decode(a)
|
||||
check:
|
||||
equalMem(addr buffer[0], addr b[0], 256) == true
|
||||
test "Small amount of bytes test":
|
||||
var buffer1: array[1, byte]
|
||||
var buffer2: array[2, byte]
|
||||
for i in 0..255:
|
||||
buffer1[0] = byte(i)
|
||||
var enc = Base58.encode(buffer1)
|
||||
var dec = Base58.decode(enc)
|
||||
check:
|
||||
len(dec) == 1
|
||||
dec[0] == buffer1[0]
|
||||
|
||||
for i in 0..255:
|
||||
for k in 0..255:
|
||||
buffer2[0] = byte(i)
|
||||
buffer2[1] = byte(k)
|
||||
var enc = Base58.encode(buffer2)
|
||||
var dec = Base58.decode(enc)
|
||||
check:
|
||||
len(dec) == 2
|
||||
dec[0] == buffer2[0]
|
||||
dec[1] == buffer2[1]
|
||||
test "Test Vectors test":
|
||||
for item in TestVectors:
|
||||
var a = fromHex(item[0])
|
||||
var enc = Base58.encode(a)
|
||||
var dec = Base58.decode(item[1])
|
||||
check:
|
||||
enc == item[1]
|
||||
dec == a
|
||||
test "Buffer Overrun test":
|
||||
var encres = ""
|
||||
var encsize = 0
|
||||
var decres: seq[byte] = @[]
|
||||
var decsize = 0
|
||||
check:
|
||||
Base58.encode([0'u8], encres, encsize) == Base58Status.Overrun
|
||||
encsize == 1
|
||||
Base58.decode("1", decres, decsize) == Base58Status.Overrun
|
||||
decsize == 5
|
||||
test "Incorrect test":
|
||||
var decres = newSeq[byte](10)
|
||||
var decsize = 0
|
||||
check:
|
||||
Base58.decode("l", decres, decsize) == Base58Status.Incorrect
|
||||
decsize == 0
|
||||
Base58.decode("2l", decres, decsize) == Base58Status.Incorrect
|
||||
decsize == 0
|
||||
Base58.decode("O", decres, decsize) == Base58Status.Incorrect
|
||||
decsize == 0
|
||||
Base58.decode("2O", decres, decsize) == Base58Status.Incorrect
|
||||
decsize == 0
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
import unittest
|
||||
import ../libp2p/[cid, multihash, multicodec]
|
||||
|
||||
suite "Content identifier CID test suite":
|
||||
|
||||
test "CIDv0 test vector":
|
||||
var cid0Text = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n"
|
||||
var cid0 = Cid.init(cid0Text)
|
||||
check:
|
||||
$cid0 == cid0Text
|
||||
cid0.version() == CIDv0
|
||||
cid0.contentType() == multiCodec("dag-pb")
|
||||
cid0.mhash().mcodec == multiCodec("sha2-256")
|
||||
var res = 0
|
||||
try:
|
||||
var cidb0 = Cid.init("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zIII")
|
||||
except CidError:
|
||||
res = 1
|
||||
check res == 1
|
||||
|
||||
test "CIDv1 test vector":
|
||||
var cid1Text = "zb2rhhFAEMepUBbGyP1k8tGfz7BSciKXP6GHuUeUsJBaK6cqG"
|
||||
var chex = "015512209D8453505BDC6F269678E16B3E56" &
|
||||
"C2A2948A41F2C792617CC9611ED363C95B63"
|
||||
var cid1 = Cid.init(cid1Text)
|
||||
check:
|
||||
$cid1 == cid1Text
|
||||
cid1.version() == CIDv1
|
||||
cid1.contentType() == multiCodec("raw")
|
||||
cid1.mhash().mcodec == multiCodec("sha2-256")
|
||||
hex(cid1) == chex
|
||||
|
||||
test "Comparison test":
|
||||
var msg = "Hello World!"
|
||||
var mmsg = "Hello World!Hello World!"
|
||||
var bmsg = cast[seq[byte]](msg)
|
||||
var bmmsg = cast[seq[byte]](mmsg)
|
||||
var cid0 = Cid.init(CIDv0, multiCodec("dag-pb"),
|
||||
MultiHash.digest("sha2-256", bmsg))
|
||||
var cid1 = Cid.init(CIDv1, multiCodec("dag-pb"),
|
||||
MultiHash.digest("sha2-256", bmsg))
|
||||
var cid2 = cid1
|
||||
var cid3 = cid0
|
||||
var cid4 = Cid.init(CIDv1, multiCodec("dag-cbor"),
|
||||
MultiHash.digest("sha2-256", bmsg))
|
||||
var cid5 = Cid.init(CIDv1, multiCodec("dag-pb"),
|
||||
MultiHash.digest("sha2-256", bmmsg))
|
||||
var cid6 = Cid.init(CIDv1, multiCodec("dag-pb"),
|
||||
MultiHash.digest("keccak-256", bmsg))
|
||||
check:
|
||||
cid0 == cid1
|
||||
cid1 == cid2
|
||||
cid2 == cid3
|
||||
cid3 == cid0
|
||||
cid0 != cid4
|
||||
cid1 != cid5
|
||||
cid2 != cid4
|
||||
cid3 != cid6
|
|
@ -1,16 +1,17 @@
|
|||
import unittest
|
||||
import asyncdispatch2
|
||||
import ../libp2p/daemon/daemonapi
|
||||
import ../libp2p/daemon/daemonapi, ../libp2p/multiaddress, ../libp2p/multicodec,
|
||||
../libp2p/cid, ../libp2p/multihash
|
||||
|
||||
proc identitySpawnTest(): Future[bool] {.async.} =
|
||||
var api = await newDaemonApi(sockpath = "/tmp/p2pd-1.sock")
|
||||
var api = await newDaemonApi()
|
||||
var data = await api.identity()
|
||||
await api.close()
|
||||
result = true
|
||||
|
||||
proc connectStreamTest(): Future[bool] {.async.} =
|
||||
var api1 = await newDaemonApi(sockpath = "/tmp/p2pd-1.sock")
|
||||
var api2 = await newDaemonApi(sockpath = "/tmp/p2pd-2.sock")
|
||||
var api1 = await newDaemonApi()
|
||||
var api2 = await newDaemonApi()
|
||||
|
||||
var id1 = await api1.identity()
|
||||
var id2 = await api2.identity()
|
||||
|
@ -26,15 +27,122 @@ proc connectStreamTest(): Future[bool] {.async.} =
|
|||
|
||||
await api2.addHandler(protos, streamHandler)
|
||||
await api1.connect(id2.peer, id2.addresses)
|
||||
# echo await api1.listPeers()
|
||||
var stream = await api1.openStream(id2.peer, protos)
|
||||
let sent = await stream.transp.write(test & "\r\n")
|
||||
doAssert(sent == len(test) + 2)
|
||||
var check = await wait(testFuture, 10000)
|
||||
doAssert(check == test)
|
||||
await stream.close()
|
||||
await api1.close()
|
||||
await api2.close()
|
||||
result = true
|
||||
|
||||
proc provideCidTest(): Future[bool] {.async.} =
|
||||
var api1 = await newDaemonApi({DHTFull})
|
||||
var api2 = await newDaemonApi({DHTFull})
|
||||
var msg = "ethereum2-beacon-chain"
|
||||
var bmsg = cast[seq[byte]](msg)
|
||||
var mh = MultiHash.digest("sha2-256", bmsg)
|
||||
var cid = Cid.init(CIDv1, multiCodec("dag-pb"), mh)
|
||||
|
||||
var id1 = await api1.identity()
|
||||
var id2 = await api2.identity()
|
||||
|
||||
await api1.connect(id2.peer, id2.addresses)
|
||||
while true:
|
||||
var peers = await api1.listPeers()
|
||||
if len(peers) != 0:
|
||||
break
|
||||
|
||||
await api1.dhtProvide(cid)
|
||||
var peers = await api2.dhtFindProviders(cid, 10)
|
||||
|
||||
if len(peers) == 1:
|
||||
if peers[0].peer == id1.peer:
|
||||
result = true
|
||||
|
||||
await api1.close()
|
||||
await api2.close()
|
||||
|
||||
# proc getOnlyOneIPv4Address(addresses: seq[MultiAddress]): seq[MultiAddress] =
|
||||
# ## We doing this becuase of bug in `go-pubsub`
|
||||
# ## https://github.com/libp2p/go-libp2p-pubsub/issues/130
|
||||
# if len(addresses) > 0:
|
||||
# result = newSeqOfCap[MultiAddress](len(addresses))
|
||||
# let ip4 = multiCodec("ip4")
|
||||
# for item in addresses:
|
||||
# if item.protoCode() == ip4:
|
||||
# result.add(item)
|
||||
# break
|
||||
|
||||
proc pubsubTest(f: set[P2PDaemonFlags]): Future[bool] {.async.} =
|
||||
var pubsubData = "TEST MESSAGE"
|
||||
var msgData = cast[seq[byte]](pubsubData)
|
||||
var api1, api2: DaemonAPI
|
||||
|
||||
api1 = await newDaemonApi(f + {Verbose, Logging})
|
||||
api2 = await newDaemonApi(f + {Verbose, Logging})
|
||||
|
||||
var id1 = await api1.identity()
|
||||
var id2 = await api2.identity()
|
||||
|
||||
var resultsCount = 0
|
||||
|
||||
var handlerFuture1 = newFuture[void]()
|
||||
var handlerFuture2 = newFuture[void]()
|
||||
|
||||
proc pubsubHandler1(api: DaemonAPI,
|
||||
ticket: PubsubTicket,
|
||||
message: PubSubMessage): Future[bool] {.async.} =
|
||||
let smsg = cast[string](message.data)
|
||||
if smsg == pubsubData:
|
||||
inc(resultsCount)
|
||||
handlerFuture1.complete()
|
||||
# Callback must return `false` to close subscription channel.
|
||||
result = false
|
||||
|
||||
proc pubsubHandler2(api: DaemonAPI,
|
||||
ticket: PubsubTicket,
|
||||
message: PubSubMessage): Future[bool] {.async.} =
|
||||
let smsg = cast[string](message.data)
|
||||
if smsg == pubsubData:
|
||||
inc(resultsCount)
|
||||
handlerFuture2.complete()
|
||||
# Callback must return `false` to close subscription channel.
|
||||
result = false
|
||||
|
||||
await api1.connect(id2.peer, id2.addresses)
|
||||
await api2.connect(id1.peer, id1.addresses)
|
||||
|
||||
var ticket1 = await api1.pubsubSubscribe("test-topic", pubsubHandler1)
|
||||
var ticket2 = await api2.pubsubSubscribe("test-topic", pubsubHandler2)
|
||||
|
||||
await sleepAsync(2000)
|
||||
|
||||
var topics1 = await api1.pubsubGetTopics()
|
||||
var topics2 = await api2.pubsubGetTopics()
|
||||
|
||||
if len(topics1) == 1 and len(topics2) == 1:
|
||||
var peers1 = await api1.pubsubListPeers("test-topic")
|
||||
var peers2 = await api2.pubsubListPeers("test-topic")
|
||||
if len(peers1) == 1 and len(peers2) == 1:
|
||||
# Publish test data via api1.
|
||||
await sleepAsync(500)
|
||||
await api1.pubsubPublish("test-topic", msgData)
|
||||
var andfut = handlerFuture1 and handlerFuture2
|
||||
await andfut or sleepAsync(10000)
|
||||
|
||||
await api1.close()
|
||||
await api2.close()
|
||||
if resultsCount == 2:
|
||||
result = true
|
||||
else:
|
||||
echo " -- CLIENT1 -- "
|
||||
echo api1.log
|
||||
echo " -- CLIENT2 -- "
|
||||
echo api2.log
|
||||
|
||||
when isMainModule:
|
||||
suite "libp2p-daemon test suite":
|
||||
test "Simple spawn and get identity test":
|
||||
|
@ -43,3 +151,12 @@ when isMainModule:
|
|||
test "Connect/Accept peer/stream test":
|
||||
check:
|
||||
waitFor(connectStreamTest()) == true
|
||||
test "Provide CID test":
|
||||
check:
|
||||
waitFor(provideCidTest()) == true
|
||||
test "GossipSub test":
|
||||
check:
|
||||
waitFor(pubsubTest({PSGossipSub})) == true
|
||||
test "FloodSub test":
|
||||
check:
|
||||
waitFor(pubsubTest({PSFloodSub})) == true
|
||||
|
|
|
@ -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",
|
||||
"/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",
|
||||
"/ip4/127.0.0.1/p2p",
|
||||
"/ip4/127.0.0.1/p2p/tcp",
|
||||
"/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",
|
||||
"/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",
|
||||
"/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"
|
|
@ -0,0 +1,306 @@
|
|||
import unittest
|
||||
import ../libp2p/multibase
|
||||
|
||||
const GoTestVectors = [
|
||||
[
|
||||
"identity",
|
||||
"\x00Decentralize everything!!!",
|
||||
"Decentralize everything!!!"
|
||||
],
|
||||
# [
|
||||
# "base16",
|
||||
# "f446563656e7472616c697a652065766572797468696e67212121",
|
||||
# "Decentralize everything!!!"
|
||||
# ],
|
||||
# [
|
||||
# "base16upper",
|
||||
# "F446563656E7472616C697A652065766572797468696E67212121",
|
||||
# "Decentralize everything!!!"
|
||||
# ],
|
||||
[
|
||||
"base32",
|
||||
"birswgzloorzgc3djpjssazlwmvzhs5dinfxgoijbee",
|
||||
"Decentralize everything!!!"
|
||||
],
|
||||
[
|
||||
"base32upper",
|
||||
"BIRSWGZLOORZGC3DJPJSSAZLWMVZHS5DINFXGOIJBEE",
|
||||
"Decentralize everything!!!"
|
||||
],
|
||||
[
|
||||
"base32pad",
|
||||
"cirswgzloorzgc3djpjssazlwmvzhs5dinfxgoijbee======",
|
||||
"Decentralize everything!!!"
|
||||
],
|
||||
[
|
||||
"base32padupper",
|
||||
"CIRSWGZLOORZGC3DJPJSSAZLWMVZHS5DINFXGOIJBEE======",
|
||||
"Decentralize everything!!!"
|
||||
],
|
||||
[
|
||||
"base32hex",
|
||||
"v8him6pbeehp62r39f9ii0pbmclp7it38d5n6e89144",
|
||||
"Decentralize everything!!!"
|
||||
],
|
||||
[
|
||||
"base32hexupper",
|
||||
"V8HIM6PBEEHP62R39F9II0PBMCLP7IT38D5N6E89144",
|
||||
"Decentralize everything!!!"
|
||||
],
|
||||
[
|
||||
"base32hexpad",
|
||||
"t8him6pbeehp62r39f9ii0pbmclp7it38d5n6e89144======",
|
||||
"Decentralize everything!!!"
|
||||
],
|
||||
[
|
||||
"base32hexpadupper",
|
||||
"T8HIM6PBEEHP62R39F9II0PBMCLP7IT38D5N6E89144======",
|
||||
"Decentralize everything!!!"
|
||||
],
|
||||
[
|
||||
"base58btc",
|
||||
"z36UQrhJq9fNDS7DiAHM9YXqDHMPfr4EMArvt",
|
||||
"Decentralize everything!!!"
|
||||
],
|
||||
# [
|
||||
# "base64",
|
||||
# "mRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchISE",
|
||||
# "Decentralize everything!!!"
|
||||
# ],
|
||||
# [
|
||||
# "base64url",
|
||||
# "uRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchISE",
|
||||
# "Decentralize everything!!!"
|
||||
# ],
|
||||
# [
|
||||
# "base64pad",
|
||||
# "MRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchISE=",
|
||||
# "Decentralize everything!!!"
|
||||
# ],
|
||||
# [
|
||||
# "base64urlpad",
|
||||
# "URGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchISE=",
|
||||
# "Decentralize everything!!!"
|
||||
# ],
|
||||
]
|
||||
|
||||
suite "MultiBase test suite":
|
||||
test "Zero-length data encoding/decoding test":
|
||||
var enc = newString(1)
|
||||
var dec = newSeq[byte]()
|
||||
var plain = newSeq[byte]()
|
||||
var olens: array[21, int]
|
||||
check:
|
||||
MultiBase.encodedLength("identity", 0) == 1
|
||||
MultiBase.decodedLength('\x00', 0) == -1
|
||||
MultiBase.decodedLength('\x00', 1) == 0
|
||||
check:
|
||||
MultiBase.encode("identity", plain) == "\x00"
|
||||
# MultiBase.encode("base1", plain) == "1"
|
||||
# MultiBase.encode("base2", plain) == "0"
|
||||
# MultiBase.encode("base8", plain) == "7"
|
||||
# MultiBase.encode("base10", plain) == "9"
|
||||
# MultiBase.encode("base16", plain) == "f"
|
||||
# MultiBase.encode("base16upper", plain) == "F"
|
||||
MultiBase.encode("base32hex", plain) == "v"
|
||||
MultiBase.encode("base32hexupper", plain) == "V"
|
||||
MultiBase.encode("base32hexpad", plain) == "t"
|
||||
MultiBase.encode("base32hexpadupper", plain) == "T"
|
||||
MultiBase.encode("base32", plain) == "b"
|
||||
MultiBase.encode("base32upper", plain) == "B"
|
||||
MultiBase.encode("base32pad", plain) == "c"
|
||||
MultiBase.encode("base32padupper", plain) == "C"
|
||||
MultiBase.encode("base58btc", plain) == "z"
|
||||
MultiBase.encode("base58flickr", plain) == "Z"
|
||||
# MultiBase.encode("base64", plain) == "m"
|
||||
# MultiBase.encode("base64pad", plain) == "M"
|
||||
# MultiBase.encode("base64url", plain) == "u"
|
||||
# MultiBase.encode("base64urlpad", plain) == "U"
|
||||
check:
|
||||
len(MultiBase.decode("\x00")) == 0
|
||||
# len(MultiBase.decode("1")) == 0
|
||||
# len(MultiBase.decode("0")) == 0
|
||||
# len(MultiBase.decode("7")) == 0
|
||||
# len(MultiBase.decode("9")) == 0
|
||||
# len(MultiBase.decode("f")) == 0
|
||||
# len(MultiBase.decode("F")) == 0
|
||||
len(MultiBase.decode("v")) == 0
|
||||
len(MultiBase.decode("V")) == 0
|
||||
len(MultiBase.decode("t")) == 0
|
||||
len(MultiBase.decode("T")) == 0
|
||||
len(MultiBase.decode("b")) == 0
|
||||
len(MultiBase.decode("B")) == 0
|
||||
len(MultiBase.decode("c")) == 0
|
||||
len(MultiBase.decode("C")) == 0
|
||||
len(MultiBase.decode("z")) == 0
|
||||
len(MultiBase.decode("Z")) == 0
|
||||
# len(MultiBase.decode("m")) == 0
|
||||
# len(MultiBase.decode("M")) == 0
|
||||
# len(MultiBase.decode("u")) == 0
|
||||
# len(MultiBase.decode("U")) == 0
|
||||
check:
|
||||
MultiBase.encode("identity", plain, enc,
|
||||
olens[0]) == MultiBaseStatus.Success
|
||||
enc == "\x00"
|
||||
olens[0] == 1
|
||||
# MultiBase.encode("base1", plain, enc,
|
||||
# olens[1]) == MultiBaseStatus.Success
|
||||
# enc == "1"
|
||||
# olens[1] == 1
|
||||
# MultiBase.encode("base2", plain, enc,
|
||||
# olens[2]) == MultiBaseStatus.Success
|
||||
# enc == "0"
|
||||
# olens[2] == 1
|
||||
# MultiBase.encode("base8", plain, enc,
|
||||
# olens[3]) == MultiBaseStatus.Success
|
||||
# enc == "7"
|
||||
# olens[3] == 1
|
||||
# MultiBase.encode("base10", plain, enc,
|
||||
# olens[4]) == MultiBaseStatus.Success
|
||||
# enc == "9"
|
||||
# olens[4] == 1
|
||||
# MultiBase.encode("base16", plain, enc,
|
||||
# olens[5]) == MultiBaseStatus.Success
|
||||
# enc == "f"
|
||||
# olens[5] == 1
|
||||
# MultiBase.encode("base16upper", plain, enc,
|
||||
# olens[6]) == MultiBaseStatus.Success
|
||||
# enc == "F"
|
||||
# olens[6] == 1
|
||||
MultiBase.encode("base32hex", plain, enc,
|
||||
olens[7]) == MultiBaseStatus.Success
|
||||
enc == "v"
|
||||
olens[7] == 1
|
||||
MultiBase.encode("base32hexupper", plain, enc,
|
||||
olens[8]) == MultiBaseStatus.Success
|
||||
enc == "V"
|
||||
olens[8] == 1
|
||||
MultiBase.encode("base32hexpad", plain, enc,
|
||||
olens[9]) == MultiBaseStatus.Success
|
||||
enc == "t"
|
||||
olens[9] == 1
|
||||
MultiBase.encode("base32hexpadupper", plain, enc,
|
||||
olens[10]) == MultiBaseStatus.Success
|
||||
enc == "T"
|
||||
olens[10] == 1
|
||||
MultiBase.encode("base32", plain, enc,
|
||||
olens[11]) == MultiBaseStatus.Success
|
||||
enc == "b"
|
||||
olens[11] == 1
|
||||
MultiBase.encode("base32upper", plain, enc,
|
||||
olens[12]) == MultiBaseStatus.Success
|
||||
enc == "B"
|
||||
olens[12] == 1
|
||||
MultiBase.encode("base32pad", plain, enc,
|
||||
olens[13]) == MultiBaseStatus.Success
|
||||
enc == "c"
|
||||
olens[13] == 1
|
||||
MultiBase.encode("base32padupper", plain, enc,
|
||||
olens[14]) == MultiBaseStatus.Success
|
||||
enc == "C"
|
||||
olens[14] == 1
|
||||
MultiBase.encode("base58btc", plain, enc,
|
||||
olens[15]) == MultiBaseStatus.Success
|
||||
enc == "z"
|
||||
olens[15] == 1
|
||||
MultiBase.encode("base58flickr", plain, enc,
|
||||
olens[16]) == MultiBaseStatus.Success
|
||||
enc == "Z"
|
||||
olens[16] == 1
|
||||
check:
|
||||
MultiBase.decode("", dec, olens[0]) == MultiBaseStatus.Incorrect
|
||||
MultiBase.decode("\x00", dec, olens[0]) == MultiBaseStatus.Success
|
||||
olens[0] == 0
|
||||
# MultiBase.decode("1", dec, olens[1]) == MultiBaseStatus.Success
|
||||
# olens[1] == 0
|
||||
# MultiBase.decode("0", dec, olens[2]) == MultiBaseStatus.Success
|
||||
# olens[2] == 0
|
||||
# MultiBase.decode("7", dec, olens[3]) == MultiBaseStatus.Success
|
||||
# olens[3] == 0
|
||||
# MultiBase.decode("9", dec, olens[4]) == MultiBaseStatus.Success
|
||||
# olens[4] == 0
|
||||
# MultiBase.decode("f", dec, olens[5]) == MultiBaseStatus.Success
|
||||
# olens[5] == 0
|
||||
# MultiBase.decode("F", dec, olens[6]) == MultiBaseStatus.Success
|
||||
# olens[6] == 0
|
||||
MultiBase.decode("v", dec, olens[7]) == MultiBaseStatus.Success
|
||||
olens[7] == 0
|
||||
MultiBase.decode("V", dec, olens[8]) == MultiBaseStatus.Success
|
||||
olens[8] == 0
|
||||
MultiBase.decode("t", dec, olens[9]) == MultiBaseStatus.Success
|
||||
olens[9] == 0
|
||||
MultiBase.decode("T", dec, olens[10]) == MultiBaseStatus.Success
|
||||
olens[10] == 0
|
||||
MultiBase.decode("b", dec, olens[11]) == MultiBaseStatus.Success
|
||||
olens[11] == 0
|
||||
MultiBase.decode("B", dec, olens[12]) == MultiBaseStatus.Success
|
||||
olens[12] == 0
|
||||
MultiBase.decode("c", dec, olens[13]) == MultiBaseStatus.Success
|
||||
olens[13] == 0
|
||||
MultiBase.decode("C", dec, olens[14]) == MultiBaseStatus.Success
|
||||
olens[14] == 0
|
||||
MultiBase.decode("z", dec, olens[15]) == MultiBaseStatus.Success
|
||||
olens[15] == 0
|
||||
MultiBase.decode("Z", dec, olens[16]) == MultiBaseStatus.Success
|
||||
olens[16] == 0
|
||||
# MultiBase.decode("m", dec, olens[16]) == MultiBaseStatus.Success
|
||||
# olens[16] == 0
|
||||
# MultiBase.decode("M", dec, olens[16]) == MultiBaseStatus.Success
|
||||
# olens[16] == 0
|
||||
# MultiBase.decode("u", dec, olens[16]) == MultiBaseStatus.Success
|
||||
# olens[16] == 0
|
||||
# MultiBase.decode("U", dec, olens[16]) == MultiBaseStatus.Success
|
||||
# olens[16] == 0
|
||||
test "go-multibase test vectors":
|
||||
for item in GoTestVectors:
|
||||
let encoding = item[0]
|
||||
let encoded = item[1]
|
||||
var expect = item[2]
|
||||
var bexpect = cast[seq[byte]](expect)
|
||||
var outlen = 0
|
||||
check:
|
||||
MultiBase.encode(encoding, bexpect) == encoded
|
||||
MultiBase.decode(encoded) == bexpect
|
||||
|
||||
let elength = MultiBase.encodedLength(encoding, len(expect))
|
||||
var ebuffer = newString(elength)
|
||||
outlen = 0
|
||||
check:
|
||||
MultiBase.encode(encoding, bexpect, ebuffer,
|
||||
outlen) == MultiBaseStatus.Success
|
||||
ebuffer.setLen(outlen)
|
||||
check:
|
||||
encoded == ebuffer
|
||||
|
||||
let dlength = MultiBase.decodedLength(encoded[0], len(encoded))
|
||||
var dbuffer = newSeq[byte](dlength)
|
||||
outlen = 0
|
||||
check:
|
||||
MultiBase.decode(encoded, dbuffer, outlen) == MultiBaseStatus.Success
|
||||
dbuffer.setLen(outlen)
|
||||
check:
|
||||
bexpect == dbuffer
|
||||
test "Unknown codec test":
|
||||
var data = @[0x00'u8, 0x01'u8]
|
||||
var ebuffer = newString(100)
|
||||
var dbuffer = newSeq[byte](100)
|
||||
var outlen = 0
|
||||
check:
|
||||
MultiBase.encode("unknown", data, ebuffer,
|
||||
outlen) == MultiBaseStatus.BadCodec
|
||||
MultiBase.decode("\x01\x00", dbuffer, outlen) == MultiBaseStatus.BadCodec
|
||||
var r1 = false
|
||||
var r2 = false
|
||||
try:
|
||||
var enc = MultiBase.encode("unknwon", data)
|
||||
except MultiBaseError:
|
||||
r1 = true
|
||||
|
||||
try:
|
||||
var dec = MultiBase.decode("\x01\x00")
|
||||
except MultiBaseError:
|
||||
r2 = true
|
||||
|
||||
check:
|
||||
r1 == true
|
||||
r2 == true
|
|
@ -0,0 +1,80 @@
|
|||
import unittest
|
||||
import ../libp2p/multihash
|
||||
|
||||
const
|
||||
RustTestVectors = [
|
||||
# TODO: SHA1
|
||||
# [
|
||||
# "sha1",
|
||||
# "beep boop",
|
||||
# "11147c8357577f51d4f0a8d393aa1aaafb28863d9421"
|
||||
# ],
|
||||
[
|
||||
"sha2-256",
|
||||
"helloworld",
|
||||
"1220936A185CAAA266BB9CBE981E9E05CB78CD732B0B3280EB944412BB6F8F8F07AF",
|
||||
],
|
||||
[
|
||||
"sha2-256",
|
||||
"beep boop",
|
||||
"122090EA688E275D580567325032492B597BC77221C62493E76330B85DDDA191EF7C",
|
||||
],
|
||||
[
|
||||
"sha2-512",
|
||||
"hello world",
|
||||
"1340309ECC489C12D6EB4CC40F50C902F2B4D0ED77EE511A7C7A9BCD3CA86D4CD86F989DD35BC5FF499670DA34255B45B0CFD830E81F605DCF7DC5542E93AE9CD76F"
|
||||
],
|
||||
[
|
||||
"sha3-224",
|
||||
"hello world",
|
||||
"171CDFB7F18C77E928BB56FAEB2DA27291BD790BC1045CDE45F3210BB6C5"
|
||||
],
|
||||
[
|
||||
"sha3-256",
|
||||
"hello world",
|
||||
"1620644BCC7E564373040999AAC89E7622F3CA71FBA1D972FD94A31C3BFBF24E3938"
|
||||
],
|
||||
[
|
||||
"sha3-384",
|
||||
"hello world",
|
||||
"153083BFF28DDE1B1BF5810071C6643C08E5B05BDB836EFFD70B403EA8EA0A634DC4997EB1053AA3593F590F9C63630DD90B"
|
||||
],
|
||||
[
|
||||
"sha3-512",
|
||||
"hello world",
|
||||
"1440840006653E9AC9E95117A15C915CAAB81662918E925DE9E004F774FF82D7079A40D4D27B1B372657C61D46D470304C88C788B3A4527AD074D1DCCBEE5DBAA99A"
|
||||
],
|
||||
[
|
||||
"keccak-224",
|
||||
"hello world",
|
||||
"1A1C25F3ECFEBABE99686282F57F5C9E1F18244CFEE2813D33F955AAE568"
|
||||
],
|
||||
[
|
||||
"keccak-256",
|
||||
"hello world",
|
||||
"1B2047173285A8D7341E5E972FC677286384F802F8EF42A5EC5F03BBFA254CB01FAD"
|
||||
],
|
||||
[
|
||||
"keccak-384",
|
||||
"hello world",
|
||||
"1C3065FC99339A2A40E99D3C40D695B22F278853CA0F925CDE4254BCAE5E22ECE47E6441F91B6568425ADC9D95B0072EB49F"
|
||||
],
|
||||
[
|
||||
"keccak-512",
|
||||
"hello world",
|
||||
"1D403EE2B40047B8060F68C67242175660F4174D0AF5C01D47168EC20ED619B0B7C42181F40AA1046F39E2EF9EFC6910782A998E0013D172458957957FAC9405B67D"
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
suite "MultiHash test suite":
|
||||
test "rust-multihash test vectors":
|
||||
for item in RustTestVectors:
|
||||
var msg = item[1]
|
||||
var bmsg = cast[seq[byte]](msg)
|
||||
var mh1 = MultiHash.digest(item[0], bmsg)
|
||||
var mh2 = MultiHash.init(item[2])
|
||||
check:
|
||||
hex(mh1) == item[2]
|
||||
hex(mh1) == hex(mh2)
|
||||
mh1 == mh2
|
Loading…
Reference in New Issue