diff --git a/libp2p.nimble b/libp2p.nimble index 12f00ca..13bd602 100644 --- a/libp2p.nimble +++ b/libp2p.nimble @@ -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" \ No newline at end of file diff --git a/libp2p/base32.nim b/libp2p/base32.nim index f117516..0a3fa7c 100644 --- a/libp2p/base32.nim +++ b/libp2p/base32.nim @@ -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) diff --git a/libp2p/base58.nim b/libp2p/base58.nim index 9a26433..6b0851b 100644 --- a/libp2p/base58.nim +++ b/libp2p/base58.nim @@ -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 diff --git a/libp2p/multibase.nim b/libp2p/multibase.nim new file mode 100644 index 0000000..fec965a --- /dev/null +++ b/libp2p/multibase.nim @@ -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) diff --git a/tests/testmultibase.nim b/tests/testmultibase.nim new file mode 100644 index 0000000..1f09204 --- /dev/null +++ b/tests/testmultibase.nim @@ -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 + \ No newline at end of file