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.blockSize.int, " byte block ", fut.`ref`)
    else:
      stderr.writeLine("got ", fut.blockSize.int, " byte block ", fut.`ref`)
  get(logger.other, fut)

method put(logger: LoggerStore; fut: FuturePut) =
  fut.addCallback:
    if fut.failed:
      stderr.writeLine("failed to put ", fut.blockSize.int, " byte block ", fut.`ref`)
    else:
      stderr.writeLine("put ", fut.blockSize.int, " byte block ", 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

BlockSize = enum
  bs1k = 1024,              ## 1 KiB
  bs32k = 32768              ## 32 KiB
Valid block sizes.   Source
BlockStatus = enum
  unknown, verified, plaintext
  Source
DiscardStore {.final.} = ref object of ErisStoreObj
  Source
ErisCap = object
  pair*: Pair
  level*: uint8
  blockSize*: BlockSize
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 block.   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
Reference = object
  bytes*: array[32, byte]
Reference to an encrypted block.   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: BlockSize): T
Convenience function to multiply an integer by a BlockSize value.   Source
proc `==`(x, y: ErisCap): bool {....raises: [], tags: [].}
  Source
func `div`[T: SomeUnsignedInt](x: T; bs: BlockSize): T
Convenience function to divide an integer by a BlockSize 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: BlockSize): 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 blockSize(blk: FutureBlock): BlockSize {.inline, ...raises: [], tags: [].}
  Source
proc blockSize(ingest: ErisIngest): BlockSize {....raises: [], tags: [].}
  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
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 block 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 block data out of a FutureBlock.   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; blockSize: BlockSize; content: Stream; mode = uniqueMode): Future[
    ErisCap] {....raises: [Exception, ValueError], tags: [ReadIOEffect, RootEffect].}
Asychronously encode content into store and derive its ErisCap.   Source
proc encode(store; blockSize: BlockSize; content: Stream; secret: Secret): Future[
    ErisCap] {....raises: [Exception, ValueError], tags: [ReadIOEffect, RootEffect].}
Asychronously encode content into store and derive its ErisCap.   Source
proc encode(store; blockSize: BlockSize; content: string; mode = uniqueMode): Future[
    ErisCap] {....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] {.
    ...raises: [Exception, ValueError], tags: [ReadIOEffect, RootEffect].}
Asychronously encode content into store and derive its ErisCap. The block size is 1KiB unless the content is at least 16KiB.   Source
proc erisCap(content: string; blockSize: BlockSize): ErisCap {.
    ...raises: [Exception, ValueError, OSError],
    tags: [RootEffect, TimeEffect, ReadIOEffect].}
Derive a convergent ErisCap for content.

Example:

assert:
  $erisCap("Hello world!", bs1k) ==
    "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: BlockSize): 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 blocks that constitute cap from store. No data is returned, this procedure is for ensuring that all blocks 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 block and with a fallback to a large block. 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 block 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; blockSize = bs32k; 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; blockSize = bs32k; 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: BlockSize): FutureGet {....raises: [], tags: [].}
  Source
proc newFutureGet(ref: Reference; bs: BlockSize): FutureGet {....raises: [],
    tags: [].}
  Source
proc newFuturePut(bs: BlockSize): FuturePut {....raises: [], tags: [].}
  Source
proc newFuturePut(ref: Reference; bs: BlockSize): 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 block.   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 recommendedBlockSize(dataLength: Natural): BlockSize {....raises: [], tags: [].}
Return the recommended BlockSize for encoding data of the given length. The current implementation selects 1KiB blocks 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 block. A 16KiB block would waste nearly half of a 32KiB block but only requires a single block to be fetched, whereas 16KiB-1 rould require 17 block 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: BlockSize): uint8 {....raises: [], tags: [].}
  Source
proc toBytes(blk: FutureBlock): owned seq[byte] {....raises: [], tags: [].}
  Source
func toChar(bs: BlockSize): 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 block 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 block from a Store. The result is not decrypted but should be verified.   Source
method hasBlock(store; r: Reference; bs: BlockSize): Future[bool] {.base,
    ...raises: [ValueError, Exception], tags: [RootEffect].}
Test if store has a block for a Reference. For some stores this is cheaper than retrieving a block.   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 block to a Store.   Source

Templates

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