eris

  Source

This module provides the basic procedures and types for ERIS encoding and decoding. Encoding and decoding always requires an ErisStore receiver object and store operations are asynchronous.

ErisStore objects are implemented in additional modules.

Example:

import eris
import eris/memory_stores
import std/[asyncdispatch, streams]

let
  text = "Hello world!"
  store = newMemoryStore()
  (capA, _) = waitFor encode(store, newStringStream(text))
  (capB, _) = waitFor encode(store, newStringStream(text), convergentMode)
  (capC, _) = waitFor encode(store, newStringStream(text), convergentMode)
assert capA != capB
assert capB == capC
assert waitFor(decode(store, capA)) == waitFor(decode(store, capB))
assert waitFor(decode(store, capB)) == waitFor(decode(store, capC))
An ErisStore is implemented by get and put methods. Both operate on a variant of the FutureBlock type which holds a buffer, parameters, and callbacks.

Example:

import eris
import eris/memory_stores
import std/[asyncdispatch, streams]

type LoggerStore {.final.} = ref object of ErisStore
  other: ErisStore

proc newLogger(s: ErisStore): LoggerStore =
  LoggerStore(other: s)

method get(logger: LoggerStore; fut: FutureGet) =
  fut.addCallback:
    if fut.failed:
      stderr.writeLine("failed to get ", fut.chunkSize.int, " byte chunk ", fut.`ref`)
    else:
      stderr.writeLine("got ", fut.chunkSize.int, " byte chunk ", fut.`ref`)
  get(logger.other, fut)

method put(logger: LoggerStore; fut: FuturePut) =
  fut.addCallback:
    if fut.failed:
      stderr.writeLine("failed to put ", fut.chunkSize.int, " byte chunk ", fut.`ref`)
    else:
      stderr.writeLine("put ", fut.chunkSize.int, " byte chunk ", fut.`ref`)
  put(logger.other, fut)

let
  store = newMemoryStore()
  logger = newLogger(store)
  (cap, _) = waitFor encode(logger, newStringStream("Hail ERIS!"))
discard waitFor decode(logger, cap)

Types

BlockStatus = enum
  unknown, verified, plaintext
  Source
ChunkSize = enum
  chunk1k = 1024,           ## 1 KiB
  chunk32k = 32768           ## 32 KiB
Valid chunk sizes.   Source
DiscardStore {.final.} = ref object of ErisStoreObj
  Source
ErisCap = object
  pair*: Pair
  level*: uint8
  chunkSize*: ChunkSize
A capability for retrieving ERIS encoded data.   Source
ErisIngest = ref ErisIngestObj
  Source
ErisStore = ref ErisStoreObj
Object for interfacing ERIS storage.   Source
ErisStoreObj = object of RootObj
  nil
  Source
ErisStream = ref ErisStreamObj
An object representing data streams.   Source
FutureBlock = ref FutureBlockObj
  Source
FutureGet = ref FutureGetObj
  Source
FuturePut = ref FuturePutObj
  Source
Key = object
  bytes*: array[32, byte]
Key for decrypting a chunk.   Source
Mode = enum
  uniqueMode, convergentMode
Type for specifying if an encoding shall be unique or convergent. See section 2.3 for an explaination of encoding modes.   Source
Operation = enum
  Get, Put
  Source
Pair {.packed.} = object
  r*: Reference
  k*: Key
  Source
Reference = object
  bytes*: array[32, byte]
Reference to an encrypted chunk.   Source
Secret = object
  bytes*: array[32, byte]
Secret for salting a Key.   Source
TreeLevel = uint8
  Source

Consts

erisCborTag = 276
CBOR tag for ERIS binary read capabilities. https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml   Source

Procs

proc `$`(cap): string {....raises: [], tags: [].}
Encode a ErisCap to standard URN form. https://inqlab.net/projects/eris/#_urn   Source
proc `$`(store): string {....raises: [], tags: [].}
  Source
proc `$`(x: Reference | Key | Secret): string
Encode to Base32.   Source
func `*`[T: SomeUnsignedInt](x: T; bs: ChunkSize): T
Convenience function to multiply an integer by a ChunkSize value.   Source
proc `==`(x, y: ErisCap): bool {....raises: [], tags: [].}
  Source
func `div`[T: SomeUnsignedInt](x: T; bs: ChunkSize): T
Convenience function to divide an integer by a ChunkSize value.   Source
proc `ref`(blk: FutureBlock): Reference {.inline, ...raises: [], tags: [].}
  Source
proc addCallback(blk: FutureBlock; cb: proc () {.closure, ...gcsafe.}) {.
    ...raises: [], tags: [].}
Add a callback to a FutureBlock. Callbacks are called last-in-first-out when complete is called on blk.   Source
proc addCallback(fut: Future; blk: FutureBlock; cb: proc () {.closure, ...gcsafe.})
  Source
proc append(ingest: ErisIngest; data: string | seq[byte]): owned(
    Future[void])
Ingest content.   Source
proc append(ingest: ErisIngest; stream: Stream): owned(Future[void]) {.
    ...raises: [Exception], tags: [ReadIOEffect, RootEffect].}
Ingest content from a Stream.   Source
func arity(bs: ChunkSize): int {....raises: [], tags: [].}
  Source
proc assertVerified(blk: FutureBlock) {....raises: [], tags: [].}
  Source
proc atEnd(s: ErisStream): bool {....raises: [], tags: [].}
Check if an ErisStream is positioned at its end. May return false negatives.   Source
func buffer(blk: FutureBlock): pointer {.inline, ...raises: [], tags: [].}
  Source
proc bytes(cap): seq[byte] {....raises: [], tags: [].}
Binary encoding of the read-capability.   Source
proc cap(ingest: ErisIngest): Future[ErisCap] {....raises: [Exception, ValueError],
    tags: [RootEffect].}
Derive the ErisCap of ingest. The state of ingest is afterwards invalid until reinit or reopen is called.   Source
proc cap(s: ErisStream): ErisCap {....raises: [], tags: [].}
  Source
func chunkSize(blk: FutureBlock): ChunkSize {.inline, ...raises: [], tags: [].}
  Source
proc chunkSize(ingest: ErisIngest): ChunkSize {....raises: [], tags: [].}
  Source
proc close(s: ErisStream) {....raises: [], tags: [].}
Release the resources of an ErisStream.   Source
proc complete(blk: FutureBlock) {....raises: [], tags: [RootEffect].}
Complete a FutureBlock.   Source
proc complete(blk: FutureGet; buf: sink seq[byte]; status = unknown) {.
    ...raises: [], tags: [RootEffect].}
  Source
proc complete(blk: FutureGet; src: pointer; len: Natural; status = unknown) {.
    ...raises: [], tags: [RootEffect].}
Complete a Get FutureBlock with the chunk at src.   Source
proc complete[T: byte | char](blk: FutureGet; buf: openArray[T];
                              status = unknown) {.inline.}
  Source
proc copy(blk: FutureBlock; dst: pointer; len: Natural) {....raises: [Exception],
    tags: [].}
Copy chunk data out of a FutureBlock.   Source
proc crypto(blk: FutureBlock; key; level: TreeLevel) {....raises: [], tags: [].}
  Source
proc decode(store; cap): Future[seq[byte]] {....raises: [Exception, ValueError],
    tags: [RootEffect].}
Asynchronously decode cap from store.   Source
proc dump(s: ErisStream; stream: Stream): owned(Future[void]) {.
    ...raises: [Exception], tags: [RootEffect, WriteIOEffect].}
  Source
proc encode(store; chunkSize: ChunkSize; content: Stream; mode = uniqueMode): Future[
    (ErisCap, uint64)] {....raises: [Exception, ValueError],
                         tags: [ReadIOEffect, RootEffect].}
Asychronously encode content into store and derive its ErisCap.   Source
proc encode(store; chunkSize: ChunkSize; content: Stream; secret: Secret): Future[
    (ErisCap, uint64)] {....raises: [Exception, ValueError],
                         tags: [ReadIOEffect, RootEffect].}
Asychronously encode content into store and derive its ErisCap.   Source
proc encode(store; chunkSize: ChunkSize; content: string; mode = uniqueMode): Future[
    (ErisCap, uint64)] {....raises: [Exception, ValueError],
                         tags: [ReadIOEffect, RootEffect].}
Asychronously encode content into store and derive its ErisCap.   Source
proc encode(store; content: Stream; mode = uniqueMode): Future[
    (ErisCap, uint64)] {....raises: [Exception, ValueError],
                         tags: [ReadIOEffect, RootEffect].}
Asychronously encode content into store and derive its ErisCap. The chunk size is 1KiB unless the content is at least 16KiB.   Source
proc erisCap(content: string; chunkSize: ChunkSize): ErisCap {.
    ...raises: [Exception, ValueError, OSError],
    tags: [RootEffect, TimeEffect, ReadIOEffect].}
Derive a convergent ErisCap for content.

Example:

assert:
  $erisCap("Hello world!", chunk1k) ==
    "urn:eris:BIAD77QDJMFAKZYH2DXBUZYAP3MXZ3DJZVFYQ5DFWC6T65WSFCU5S2IT4YZGJ7AC4SYQMP2DM2ANS2ZTCP3DJJIRV733CRAAHOSWIYZM3M"
  Source
proc fail(blk: FutureBlock; e: ref Exception) {....raises: [], tags: [RootEffect].}
  Source
func failed(blk: FutureBlock): bool {.inline, ...raises: [], tags: [].}
  Source
proc fromBase32[T: Reference | Key | Secret](v: var T; s: string): bool
  Source
proc get(store; pair: Pair; level: TreeLevel; bs: ChunkSize): Future[seq[byte]] {.
    ...raises: [ValueError, Exception], tags: [RootEffect].}
  Source
proc get(store; ref: Reference; blk: FutureGet) {....raises: [], tags: [RootEffect].}
  Source
proc getAll(store: ErisStore; cap: ErisCap): Future[void] {....raises: [Exception],
    tags: [RootEffect].}
Get all chunks that constitute cap from store. No data is returned, this procedure is for ensuring that all chunks are present at some store.   Source
proc getBlock(store: ErisStore; ref: Reference): Future[seq[byte]] {....deprecated,
    raises: [Exception, ValueError], tags: [RootEffect].}
Deprecated
This requests a small chunk and with a fallback to a large chunk. Do not use it.   Source
proc getPosition(s: ErisStream): BiggestUInt {....raises: [], tags: [].}
Return the position of an ErisStream.   Source
proc hash(cap): Hash {.inline, ...raises: [], tags: [].}
  Source
proc hash(r: Reference): Hash {....raises: [], tags: [].}
Reduce a Reference to a Hash value.   Source
proc length(s: ErisStream): Future[BiggestUInt] {.
    ...raises: [Exception, ValueError], tags: [RootEffect].}
Estimate the length of an ErisStream. The result is the length of s rounded up to the next chunk boundary.   Source
proc loadBlock(s: ErisStream; bNum: BiggestUInt): Future[void] {.
    ...raises: [Exception], tags: [RootEffect].}
  Source
proc moveBytes(blk: FutureBlock): owned seq[byte] {....raises: [], tags: [].}
Move the seq[byte] out of a FutureBlock. This is only safe to use in the first callback added to blk because it will be called last.   Source
proc newDiscardStore(): DiscardStore {....raises: [], tags: [].}
Create an ErisStore that discards writes and fails to read.   Source
proc newErisIngest(store: ErisStore; chunkSize = chunk32k; mode = uniqueMode): ErisIngest {.
    ...raises: [], tags: [].}
Create a new ErisIngest object. If mode is uniqueMode then a random convergence secret will be generated using entropy from the operating system. For convergentMode a zero-secret will be used and the encoding will be deterministic and reproducible.   Source
proc newErisIngest(store: ErisStore; chunkSize = chunk32k; secret: Secret): ErisIngest {.
    ...raises: [], tags: [].}
Create a new ErisIngest object.   Source
proc newErisStream(store; cap): owned ErisStream {....raises: [], tags: [].}
Open a new stream for reading ERIS data.   Source
proc newFutureGet(bs: ChunkSize): FutureGet {....raises: [], tags: [].}
  Source
proc newFutureGet(ref: Reference; bs: ChunkSize): FutureGet {....raises: [],
    tags: [].}
  Source
proc newFuturePut(bs: ChunkSize): FuturePut {....raises: [], tags: [].}
  Source
proc newFuturePut(ref: Reference; bs: ChunkSize): FuturePut {....raises: [],
    tags: [].}
  Source
proc newFuturePut[T: byte | char](buffer: openArray[T]): FuturePut
  Source
proc notFound(blk: FutureBlock; msg = "") {.inline, ...raises: [],
    tags: [RootEffect].}
Fail f with a KeyError exception.   Source
proc padToNextBlock(ingest: ErisIngest; pad = 0x80'u8): Future[void] {.
    ...raises: [Exception], tags: [RootEffect].}
Pad the ingest stream with 0x80 until the start of the next chunk.   Source
proc parseCap[T: char | byte](bin: openArray[T]): ErisCap
  Source
proc parseErisUrn(urn: string): ErisCap {....raises: [ValueError], tags: [].}
Decode a URN to a ErisCap.   Source
proc position(ingest: ErisIngest): BiggestUInt {....raises: [], tags: [].}
Get the current append position of ingest. This is same as the number of bytes appended.   Source
proc read(s: ErisStream; size: int): Future[seq[byte]] {.
    ...raises: [Exception, ValueError], tags: [RootEffect].}
  Source
proc readAll(s: ErisStream): Future[seq[byte]] {.
    ...raises: [Exception, ValueError], tags: [RootEffect].}
Reads all data from the specified ErisStream.   Source
proc readBuffer(s: ErisStream; buffer: pointer; bufLen: int): Future[int] {.
    ...raises: [Exception, ValueError], tags: [RootEffect].}
  Source
proc readDataStr(s: ErisStream; buffer: var string; slice: Slice[int]): Future[
    int] {....raises: [Exception, ValueError], tags: [RootEffect].}
  Source
proc readLine(s: ErisStream): Future[string] {....raises: [Exception, ValueError],
    tags: [RootEffect].}
  Source
func recommendedChunkSize(dataLength: Natural): ChunkSize {....raises: [], tags: [].}
Return the recommended ChunkSize for encoding data of the given length. The current implementation selects 1KiB chunks for lengths less than 16KiB otherwise 32KiB. The reasoning is that anything less 16KiB is encoded in a tree with a depth of no more than two chunk. A 16KiB chunk would waste nearly half of a 32KiB chunk but only requires a single chunk to be fetched, whereas 16KiB-1 rould require 17 chunk requests. The behavior of this function is not guaranted to remain constant and because of storage efficiency and latency tradeoffs may not yield the best choice for all applications.   Source
proc ref=(blk: FutureBlock; r: Reference) {.inline, ...raises: [], tags: [].}
  Source
proc reference[T: byte | char](data: openArray[T]): Reference
Derive the Reference for a 1KiB or 32KiB buffer.   Source
proc references(store: ErisStore; cap: ErisCap): Future[HashSet[Reference]] {.
    ...raises: [Exception, ValueError], tags: [RootEffect].}
Collect the set of References that constitute an ErisCap.   Source
proc reinit(ingest: ErisIngest) {....raises: [], tags: [].}
Re-initialize an ErisIngest object.   Source
proc reopen(ingest: ErisIngest; cap: ErisCap): owned(Future[void]) {.
    ...raises: [Exception], tags: [RootEffect].}
Re-open an ErisIngest for appending to an ErisCap.   Source
proc reopenErisIngest(store: ErisStore; cap: ErisCap; mode = uniqueMode): Future[
    ErisIngest] {....raises: [Exception, ValueError], tags: [RootEffect].}
Re-open a ErisCap for appending.   Source
proc reopenErisIngest(store: ErisStore; cap: ErisCap; secret: Secret): Future[
    ErisIngest] {....raises: [Exception, ValueError], tags: [RootEffect].}
Re-open a ErisCap for appending.   Source
proc setPosition(s: ErisStream; pos: BiggestUInt) {....raises: [], tags: [].}
Seek an ErisStream.   Source
func toBase32(cap): string {....raises: [], tags: [].}
  Source
func toByte(bs: ChunkSize): uint8 {....raises: [], tags: [].}
  Source
proc toBytes(blk: FutureBlock): owned seq[byte] {....raises: [], tags: [].}
  Source
func toChar(bs: ChunkSize): char {....raises: [], tags: [].}
  Source
func verified(blk: FutureBlock): bool {.inline, ...raises: [], tags: [].}
  Source
proc verify(blk: FutureBlock): bool {.discardable, ...raises: [], tags: [].}
Verify that blk corresponds to ref and set the chunk error otherwise.   Source
proc verify(blk: FutureBlock; ref: Reference): bool {.discardable, ...deprecated,
    raises: [], tags: [].}
Deprecated
  Source

Methods

method close(store) {.base, ...raises: [], tags: [].}
Method for closing a Store.   Source
method get(store; blk: FutureGet) {.base, ...raises: [], tags: [RootEffect].}
Method for getting a chunk from a Store. The result is not decrypted but should be verified.   Source
method hasBlock(store; r: Reference; bs: ChunkSize): Future[bool] {.base,
    ...raises: [ValueError, Exception], tags: [RootEffect].}
Test if store has a chunk for a Reference. For some stores this is cheaper than retrieving a chunk.   Source
method id(store): string {.base, ...raises: [], tags: [].}
Get an id for store. Should be unique within a running program.   Source
method put(store; blk: FuturePut) {.base, ...raises: [], tags: [RootEffect].}
Method for putting an encrypted chunk to a Store.   Source

Iterators

iterator chunkPairs(blk: seq[byte]): Pair {....raises: [], tags: [].}
  Source

Templates

template asFuture(blk: FutureBlock): untyped
  Source
template asFuture(blk: FutureBlock; body: untyped): untyped
  Source