nim-libp2p-experimental/libp2p/peerid.nim

218 lines
6.1 KiB
Nim

## 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 API for libp2p peer.
{.push raises: [Defect].}
import
std/[hashes, strutils],
stew/[base58, results],
chronicles,
nimcrypto/utils,
./crypto/crypto, ./multicodec, ./multihash, ./vbuffer,
./protobuf/minprotobuf
export results
const
maxInlineKeyLength* = 42
type
PeerID* = object
data*: seq[byte]
func `$`*(pid: PeerID): string =
## Return base58 encoded ``pid`` representation.
# This unusual call syntax is used to avoid a strange Nim compilation error
base58.encode(Base58, pid.data)
func shortLog*(pid: PeerId): string =
## Returns compact string representation of ``pid``.
var spid = $pid
if len(spid) > 10:
spid[3] = '*'
spid.delete(4, spid.high - 6)
spid
chronicles.formatIt(PeerID): shortLog(it)
func toBytes*(pid: PeerID, data: var openarray[byte]): int =
## Store PeerID ``pid`` to array of bytes ``data``.
##
## Returns number of bytes needed to store ``pid``.
result = len(pid.data)
if len(data) >= result and result > 0:
copyMem(addr data[0], unsafeAddr pid.data[0], result)
template getBytes*(pid: PeerID): seq[byte] =
## Return PeerID ``pid`` as array of bytes.
pid.data
func hex*(pid: PeerID): string =
## Returns hexadecimal string representation of ``pid``.
toHex(pid.data)
template len*(pid: PeerID): int =
## Returns length of ``pid`` binary representation.
len(pid.data)
func cmp*(a, b: PeerID): int =
## Compares two peer ids ``a`` and ``b``.
## Returns:
##
## | 0 iff a == b
## | < 0 iff a < b
## | > 0 iff a > b
var i = 0
var m = min(len(a.data), len(b.data))
while i < m:
result = ord(a.data[i]) - ord(b.data[i])
if result != 0: return
inc(i)
result = len(a.data) - len(b.data)
template `<=`*(a, b: PeerID): bool =
(cmp(a, b) <= 0)
template `<`*(a, b: PeerID): bool =
(cmp(a, b) < 0)
template `>=`*(a, b: PeerID): bool =
(cmp(a, b) >= 0)
template `>`*(a, b: PeerID): bool =
(cmp(a, b) > 0)
template `==`*(a, b: PeerID): bool =
(cmp(a, b) == 0)
template hash*(pid: PeerID): Hash =
hash(pid.data)
func validate*(pid: PeerID): bool =
## Validate check if ``pid`` is empty or not.
len(pid.data) > 0 and MultiHash.validate(pid.data)
func hasPublicKey*(pid: PeerID): bool =
## Returns ``true`` if ``pid`` is small enough to hold public key inside.
if len(pid.data) > 0:
var mh: MultiHash
if MultiHash.decode(pid.data, mh).isOk:
if mh.mcodec == multiCodec("identity"):
result = true
func extractPublicKey*(pid: PeerID, pubkey: var PublicKey): bool =
## Returns ``true`` if public key was successfully decoded from PeerID
## ``pid``and stored to ``pubkey``.
##
## Returns ``false`` otherwise.
if len(pid.data) > 0:
var mh: MultiHash
if MultiHash.decode(pid.data, mh).isOk:
if mh.mcodec == multiCodec("identity"):
let length = len(mh.data.buffer)
result = pubkey.init(mh.data.buffer.toOpenArray(mh.dpos, length - 1))
func init*(pid: var PeerID, data: openarray[byte]): bool =
## Initialize peer id from raw binary representation ``data``.
##
## Returns ``true`` if peer was successfully initialiazed.
var p = PeerID(data: @data)
if p.validate():
pid = p
result = true
func init*(pid: var PeerID, data: string): bool =
## Initialize peer id from base58 encoded string representation.
##
## Returns ``true`` if peer was successfully initialiazed.
var p = newSeq[byte](len(data) + 4)
var length = 0
if Base58.decode(data, p, length) == Base58Status.Success:
p.setLen(length)
var opid: PeerID
shallowCopy(opid.data, p)
if opid.validate():
pid = opid
result = true
func init*(t: typedesc[PeerID], data: openarray[byte]): Result[PeerID, cstring] =
## Create new peer id from raw binary representation ``data``.
var res: PeerID
if not init(res, data):
err("peerid: incorrect PeerID binary form")
else:
ok(res)
func init*(t: typedesc[PeerID], data: string): Result[PeerID, cstring] =
## Create new peer id from base58 encoded string representation ``data``.
var res: PeerID
if not init(res, data):
err("peerid: incorrect PeerID string")
else:
ok(res)
func init*(t: typedesc[PeerID], pubkey: PublicKey): Result[PeerID, cstring] =
## Create new peer id from public key ``pubkey``.
var pubraw = ? pubkey.getBytes().orError(
cstring("peerid: failed to get bytes from given key"))
var mh: MultiHash
if len(pubraw) <= maxInlineKeyLength:
mh = ? MultiHash.digest("identity", pubraw)
else:
mh = ? MultiHash.digest("sha2-256", pubraw)
ok(PeerID(data: mh.data.buffer))
func init*(t: typedesc[PeerID], seckey: PrivateKey): Result[PeerID, cstring] =
## Create new peer id from private key ``seckey``.
PeerID.init(? seckey.getPublicKey().orError(cstring("invalid private key")))
func match*(pid: PeerID, pubkey: PublicKey): bool =
## Returns ``true`` if ``pid`` matches public key ``pubkey``.
let p = PeerID.init(pubkey)
if p.isErr:
false
else:
pid == p.get()
func match*(pid: PeerID, seckey: PrivateKey): bool =
## Returns ``true`` if ``pid`` matches private key ``seckey``.
let p = PeerID.init(seckey)
if p.isErr:
false
else:
pid == p.get()
## Serialization/Deserialization helpers
func write*(vb: var VBuffer, pid: PeerID) =
## Write PeerID value ``peerid`` to buffer ``vb``.
vb.writeSeq(pid.data)
func write*(pb: var ProtoBuffer, field: int, pid: PeerID) =
## Write PeerID value ``peerid`` to object ``pb`` using ProtoBuf's encoding.
write(pb, field, pid.data)
func getField*(pb: ProtoBuffer, field: int,
pid: var PeerID): ProtoResult[bool] {.inline.} =
## Read ``PeerID`` from ProtoBuf's message and validate it
var buffer: seq[byte]
let res = ? pb.getField(field, buffer)
if not(res):
ok(false)
else:
var peerId: PeerID
if peerId.init(buffer):
pid = peerId
ok(true)
else:
err(ProtoError.IncorrectBlob)