MultiBase initial implementation with tests.
This commit is contained in:
parent
fc6902dda2
commit
7d60e22782
|
@ -13,8 +13,9 @@ requires "nim > 0.18.0",
|
|||
|
||||
task test, "Runs the test suite":
|
||||
exec "nim c -r tests/testvarint"
|
||||
exec "nim c -r tests/testdaemon"
|
||||
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/testdaemon"
|
|
@ -67,7 +67,8 @@ const
|
|||
HEXLowerCaseAlphabet* = newAlphabet32("0123456789abcdefghijklmnopqrstuv")
|
||||
|
||||
proc encodedLength*(btype: typedesc[Base32Types], length: int): int =
|
||||
## Return length of BASE32 encoded value for plain length ``length``.
|
||||
## 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)
|
||||
|
@ -77,7 +78,7 @@ proc encodedLength*(btype: typedesc[Base32Types], length: int): int =
|
|||
result += 8
|
||||
|
||||
proc decodedLength*(btype: typedesc[Base32Types], length: int): int =
|
||||
## Return length of decoded value of BASE32 encoded value of length
|
||||
## 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)
|
||||
|
|
|
@ -48,6 +48,16 @@ const
|
|||
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
|
||||
|
@ -114,7 +124,7 @@ proc encode*(btype: typedesc[Base58C],
|
|||
else:
|
||||
result = ""
|
||||
|
||||
proc decode*(btype: typedesc[Base58C], instr: string,
|
||||
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
|
||||
|
|
|
@ -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,307 @@
|
|||
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
|
||||
|
Loading…
Reference in New Issue