CIDv0 support

This commit is contained in:
Ehmry - 2018-08-10 20:18:02 +02:00
parent 3d0bbca3b4
commit 7203eb2a65
21 changed files with 729 additions and 1271 deletions

View File

@ -1,60 +1,8 @@
# WARNING
An IPFS library. No support for Protocol Buffers, which means no support
for the current IPFS file-system.
Contains my own contrived standards and formats that will break often.
Upstream development on CBOR file-system structures has predictably stalled
over discussion of features such as non-ASCII, non-unicode file-names
and extended attributes.
## ipldrepl
A Lisp REPL utility for storing files and directories in IPLD.
### Functions
#### `(apply <function> <list>)`
Standard Lisp `apply` function, apply a list as arguments to a function.
#### `(cbor <cid>)`
Return CBOR encoding of UnixFS node as a diagnostic string.
Provided for illustrating canonicalized CBOR encoding.
#### `(cons <head> <tail>)`
Standard Lisp `cons` function, prepend to a list.
#### `(copy <cid> <from> <to>)`
Duplicate a directory entry.
#### `(define <symbol> <value>)`
Bind a value to a symbol. Returns value.
#### `(glob <glob-string> <...>)`
Return a list of paths matching a Unix-style glob string.
#### `(ingest <path> <...>)`
Ingest a list of paths to the store, returning a CID to a directory with
the contents each path under the trailing name of the path.
#### `(map <function> <list>)`
Standard Lisp `map`, apply a function to each member of a list.
#### `(merge <cid> <...>)`
Merge a list of root directories represented by CIDs into a new root.
Members of the root are not merged recursively.
#### `(path <string>)`
Convert a string to a path, if the path is valid and present.
#### `(root <string> <cid>)`
Create a new root contaning the giving CID at the given name.
#### `(walk <cid> <string>)`
Walk a path down one CID to another.
Current plan is to fork, postponed indefinitly.

File diff suppressed because it is too large Load Diff

View File

@ -2,11 +2,12 @@
version = "0.1.1"
author = "Emery Hemingway"
description = "IPLD library"
description = "InterPlanetary Linked Data library"
license = "GPLv3"
srcDir = "src"
# Dependencies
requires "nim >= 0.17.3", "nimSHA2", "base58", "cbor >= 0.2.0"
requires "nim >= 0.18.1", "nimSHA2", "base58", "cbor >= 0.2.0"
bin = @["ipld/ipldrepl","ipld/ipldcat"]
bin = @["src/ipld/ipldrepl","src/ipld/ipldcat"]

View File

@ -1,170 +0,0 @@
import nimSHA2, streams, multiformats, base58.bitcoin, cbor, hex, hashes, blake2
const MaxBlockSize* = 1 shl 18
## Maximum supported block size.
type Cid* = object
## Content IDentifier, used to identify blocks.
digest*: seq[uint8]
hash*: MulticodecTag
codec*: MulticodecTag
ver*: int
proc initCid*(): Cid =
## Initialize an invalid CID.
Cid(hash: MulticodecTag.Invalid, codec: MulticodecTag.Invalid)
proc isValid*(x: Cid): bool =
## Check that a CID has been properly initialized.
x.hash != MulticodecTag.Invalid
proc `==`*(x, y: Cid): bool =
## Compare two CIDs. Must be of the same type and
## use the same hash algorithm.
result =
x.ver == y.ver and
x.codec == y.codec and
x.hash == y.hash and
x.digest == y.digest
proc hash*(cid: Cid): Hash =
## Reduce a CID into an integer for use in tables.
result = hash cid.digest
result = result !& cid.ver !& cid.codec.int !& cid.hash.int
result = !$result
proc isRaw*(cid: Cid): bool =
## Test if a CID represents a raw block.
cid.codec == MulticodecTag.Raw
proc isDagCbor*(cid: Cid): bool =
## Test if a CID represents CBOR encoded data.
cid.codec == MulticodecTag.DagCbor
proc toBin(cid: Cid): string =
let s = newStringStream()
s.writeUvarint cid.ver
s.writeUvarint cid.codec.int
s.writeUvarint cid.hash.int
s.writeUvarint cid.digest.len
for e in cid.digest:
s.write e
s.setPosition 0
result = s.readAll
close s
proc toRaw*(cid: Cid): string =
## Return CID encoded in binary.
MultibaseTag.Identity.char & cid.toBIn
proc newCborBytes*(cid: Cid): CborNode = newCborBytes cid.toRaw
## Generate a CBOR representation of a CID.
proc toHex*(cid: Cid): string =
## Return CID encoded in hexidecimal.
MultibaseTag.Base16.char & hex.encode(cid.toBin)
proc toBase58*(cid: Cid): string =
## Return CID encoded in base 58.
MultibaseTag.Base58btc.char & bitcoin.encode(cid.toBin)
proc `$`*(cid: Cid): string = cid.toBase58
## Return CID in base 58, the default textual encoding.
proc parseCid*(s: string): Cid =
## Detect CID encoding and parse from a string.
if unlikely(s.len < (1+1+1+1)):
raise newException(ValueError, "too short to be a valid CID")
var
raw: string
off: int
codec, hash: int
digestLen: int
case s[0].MultibaseTag
of MultibaseTag.Identity:
raw = s
off = 1
of MultibaseTag.Base16, MultibaseTag.InconsistentBase16:
raw = hex.decode(s[1..s.high])
if unlikely(raw.isNil):
raise newException(ValueError, "not a CID")
of MultibaseTag.Base58btc:
raw = bitcoin.decode(s[1..s.high])
else:
raise newException(ValueError, "unknown multibase encoding tag")
off.inc parseUvarint(raw, result.ver, off)
off.inc parseUvarint(raw, codec, off)
off.inc parseUvarint(raw, hash, off)
off.inc parseUvarint(raw, digestLen, off)
if unlikely(off + digestLen != raw.len):
raise newException(ValueError, "invalid multihash length")
result.digest = newSeq[uint8](digestLen)
for i in 0..<digestLen:
result.digest[i] = (uint8)raw[i+off]
result.hash = hash.MulticodecTag
result.codec = codec.MulticodecTag
proc CidSha256*(data: string; codec = MulticodecTag.Raw): Cid =
## Generate a CID for a string of data using the SHA 256 hash algorithm.
let sha = computeSHA256(data)
result.digest = newSeq[uint8](32)
for i in 0..31:
result.digest[i] = (uint8)sha[i]
result.hash = MulticodecTag.Sha2_256
result.codec = codec
result.ver = 1
proc CidBlake2b256*(data: string; codec = MulticodecTag.Raw): Cid =
## Generate a CID for a string of data using the BLAKE2b hash algorithm.
var b: Blake2b
blake2b_init(b, 32, nil, 0)
blake2b_update(b, data, data.len)
result.digest = blake2b_final(b)
result.hash = MulticodecTag.Blake2b_256
result.codec = codec
result.ver = 1
proc verify*(cid: Cid; data: string): bool =
## Verify that a string of data corresponds to a CID.
case cid.hash
of MulticodecTag.Sha2_256:
let sha = computeSHA256(data)
for i in 0..31:
if cid.digest[i] != (uint8)sha[i]:
return false
result = true
of MulticodecTag.Blake2b_256:
var b: Blake2b
blake2b_init(b, 32, nil, 0)
blake2b_update(b, data, data.len)
let digest = blake2b_final(b)
result = (cid.digest == digest)
else:
raise newException(ValueError, "unknown hash type " & $cid.hash)
iterator simpleChunks*(s: Stream; size = 256 * 1024): string =
## Iterator that breaks a stream into simple chunks.
while not s.atEnd:
yield s.readStr size
when isMainModule:
import times
let data = newString MaxBlockSize
block sha256:
var i = 0
let t0 = cpuTime()
while i < 100:
discard CidSha256 data
inc i
let d = cpuTime() - t0
echo "SHA256 ticks: ", d
block blake2b:
var i = 0
let t0 = cpuTime()
while i < 100:
discard CidBlake2b256 data
inc i
let d = cpuTime() - t0
echo "BLAKE2b ticks: ", d

235
src/ipld.nim Normal file
View File

@ -0,0 +1,235 @@
import std/hashes, std/streams, std/strutils
import nimSHA2, ./ipld/multiformats, base58/bitcoin, cbor, ./ipld/priv/hex, ./ipld/priv/blake2
const MaxBlockSize* = 1 shl 18
## Maximum supported block size.
type
CidVersion* = enum CIDv0, CIDv1
Cid* = object
## Content IDentifier, used to identify blocks.
case kind*: CidVersion
of CIDv0:
sha256: array[32, uint8]
of CIDv1:
digest*: seq[uint8] # this is stupid, make it a fixed size
hash*: MulticodecTag
codec*: MulticodecTag
ver*: int
proc initCid*(): Cid =
## Initialize an invalid CID.
Cid(kind: CIDv1, hash: MulticodecTag.Invalid, codec: MulticodecTag.Invalid)
proc isValid*(x: Cid): bool =
## Check that a CID has been properly initialized.
case x.kind
of CIDv0: true # whatever
of CIDv1: x.hash != MulticodecTag.Invalid
proc `==`*(x, y: Cid): bool =
## Compare two CIDs. Must be of the same type and
## use the same hash algorithm.
if x.kind == y.kind:
case x.kind
of CIDv0:
result = (x.sha256 == y.sha256)
of CIDv1:
result = (
x.ver == y.ver and
x.codec == y.codec and
x.hash == y.hash and
x.digest == y.digest)
proc hash*(cid: Cid): Hash =
## Reduce a CID into an integer for use in tables.
case cid.kind
of CIDv0:
result = hash cid.sha256
of CIDv1:
result = hash cid.digest
result = result !& cid.ver !& cid.codec.int !& cid.hash.int
result = !$result
proc isRaw*(cid: Cid): bool =
## Test if a CID represents a raw block.
cid.kind == CIDv1 and cid.codec == MulticodecTag.Raw
proc isDag*(cid: Cid): bool =
## Test if a CID represents protobuf or CBOR encoded data.
cid.kind == CIDv0 or cid.codec in {MulticodecTag.DagPb, MulticodecTag.DagCbor}
proc isDagPb*(cid: Cid): bool =
## Test if a CID represents protobuf encoded data.
cid.kind == CIDv0 or cid.codec == MulticodecTag.DagPb
proc isDagCbor*(cid: Cid): bool =
## Test if a CID represents CBOR encoded data.
cid.kind == CIDv1 and cid.codec == MulticodecTag.DagCbor
proc toBin(cid: Cid): string =
case cid.kind
of CIDv0:
result = newString(34)
result[0] = 0x12.char
result[1] = 0x20.char
var sha = cid.sha256
copyMem(result[2].addr, sha[0].addr, 32)
of CIDv1:
let s = newStringStream()
s.writeUvarint cid.ver
s.writeUvarint cid.codec.int
s.writeUvarint cid.hash.int
s.writeUvarint cid.digest.len
for e in cid.digest:
s.write e
s.setPosition 0
result = s.readAll
close s
proc toRaw*(cid: Cid): string =
## Return CID encoded in binary.
case cid.kind
of CIDv0:
cid.toBin
of CIDv1:
MultibaseTag.Identity.char & cid.toBIn
proc newCborBytes*(cid: Cid): CborNode = newCborBytes cid.toRaw
## Generate a CBOR representation of a CID.
proc toHex*(cid: Cid): string =
## Return CID encoded in hexidecimal.
assert(isValid cid)
MultibaseTag.Base16.char & hex.encode(cid.toBin)
proc toBase58*(cid: Cid): string =
## Return CID encoded in base 58.
assert(isValid cid)
case cid.kind
of CIDv0:
bitcoin.encode(cid.toBin)
of CIDv1:
MultibaseTag.Base58btc.char & bitcoin.encode(cid.toBin)
proc `$`*(cid: Cid): string =
## Return CID in base 58, the default textual encoding.
cid.toBase58
proc parseCid*(s: string): Cid =
## Detect CID encoding and parse from a string.
if unlikely(s.len < (1+1+1+1)):
raise newException(ValueError, "too short to be a valid CID")
var
raw: string
off: int
codec, hash: int
digestLen: int
if s.len == 46 and s.startsWith "Qm":
var data = bitcoin.decode(s)
if data.len == 34 and data.startsWith "\x12\x20":
result.kind = CIDv0
copyMem(result.sha256[0].addr, data[2].addr, 32)
else:
raise newException(ValueError, "invalid CIDv0")
else:
case s[0].MultibaseTag
of MultibaseTag.Identity:
raw = s
off = 1
of MultibaseTag.Base16, MultibaseTag.InconsistentBase16:
raw = hex.decode(s[1..s.high])
if unlikely(raw.isNil):
raise newException(ValueError, "not a CID")
of MultibaseTag.Base58btc:
raw = bitcoin.decode(s[1..s.high])
else:
raise newException(ValueError, "unknown multibase encoding tag")
off.inc parseUvarint(raw, result.ver, off)
off.inc parseUvarint(raw, codec, off)
off.inc parseUvarint(raw, hash, off)
off.inc parseUvarint(raw, digestLen, off)
if unlikely(off + digestLen != raw.len):
raise newException(ValueError, "invalid multihash length")
result.kind = CIDv0
result.digest = newSeq[uint8](digestLen)
for i in 0..<digestLen:
result.digest[i] = (uint8)raw[i+off]
result.hash = hash.MulticodecTag
result.codec = codec.MulticodecTag
proc CidSha256*(data: string; codec = MulticodecTag.Raw): Cid =
## Generate a CID for a string of data using the SHA 256 hash algorithm.
result.kind = CIDv1
let sha = computeSHA256(data)
result.digest = newSeq[uint8](32)
for i in 0..31:
result.digest[i] = (uint8)sha[i]
result.hash = MulticodecTag.Sha2_256
result.codec = codec
result.ver = 1
proc CidBlake2b256*(data: string; codec = MulticodecTag.Raw): Cid =
## Generate a CID for a string of data using the BLAKE2b hash algorithm.
result.kind = CIDv1
var b: Blake2b
blake2b_init(b, 32, nil, 0)
blake2b_update(b, data, data.len)
result.digest = blake2b_final(b)
result.hash = MulticodecTag.Blake2b_256
result.codec = codec
result.ver = 1
proc verify*(cid: Cid; data: string): bool =
## Verify that a string of data corresponds to a CID.
case cid.kind
of CIDv0:
let sha = computeSHA256(data)
for i in 0..31:
if cid.sha256[i] != (uint8)sha[i]:
return false
result = true
of CIDv1:
case cid.hash
of MulticodecTag.Sha2_256:
let sha = computeSHA256(data)
for i in 0..31:
if cid.digest[i] != (uint8)sha[i]:
return false
result = true
of MulticodecTag.Blake2b_256:
var b: Blake2b
blake2b_init(b, 32, nil, 0)
blake2b_update(b, data, data.len)
let digest = blake2b_final(b)
result = (cid.digest == digest)
else:
raise newException(ValueError, "unknown hash type " & $cid.hash)
iterator simpleChunks*(s: Stream; size = 256 * 1024): string =
## Iterator that breaks a stream into simple chunks.
while not s.atEnd:
yield s.readStr size
when isMainModule:
import times
let data = newString MaxBlockSize
block sha256:
var i = 0
let t0 = cpuTime()
while i < 100:
discard CidSha256 data
inc i
let d = cpuTime() - t0
echo "SHA256 ticks: ", d
block blake2b:
var i = 0
let t0 = cpuTime()
while i < 100:
discard CidBlake2b256 data
inc i
let d = cpuTime() - t0
echo "BLAKE2b ticks: ", d

View File

@ -0,0 +1,28 @@
/*
* \brief C++ base of IPLD client
* \author Emery Hemingway
* \date 2017-11-08
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <ipld_session/connection.h>
#include <base/heap.h>
struct IpldClientBase
{
Genode::Heap heap;
Genode::Allocator_avl tx_packet_alloc { &heap };
Ipld::Connection conn;
IpldClientBase(Genode::Env *env, char const *label, Genode::size_t tx_buf_size)
: heap(env->pd(), env->rm()),
conn(*env, tx_packet_alloc, label, tx_buf_size)
{ }
};

View File

@ -0,0 +1,147 @@
#
# \brief IpldStore interface to the IPLD session
# \author Emery Hemingway
# \date 2017-11-04
#
#
# Copyright (C) 2017 Genode Labs GmbH
#
# This file is part of the Genode OS framework, which is distributed
# under the terms of the GNU Affero General Public License version 3.
#
when not defined(genode):
{.error: "Genode only IPLD client".}
import cbor, genode, tables, strutils
import ipld, ipld/multiformats, ipld/store, ipld/genode/ipldsession
const
currentPath = currentSourcePath.rsplit("/", 1)[0]
ipldClientH = currentPath & "/ipldclient.h"
{.passC: "-I" & currentPath.}
type
IpldClientBase {.importcpp, header: ipldClientH.} = object
IpldClientCpp = Constructible[IpldClientBase]
proc sigh_ack_avail(cpp: IpldClientCpp; sig: SignalContextCapability) {.
importcpp: "#->conn.channel().sigh_ack_avail(@)", tags: [RpcEffect].}
proc readyToSubmit(cpp: IpldClientCpp): bool {.
importcpp: "#->conn.source().ready_to_submit()".}
proc readyToAck(cpp: IpldClientCpp): bool {.
importcpp: "#->conn.source().ready_to_ack()".}
proc ackAvail(cpp: IpldClientCpp): bool {.
importcpp: "#->conn.source().ack_avail()".}
proc allocPacket(cpp: IpldClientCpp; size = MaxPacketSize): IpldPacket {.
importcpp: "#->conn.source().alloc_packet(@)".}
proc packetContent(cpp: IpldClientCpp; pkt: IpldPacket): pointer {.
importcpp: "#->conn.source().packet_content(@)".}
proc submitPacket(cpp: IpldClientCpp; pkt: IpldPacket; cid: cstring; op: IpldOpcode) {.
importcpp: "#->conn.source().submit_packet(Ipld::Packet(#, (char const *)#, #))".}
proc getAckedPacket(cpp: IpldClientCpp): IpldPacket {.
importcpp: "#->conn.source().get_acked_packet()".}
proc releasePacket(cpp: IpldClientCpp; pkt: IpldPacket) {.
importcpp: "#->conn.source().release_packet(@)".}
type
IpldClient* = ref IpldClientObj
IpldClientObj = object of IpldStoreObj
## IPLD session client
cpp: IpldClientCpp
proc icClose(s: IpldStore) =
var ic = IpldClient(s)
destruct ic.cpp
proc icPut(s: IpldStore; blk: string; hash: MulticodecTag): Cid =
## Put block to Ipld server, blocks for two packet round-trip.
let ic = IpldClient(s)
var blk = blk
var pktCid = initCid()
pktCid.hash = hash
assert(ic.cpp.readyToSubmit, "Ipld client packet queue congested")
var pkt = ic.cpp.allocPacket(blk.len)
let pktBuf = ic.cpp.packetContent pkt
defer: ic.cpp.releasePacket pkt
assert(not pktBuf.isNil, "allocated packet has nil content")
assert(pkt.size >= blk.len)
pkt.setLen blk.len
copyMem(pktBuf, blk[0].addr, blk.len)
assert(ic.cpp.readyToSubmit, "Ipld client packet queue congested")
ic.cpp.submitPacket(pkt, pktCid.toHex, PUT)
let ack = ic.cpp.getAckedPacket()
doAssert(ack.error == OK)
result = ack.cid()
assert(result.isValid, "server returned a packet with and invalid CID")
proc icGetBuffer(s: IpldStore; cid: Cid; buf: pointer; len: Natural): int =
## Get from Ipld server, blocks for packet round-trip.
let ic = IpldClient(s)
assert(ic.cpp.readyToSubmit, "Ipld client packet queue congested")
let pkt = ic.cpp.allocPacket len
ic.cpp.submitPacket(pkt, cid.toHex, GET)
let ack = ic.cpp.getAckedPacket
doAssert(ack.cid == cid)
if ack.error == OK:
let pktBuf = ic.cpp.packetContent ack
assert(not pktBuf.isNil, "ack packet has nil content")
assert(ack.len <= len)
assert(ack.len > 0)
result = ack.len
copyMem(buf, pktBuf, result)
if pkt.size > 0:
ic.cpp.releasePacket pkt
# free the original packet that was allocated
case ack.error:
of OK: discard
of MISSING:
raise cid.newMissingObject
else:
raise newException(SystemError, "Ipld packet error " & $ack.error)
proc icGet(s: IpldStore; cid: Cid; result: var string) =
## Get from Ipld server, blocks for packet round-trip.
let ic = IpldClient(s)
assert(ic.cpp.readyToSubmit, "Ipld client packet queue congested")
let pkt = ic.cpp.allocPacket()
defer: ic.cpp.releasePacket pkt
ic.cpp.submitPacket(pkt, cid.toHex, GET)
let ack = ic.cpp.getAckedPacket()
doAssert(ack.cid == cid)
case ack.error:
of OK:
let ackBuf = ic.cpp.packetContent ack
assert(not ackBuf.isNil)
assert(ack.len > 0)
result.setLen ack.len
copyMem(result[0].addr, ackBuf, result.len)
assert(cid.verify(result), "Ipld client packet failed verification")
of MISSING:
raise cid.newMissingObject
else:
raise newException(SystemError, "Ipld packet error " & $ack.error)
const
DefaultIpldBufferSize* = 1 shl 20
proc newIpldClient*(env: GenodeEnv; label = ""; bufferSize = DefaultIpldBufferSize): IpldClient =
## Blocks retrieved by `get` are not verified.
proc construct(cpp: IpldClientCpp; env: GenodeEnv; label: cstring; txBufSize: int) {.
importcpp.}
new result
construct(result.cpp, env, label, bufferSize)
result.closeImpl = icClose
result.putImpl = icPut
result.getBufferImpl = icGetBuffer
result.getImpl = icGet

View File

@ -0,0 +1,56 @@
/*
* \brief IPLD C++ session component
* \author Emery Hemingway
* \date 2017-11-07
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__NIM__IPLDSERVER_H_
#define _INCLUDE__NIM__IPLDSERVER_H_
#include <ipld_session/rpc_object.h>
#include <base/heap.h>
#include <base/attached_ram_dataspace.h>
struct Communication_buffer
{
Genode::Attached_ram_dataspace _tx_ds;
Communication_buffer(Genode::Pd_session &pd,
Genode::Region_map &rm,
Genode::size_t tx_buf_size)
: _tx_ds(pd, rm, tx_buf_size) { }
};
struct IpldSessionComponentBase : Communication_buffer,
Ipld::Session_rpc_object
{
static Genode::size_t tx_buf_size(char const *args)
{
Genode::size_t const buf_size = Genode::Arg_string::find_arg(
args, "tx_buf_size").aligned_size();
if (!buf_size)
throw Genode::Service_denied();
return buf_size;
}
IpldSessionComponentBase(Genode::Env *env, char const *args)
:
Communication_buffer(env->pd(), env->rm(), tx_buf_size(args)),
Session_rpc_object(env->rm(), env->ep().rpc_ep(), _tx_ds.cap())
{ }
void packetHandler(Genode::Signal_context_capability cap)
{
_tx.sigh_ready_to_ack(cap);
_tx.sigh_packet_avail(cap);
}
};
#endif /* _INCLUDE__NIM__IPLDSERVER_H_ */

View File

@ -0,0 +1,142 @@
#
# \brief IPLD server factory
# \author Emery Hemingway
# \date 2017-11-11
#
#
# Copyright (C) 2017 Genode Labs GmbH
#
# This file is part of the Genode OS framework, which is distributed
# under the terms of the GNU Affero General Public License version 3.
#
import std/strtabs, std/tables, std/xmltree, std/strutils
import cbor, genode, genode/signals, genode/servers, ipld, ipld/store, ipldsession
const
currentPath = currentSourcePath.rsplit("/", 1)[0]
ipldserverH = currentPath & "/ipldserver.h"
type
IpldSessionComponentBase {.importcpp, header: ipldserverH.} = object
SessionCpp = Constructible[IpldSessionComponentBase]
Session = ref object
cpp: SessionCpp
sig: SignalHandler
store: IpldStore
id: SessionId
label: string
proc processPacket(session: Session; pkt: var IpldPacket) =
proc packetContent(cpp: SessionCpp; pkt: IpldPacket): pointer {.
importcpp: "#->sink().packet_content(@)".}
let cid = pkt.cid
case pkt.operation
of PUT:
try:
var
pktBuf = session.cpp.packetContent pkt
heapBuf = newString pkt.len
copyMem(heapBuf[0].addr, pktBuf, heapBuf.len)
let putCid = session.store.put(heapBuf, cid.hash)
assert(putCid.isValid, "server packet returned invalid CID from put")
pkt.setCid putCid
except:
echo "unhandled PUT error ", getCurrentExceptionMsg()
pkt.setError ERROR
of GET:
try:
let
pktBuf = session.cpp.packetContent pkt
n = session.store.getBuffer(cid, pktBuf, pkt.size)
pkt.setLen n
except BufferTooSmall:
pkt.setError OVERSIZE
except MissingObject:
pkt.setError MISSING
except:
echo "unhandled GET error ", getCurrentExceptionMsg()
pkt.setError ERROR
else:
echo "invalid packet operation"
pkt.setError ERROR
proc newSession(env: GenodeEnv; store: IpldStore; id: SessionId; label, args: string): Session =
## Create a new session and packet handling procedure
let session = new Session
assert(not session.isNil)
proc construct(cpp: SessionCpp; env: GenodeEnv; args: cstring) {.importcpp.}
session.cpp.construct(env, args)
session.store = store
session.id = id
session.label = label
session.sig = env.ep.newSignalHandler do ():
proc packetAvail(cpp: SessionCpp): bool {.
importcpp: "#->sink().packet_avail()".}
proc readyToAck(cpp: SessionCpp): bool {.
importcpp: "#->sink().ready_to_ack()".}
while session.cpp.packetAvail and session.cpp.readyToAck:
proc getPacket(cpp: SessionCpp): IpldPacket {.
importcpp: "#->sink().get_packet()".}
var pkt = session.cpp.getPacket()
session.processPacket pkt
proc acknowledgePacket(cpp: SessionCpp; pkt: IpldPacket) {.
importcpp: "#->sink().acknowledge_packet(@)".}
session.cpp.acknowledgePacket(pkt)
proc packetHandler(cpp: SessionCpp; cap: SignalContextCapability) {.
importcpp: "#->packetHandler(@)".}
session.cpp.packetHandler(session.sig.cap)
result = session
proc manage(ep: Entrypoint; s: Session): IpldSessionCapability =
## Manage a session from the default entrypoint.
proc manage(ep: Entrypoint; cpp: SessionCpp): IpldSessionCapability {.
importcpp: "#.manage(*#)".}
result = ep.manage(s.cpp)
GC_ref s
proc dissolve(ep: Entrypoint; s: Session) =
## Dissolve a session from the entrypoint so that it can be freed.
proc dissolve(ep: Entrypoint; cpp: SessionCpp) {.
importcpp: "#.dissolve(*#)".}
ep.dissolve(s.cpp)
destruct(s.cpp)
dissolve(s.sig)
GC_unref s
type
IpldServer* = ref object
env: GenodeEnv
store*: IpldStore
sessions*: Table[SessionId, Session]
proc newIpldServer*(env: GenodeEnv; store: IpldStore): IpldServer =
IpldServer(
env: env, store: store,
sessions: initTable[SessionId, Session]())
proc create*(server: IpldServer; id: SessionId; label, args: string) =
if not server.sessions.contains id:
try:
let
session = newSession(server.env, server.store, id, label, args)
cap = server.env.ep.manage(session)
server.sessions[id] = session
proc deliverSession(env: GenodeEnv; id: SessionId; cap: IpldSessionCapability) {.
importcpp: "#->parent().deliver_session_cap(Genode::Parent::Server::Id{#}, #)".}
server.env.deliverSession(id, cap)
echo "session opened for ", label
except:
echo "failed to create session for '", label, "', ", getCurrentExceptionMsg()
server.env.sessionResponseDeny id
proc close*(server: IpldServer; id: SessionId) =
## Close a session at the IPLD server.
if server.sessions.contains id:
let session = server.sessions[id]
server.env.ep.dissolve(session)
server.sessions.del id
server.env.sessionResponseClose id

View File

@ -0,0 +1,47 @@
#
# \brief IPLD session definitions
# \author Emery Hemingway
# \date 2017-11-11
#
#
# Copyright (C) 2017 Genode Labs GmbH
#
# This file is part of the Genode OS framework, which is distributed
# under the terms of the GNU Affero General Public License version 3.
#
import ipld
const MaxPacketSize* = 1 shl 18;
type
IpldSessionCapability* {.final, pure,
importcpp: "Ipld::Session_capability",
header: "<ipld_session/capability.h>".} = object
IpldPacket* {.
importcpp: "Ipld::Packet",
header: "<ipld_session/ipld_session.h>".} = object
IpldOpcode* {.importcpp: "Ipld::Packet::Opcode".} = enum
PUT, GET, INVALID
IpldError* {.importcpp: "Ipld::Packet::Error".} = enum
OK, MISSING, OVERSIZE, FULL, ERROR
proc size*(pkt: IpldPacket): csize {.importcpp.}
## Physical packet size.
proc cidStr(p: IpldPacket): cstring {.importcpp: "#.cid().string()".}
proc cid*(p: IpldPacket): Cid = parseCid $p.cidStr
proc setCid*(p: var IpldPacket; cid: cstring) {.importcpp: "#.cid(@)".}
proc setCid*(p: var IpldPacket; cid: Cid) = p.setCid(cid.toHex())
proc operation*(pkt: IpldPacket): IpldOpcode {.importcpp.}
proc len*(pkt: IpldPacket): csize {.importcpp: "length".}
## Logical packet length.
proc setLen*(pkt: var IpldPacket; len: int) {.importcpp: "length".}
## Set logical packet length.
proc error*(pkt: IpldPacket): IpldError {.importcpp.}
proc setError*(pkt: var IpldPacket; err: IpldError) {.importcpp: "error".}

View File

@ -0,0 +1,47 @@
#
# \brief IPLD session definitions
# \author Emery Hemingway
# \date 2017-11-11
#
#
# Copyright (C) 2017 Genode Labs GmbH
#
# This file is part of the Genode OS framework, which is distributed
# under the terms of the GNU Affero General Public License version 3.
#
import ipld
const MaxPacketSize* = 1 shl 18;
type
IpldSessionCapability* {.final, pure,
importcpp: "Ipld::Session_capability",
header: "<ipld_session/capability.h>".} = object
IpldPacket* {.
importcpp: "Ipld::Packet",
header: "<ipld_session/ipld_session.h>".} = object
IpldOpcode* {.importcpp: "Ipld::Packet::Opcode".} = enum
PUT, GET, INVALID
IpldError* {.importcpp: "Ipld::Packet::Error".} = enum
OK, MISSING, OVERSIZE, FULL, ERROR
proc size*(pkt: IpldPacket): csize {.importcpp.}
## Physical packet size.
proc cidStr(p: IpldPacket): cstring {.importcpp: "#.cid().string()".}
proc cid*(p: IpldPacket): Cid = parseCid $p.cidStr
proc setCid*(p: var IpldPacket; cid: cstring) {.importcpp: "#.cid(@)".}
proc setCid*(p: var IpldPacket; cid: Cid) = p.setCid(cid.toHex())
proc operation*(pkt: IpldPacket): IpldOpcode {.importcpp.}
proc len*(pkt: IpldPacket): csize {.importcpp: "length".}
## Logical packet length.
proc setLen*(pkt: var IpldPacket; len: int) {.importcpp: "length".}
## Set logical packet length.
proc error*(pkt: IpldPacket): IpldError {.importcpp.}
proc setError*(pkt: var IpldPacket; err: IpldError) {.importcpp: "error".}

View File

@ -1,6 +1,6 @@
import httpclient, json, base58.bitcoin, streams, nimSHA2, cbor, tables
import httpclient, json, base58/bitcoin, streams, nimSHA2, cbor, tables
import ipld, multiformats, ipldstore, unixfs
import ../ipld, ./multiformats, ./stores, ./unixfs
type
IpfsStore* = ref IpfsStoreObj

View File

@ -1,6 +1,6 @@
import streams, os, parseopt
import ipfsdaemon, ipldstore, ipld, unixfs
import ../ipld, ./ipfsdaemon, ./stores, ./unixfs
proc readFile(store: IpldStore; s: Stream; cid: Cid) =
var chunk = ""

View File

@ -1,6 +1,6 @@
import nre, os, strutils, tables, parseopt, streams, cbor
import ipld, ipldstore, unixfs, multiformats
import ../ipld, ./stores, ./unixfs, ./multiformats
type
EvalError = object of SystemError
@ -232,7 +232,7 @@ proc print(ast: NodeRef; s: Stream) =
proc readAtom(r: Reader): Atom =
let token = r.next
try:
block:
if token[token.low] == '"':
if token[token.high] != '"':
newAtomError("invalid string '$1'" % token)
@ -241,12 +241,12 @@ proc readAtom(r: Reader): Atom =
elif token.contains DirSep:
# TODO: memoize this, store a table of paths to atoms
newAtomPath token
elif token.len > 48:
elif token.len == 46 or token.len > 48:
Atom(kind: atomCid, cid: token.parseCid)
else:
Atom(kind: atomSymbol, sym: token.normalize)
except:
newAtomError(getCurrentExceptionMsg())
#except:
# newAtomError(getCurrentExceptionMsg())
proc readForm(r: Reader): NodeRef
@ -399,7 +399,7 @@ proc lsFunc(env: Env; args: NodeObj): NodeRef =
result = newNodeList()
for n in args.walk:
let a = n.atom
if a.cid.isDagCbor:
if a.cid.isDag:
let ufsNode = env.getUnixfs a.cid
if ufsNode.isDir:
for name, u in ufsNode.items:
@ -410,7 +410,7 @@ proc lsFunc(env: Env; args: NodeObj): NodeRef =
e.append name.newAtomString.newNode
result.append e
else:
raiseAssert("ls over a raw IPLD block")
raiseAssert("ls over " & $a.cid.codec)
proc mapFunc(env: Env; args: NodeObj): NodeRef =
assertArgCount(args, 2)

View File

@ -0,0 +1 @@
-d:ssl

View File

@ -1,5 +1,4 @@
import streams, strutils, os, ipld, cbor, multiformats, hex,
ipldstore, ipldclient
import streams, strutils, os, ../ipld, cbor, ./multiformats, ./store
type
IpldReplicator* = ref IpldReplicatorObj

View File

@ -1,4 +1,6 @@
import streams, strutils, os, ipld, cbor, multiformats, hex
import std/streams, std/strutils, std/os
import cbor
import ../ipld, ./multiformats, ./priv/hex
type
MissingObject* = ref object of SystemError

View File

@ -1,6 +1,6 @@
import strutils, multiformats, streams, tables, cbor, os, hex, math
import strutils, multiformats, streams, tables, cbor, os, math
import ipld, ipldstore
import ../ipld, ./stores
type EntryKey = enum
typeKey = 1,