mirror of https://github.com/vacp2p/nim-libp2p.git
first pass, use results for Cid module (#480)
* first pass, use results for Cid module * improvements to decode
This commit is contained in:
parent
a990fe95a0
commit
5543f6681f
171
libp2p/cid.nim
171
libp2p/cid.nim
|
@ -8,13 +8,18 @@
|
||||||
## those terms.
|
## those terms.
|
||||||
|
|
||||||
## This module implementes CID (Content IDentifier).
|
## This module implementes CID (Content IDentifier).
|
||||||
|
|
||||||
|
{.push raises: [Defect].}
|
||||||
|
|
||||||
import tables
|
import tables
|
||||||
import multibase, multicodec, multihash, vbuffer, varint
|
import multibase, multicodec, multihash, vbuffer, varint
|
||||||
import stew/base58
|
import stew/[base58, results]
|
||||||
|
|
||||||
|
export results
|
||||||
|
|
||||||
type
|
type
|
||||||
CidStatus* {.pure.} = enum
|
CidError* {.pure.} = enum
|
||||||
Error, Success, Incorrect, Overrun
|
Error, Incorrect, Unsupported, Overrun
|
||||||
|
|
||||||
CidVersion* = enum
|
CidVersion* = enum
|
||||||
CIDvIncorrect, CIDv0, CIDv1, CIDvReserved
|
CIDvIncorrect, CIDv0, CIDv1, CIDvReserved
|
||||||
|
@ -25,8 +30,6 @@ type
|
||||||
hpos*: int
|
hpos*: int
|
||||||
data*: VBuffer
|
data*: VBuffer
|
||||||
|
|
||||||
CidError* = object of CatchableError
|
|
||||||
|
|
||||||
const
|
const
|
||||||
ContentIdsList = [
|
ContentIdsList = [
|
||||||
multiCodec("raw"),
|
multiCodec("raw"),
|
||||||
|
@ -66,66 +69,74 @@ proc initCidCodeTable(): Table[int, MultiCodec] {.compileTime.} =
|
||||||
const
|
const
|
||||||
CodeContentIds = initCidCodeTable()
|
CodeContentIds = initCidCodeTable()
|
||||||
|
|
||||||
proc decode(data: openarray[byte], cid: var Cid): CidStatus =
|
template orError*(exp: untyped, err: untyped): untyped =
|
||||||
if len(data) == 34:
|
(exp.mapErr do (_: auto) -> auto: err)
|
||||||
if data[0] == 0x12'u8 and data[1] == 0x20'u8:
|
|
||||||
cid.cidver = CIDv0
|
proc decode(data: openarray[byte]): Result[Cid, CidError] =
|
||||||
cid.mcodec = multiCodec("dag-pb")
|
if len(data) == 34 and data[0] == 0x12'u8 and data[1] == 0x20'u8:
|
||||||
cid.hpos = 0
|
ok(Cid(
|
||||||
cid.data = initVBuffer(data)
|
cidver: CIDv0,
|
||||||
result = CidStatus.Success
|
mcodec: multiCodec("dag-pb"),
|
||||||
if cid.cidver == CIDvIncorrect:
|
hpos: 0,
|
||||||
|
data: initVBuffer(data)))
|
||||||
|
else:
|
||||||
var version, codec: uint64
|
var version, codec: uint64
|
||||||
var res, offset: int
|
var res, offset: int
|
||||||
var vb = initVBuffer(data)
|
var vb = initVBuffer(data)
|
||||||
if vb.isEmpty():
|
if vb.isEmpty():
|
||||||
return CidStatus.Incorrect
|
err(CidError.Incorrect)
|
||||||
res = vb.readVarint(version)
|
else:
|
||||||
if res == -1:
|
res = vb.readVarint(version)
|
||||||
return CidStatus.Incorrect
|
if res == -1:
|
||||||
offset += res
|
err(CidError.Incorrect)
|
||||||
if version != 1'u64:
|
else:
|
||||||
return CidStatus.Incorrect
|
offset += res
|
||||||
res = vb.readVarint(codec)
|
if version != 1'u64:
|
||||||
if res == -1:
|
err(CidError.Incorrect)
|
||||||
return CidStatus.Incorrect
|
else:
|
||||||
offset += res
|
res = vb.readVarint(codec)
|
||||||
var mcodec = CodeContentIds.getOrDefault(cast[int](codec),
|
if res == -1:
|
||||||
InvalidMultiCodec)
|
err(CidError.Incorrect)
|
||||||
if mcodec == InvalidMultiCodec:
|
else:
|
||||||
return CidStatus.Incorrect
|
offset += res
|
||||||
if not MultiHash.validate(vb.buffer.toOpenArray(vb.offset,
|
var mcodec = CodeContentIds.getOrDefault(cast[int](codec),
|
||||||
vb.buffer.high)):
|
InvalidMultiCodec)
|
||||||
return CidStatus.Incorrect
|
if mcodec == InvalidMultiCodec:
|
||||||
vb.finish()
|
err(CidError.Incorrect)
|
||||||
cid.cidver = CIDv1
|
else:
|
||||||
cid.mcodec = mcodec
|
if not MultiHash.validate(vb.buffer.toOpenArray(vb.offset,
|
||||||
cid.hpos = offset
|
vb.buffer.high)):
|
||||||
cid.data = vb
|
err(CidError.Incorrect)
|
||||||
result = CidStatus.Success
|
else:
|
||||||
|
vb.finish()
|
||||||
|
ok(Cid(
|
||||||
|
cidver: CIDv1,
|
||||||
|
mcodec: mcodec,
|
||||||
|
hpos: offset,
|
||||||
|
data: vb))
|
||||||
|
|
||||||
proc decode(data: openarray[char], cid: var Cid): CidStatus =
|
proc decode(data: openarray[char]): Result[Cid, CidError] =
|
||||||
var buffer: seq[byte]
|
var buffer: seq[byte]
|
||||||
var plen = 0
|
var plen = 0
|
||||||
if len(data) < 2:
|
if len(data) < 2:
|
||||||
return CidStatus.Incorrect
|
return err(CidError.Incorrect)
|
||||||
if len(data) == 46:
|
if len(data) == 46:
|
||||||
if data[0] == 'Q' and data[1] == 'm':
|
if data[0] == 'Q' and data[1] == 'm':
|
||||||
buffer = newSeq[byte](BTCBase58.decodedLength(len(data)))
|
buffer = newSeq[byte](BTCBase58.decodedLength(len(data)))
|
||||||
if BTCBase58.decode(data, buffer, plen) != Base58Status.Success:
|
if BTCBase58.decode(data, buffer, plen) != Base58Status.Success:
|
||||||
return CidStatus.Incorrect
|
return err(CidError.Incorrect)
|
||||||
buffer.setLen(plen)
|
buffer.setLen(plen)
|
||||||
if len(buffer) == 0:
|
if len(buffer) == 0:
|
||||||
let length = MultiBase.decodedLength(data[0], len(data))
|
let length = MultiBase.decodedLength(data[0], len(data))
|
||||||
if length == -1:
|
if length == -1:
|
||||||
return CidStatus.Incorrect
|
return err(CidError.Incorrect)
|
||||||
buffer = newSeq[byte](length)
|
buffer = newSeq[byte](length)
|
||||||
if MultiBase.decode(data, buffer, plen) != MultiBaseStatus.Success:
|
if MultiBase.decode(data, buffer, plen) != MultiBaseStatus.Success:
|
||||||
return CidStatus.Incorrect
|
return err(CidError.Incorrect)
|
||||||
buffer.setLen(plen)
|
buffer.setLen(plen)
|
||||||
if buffer[0] == 0x12'u8:
|
if buffer[0] == 0x12'u8:
|
||||||
return CidStatus.Incorrect
|
return err(CidError.Incorrect)
|
||||||
result = decode(buffer, cid)
|
decode(buffer)
|
||||||
|
|
||||||
proc validate*(ctype: typedesc[Cid], data: openarray[byte]): bool =
|
proc validate*(ctype: typedesc[Cid], data: openarray[byte]): bool =
|
||||||
## Returns ``true`` is data has valid binary CID representation.
|
## Returns ``true`` is data has valid binary CID representation.
|
||||||
|
@ -157,30 +168,30 @@ proc validate*(ctype: typedesc[Cid], data: openarray[byte]): bool =
|
||||||
return false
|
return false
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc mhash*(cid: Cid): MultiHash =
|
proc mhash*(cid: Cid): Result[MultiHash, CidError] =
|
||||||
## Returns MultiHash part of CID.
|
## Returns MultiHash part of CID.
|
||||||
if cid.cidver notin {CIDv0, CIDv1}:
|
if cid.cidver notin {CIDv0, CIDv1}:
|
||||||
raise newException(CidError, "Incorrect CID!")
|
err(CidError.Incorrect)
|
||||||
result = MultiHash.init(
|
else:
|
||||||
cid.data.buffer.toOpenArray(cid.hpos, cid.data.high)).tryGet()
|
MultiHash.init(cid.data.buffer.toOpenArray(cid.hpos, cid.data.high)).orError(CidError.Incorrect)
|
||||||
|
|
||||||
proc contentType*(cid: Cid): MultiCodec =
|
proc contentType*(cid: Cid): Result[MultiCodec, CidError] =
|
||||||
## Returns content type part of CID
|
## Returns content type part of CID
|
||||||
if cid.cidver notin {CIDv0, CIDv1}:
|
if cid.cidver notin {CIDv0, CIDv1}:
|
||||||
raise newException(CidError, "Incorrect CID!")
|
err(CidError.Incorrect)
|
||||||
result = cid.mcodec
|
else:
|
||||||
|
ok(cid.mcodec)
|
||||||
|
|
||||||
proc version*(cid: Cid): CidVersion =
|
proc version*(cid: Cid): CidVersion =
|
||||||
## Returns CID version
|
## Returns CID version
|
||||||
result = cid.cidver
|
result = cid.cidver
|
||||||
|
|
||||||
proc init*[T: char|byte](ctype: typedesc[Cid], data: openarray[T]): Cid =
|
proc init*[T: char|byte](ctype: typedesc[Cid], data: openarray[T]): Result[Cid, CidError] =
|
||||||
## Create new content identifier using array of bytes or string ``data``.
|
## Create new content identifier using array of bytes or string ``data``.
|
||||||
if decode(data, result) != CidStatus.Success:
|
decode(data)
|
||||||
raise newException(CidError, "Incorrect CID!")
|
|
||||||
|
|
||||||
proc init*(ctype: typedesc[Cid], version: CidVersion, content: MultiCodec,
|
proc init*(ctype: typedesc[Cid], version: CidVersion, content: MultiCodec,
|
||||||
hash: MultiHash): Cid =
|
hash: MultiHash): Result[Cid, CidError] =
|
||||||
## Create new content identifier using content type ``content`` and
|
## Create new content identifier using content type ``content`` and
|
||||||
## MultiHash ``hash`` using version ``version``.
|
## MultiHash ``hash`` using version ``version``.
|
||||||
##
|
##
|
||||||
|
@ -188,33 +199,35 @@ proc init*(ctype: typedesc[Cid], version: CidVersion, content: MultiCodec,
|
||||||
## Cid.init(CIDv0, multiCodec("dag-pb"), MultiHash.digest("sha2-256", data))
|
## Cid.init(CIDv0, multiCodec("dag-pb"), MultiHash.digest("sha2-256", data))
|
||||||
##
|
##
|
||||||
## All other encodings and hashes are not supported by CIDv0.
|
## All other encodings and hashes are not supported by CIDv0.
|
||||||
result.cidver = version
|
|
||||||
|
var res: Cid
|
||||||
|
res.cidver = version
|
||||||
|
|
||||||
if version == CIDv0:
|
if version == CIDv0:
|
||||||
if content != multiCodec("dag-pb"):
|
if content != multiCodec("dag-pb"):
|
||||||
raise newException(CidError,
|
return err(CidError.Unsupported)
|
||||||
"CIDv0 supports only `dag-pb` content type!")
|
res.data = initVBuffer()
|
||||||
result.data = initVBuffer()
|
|
||||||
if hash.mcodec != multiCodec("sha2-256"):
|
if hash.mcodec != multiCodec("sha2-256"):
|
||||||
raise newException(CidError,
|
return err(CidError.Unsupported)
|
||||||
"CIDv0 supports only `sha2-256` hash digest!")
|
res.mcodec = content
|
||||||
result.mcodec = content
|
res.data.write(hash)
|
||||||
result.data.write(hash)
|
res.data.finish()
|
||||||
result.data.finish()
|
return ok(res)
|
||||||
elif version == CIDv1:
|
elif version == CIDv1:
|
||||||
let mcodec = CodeContentIds.getOrDefault(cast[int](content),
|
let mcodec = CodeContentIds.getOrDefault(cast[int](content),
|
||||||
InvalidMultiCodec)
|
InvalidMultiCodec)
|
||||||
if mcodec == InvalidMultiCodec:
|
if mcodec == InvalidMultiCodec:
|
||||||
raise newException(CidError, "Incorrect content type")
|
return err(CidError.Incorrect)
|
||||||
result.mcodec = mcodec
|
res.mcodec = mcodec
|
||||||
result.data = initVBuffer()
|
res.data = initVBuffer()
|
||||||
result.data.writeVarint(cast[uint64](1))
|
res.data.writeVarint(cast[uint64](1))
|
||||||
result.data.write(mcodec)
|
res.data.write(mcodec)
|
||||||
result.hpos = len(result.data.buffer)
|
res.hpos = len(res.data.buffer)
|
||||||
result.data.write(hash)
|
res.data.write(hash)
|
||||||
result.data.finish()
|
res.data.finish()
|
||||||
|
return ok(res)
|
||||||
else:
|
else:
|
||||||
raise newException(CidError, "CID version is not supported" & $version)
|
return err(CidError.Unsupported)
|
||||||
|
|
||||||
proc `==`*(a: Cid, b: Cid): bool =
|
proc `==`*(a: Cid, b: Cid): bool =
|
||||||
## Compares content identifiers ``a`` and ``b``, returns ``true`` if hashes
|
## Compares content identifiers ``a`` and ``b``, returns ``true`` if hashes
|
||||||
|
@ -258,6 +271,12 @@ proc encode*(mbtype: typedesc[MultiBase], encoding: string,
|
||||||
proc `$`*(cid: Cid): string =
|
proc `$`*(cid: Cid): string =
|
||||||
## Return official string representation of content identifier ``cid``.
|
## Return official string representation of content identifier ``cid``.
|
||||||
if cid.cidver == CIDv0:
|
if cid.cidver == CIDv0:
|
||||||
result = BTCBase58.encode(cid.data.buffer)
|
BTCBase58.encode(cid.data.buffer)
|
||||||
elif cid.cidver == CIDv1:
|
elif cid.cidver == CIDv1:
|
||||||
result = Multibase.encode("base58btc", cid.data.buffer).tryGet()
|
let res = Multibase.encode("base58btc", cid.data.buffer)
|
||||||
|
if res.isOk():
|
||||||
|
res.get()
|
||||||
|
else:
|
||||||
|
""
|
||||||
|
else:
|
||||||
|
""
|
||||||
|
|
|
@ -7,29 +7,24 @@ suite "Content identifier CID test suite":
|
||||||
|
|
||||||
test "CIDv0 test vector":
|
test "CIDv0 test vector":
|
||||||
var cid0Text = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n"
|
var cid0Text = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n"
|
||||||
var cid0 = Cid.init(cid0Text)
|
var cid0 = Cid.init(cid0Text).tryGet()
|
||||||
check:
|
check:
|
||||||
$cid0 == cid0Text
|
$cid0 == cid0Text
|
||||||
cid0.version() == CIDv0
|
cid0.version() == CIDv0
|
||||||
cid0.contentType() == multiCodec("dag-pb")
|
cid0.contentType().tryGet() == multiCodec("dag-pb")
|
||||||
cid0.mhash().mcodec == multiCodec("sha2-256")
|
cid0.mhash().tryGet().mcodec == multiCodec("sha2-256")
|
||||||
var res = 0
|
Cid.init("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zIII").isErr()
|
||||||
try:
|
|
||||||
discard Cid.init("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zIII")
|
|
||||||
except CidError:
|
|
||||||
res = 1
|
|
||||||
check res == 1
|
|
||||||
|
|
||||||
test "CIDv1 test vector":
|
test "CIDv1 test vector":
|
||||||
var cid1Text = "zb2rhhFAEMepUBbGyP1k8tGfz7BSciKXP6GHuUeUsJBaK6cqG"
|
var cid1Text = "zb2rhhFAEMepUBbGyP1k8tGfz7BSciKXP6GHuUeUsJBaK6cqG"
|
||||||
var chex = "015512209D8453505BDC6F269678E16B3E56" &
|
var chex = "015512209D8453505BDC6F269678E16B3E56" &
|
||||||
"C2A2948A41F2C792617CC9611ED363C95B63"
|
"C2A2948A41F2C792617CC9611ED363C95B63"
|
||||||
var cid1 = Cid.init(cid1Text)
|
var cid1 = Cid.init(cid1Text).tryGet()
|
||||||
check:
|
check:
|
||||||
$cid1 == cid1Text
|
$cid1 == cid1Text
|
||||||
cid1.version() == CIDv1
|
cid1.version() == CIDv1
|
||||||
cid1.contentType() == multiCodec("raw")
|
cid1.contentType().tryGet() == multiCodec("raw")
|
||||||
cid1.mhash().mcodec == multiCodec("sha2-256")
|
cid1.mhash().tryGet().mcodec == multiCodec("sha2-256")
|
||||||
hex(cid1) == chex
|
hex(cid1) == chex
|
||||||
|
|
||||||
test "Comparison test":
|
test "Comparison test":
|
||||||
|
@ -38,17 +33,17 @@ suite "Content identifier CID test suite":
|
||||||
var bmsg = cast[seq[byte]](msg)
|
var bmsg = cast[seq[byte]](msg)
|
||||||
var bmmsg = cast[seq[byte]](mmsg)
|
var bmmsg = cast[seq[byte]](mmsg)
|
||||||
var cid0 = Cid.init(CIDv0, multiCodec("dag-pb"),
|
var cid0 = Cid.init(CIDv0, multiCodec("dag-pb"),
|
||||||
MultiHash.digest("sha2-256", bmsg).get())
|
MultiHash.digest("sha2-256", bmsg).get()).tryGet()
|
||||||
var cid1 = Cid.init(CIDv1, multiCodec("dag-pb"),
|
var cid1 = Cid.init(CIDv1, multiCodec("dag-pb"),
|
||||||
MultiHash.digest("sha2-256", bmsg).get())
|
MultiHash.digest("sha2-256", bmsg).get()).tryGet()
|
||||||
var cid2 = cid1
|
var cid2 = cid1
|
||||||
var cid3 = cid0
|
var cid3 = cid0
|
||||||
var cid4 = Cid.init(CIDv1, multiCodec("dag-cbor"),
|
var cid4 = Cid.init(CIDv1, multiCodec("dag-cbor"),
|
||||||
MultiHash.digest("sha2-256", bmsg).get())
|
MultiHash.digest("sha2-256", bmsg).get()).tryGet()
|
||||||
var cid5 = Cid.init(CIDv1, multiCodec("dag-pb"),
|
var cid5 = Cid.init(CIDv1, multiCodec("dag-pb"),
|
||||||
MultiHash.digest("sha2-256", bmmsg).get())
|
MultiHash.digest("sha2-256", bmmsg).get()).tryGet()
|
||||||
var cid6 = Cid.init(CIDv1, multiCodec("dag-pb"),
|
var cid6 = Cid.init(CIDv1, multiCodec("dag-pb"),
|
||||||
MultiHash.digest("keccak-256", bmsg).get())
|
MultiHash.digest("keccak-256", bmsg).get()).tryGet()
|
||||||
check:
|
check:
|
||||||
cid0 == cid1
|
cid0 == cid1
|
||||||
cid1 == cid2
|
cid1 == cid2
|
||||||
|
|
Loading…
Reference in New Issue