hypercore 0.8.1

Secure, distributed, append-only log
Documentation
//! Based on https://github.com/mafintosh/hypercore/blob/cf08d8c907e302cf4b699738f229b050eba41b59/test/compat.js

extern crate data_encoding;
extern crate ed25519_dalek;
extern crate hypercore;
extern crate random_access_disk;
extern crate random_access_storage;
extern crate remove_dir_all;
extern crate tempfile;

use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

use data_encoding::HEXLOWER;
use ed25519_dalek::Keypair;
use hypercore::Feed;
use hypercore::{Storage, Store};
use random_access_disk::RandomAccessDisk;
use remove_dir_all::remove_dir_all;

#[test]
fn deterministic_data_and_tree() {
  let expected_tree = hex_bytes(concat!(
    "0502570200002807424c414b4532620000000000000000000000000000000000ab27d45f509274",
    "ce0d08f4f09ba2d0e0d8df61a0c2a78932e81b5ef26ef398df0000000000000001064321a8413b",
    "e8c604599689e2c7a59367b031b598bceeeb16556a8f3252e0de000000000000000294c1705400",
    "5942a002c7c39fbb9c6183518691fb401436f1a2f329b380230af800000000000000018dfe81d5",
    "76464773f848b9aba1c886fde57a49c283ab57f4a297d976d986651e00000000000000041d2fad",
    "c9ce604c7e592949edc964e45aaa10990d7ee53328439ef9b2cf8aa6ff00000000000000013a8d",
    "cc74e80b8314e8e13e1e462358cf58cf5fc4413a9b18a891ffacc551c395000000000000000228",
    "28647a654a712738e35f49d1c05c676010be0b33882affc1d1e7e9fee59d400000000000000001",
    "000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "00baac70b6d38243efa028ee977c462e4bec73d21d09ceb8cc16f4d4b1ee228a45000000000000",
    "0001d1b021632c7fab84544053379112ca7b165bb21283821816c5b6c89ff7f78e2d0000000000",
    "000002d2ab421cece792033058787a5ba72f3a701fddc25540d5924e9819d7c12e02f200000000",
    "00000001"
  ));

  for _ in 0..5 {
    let (dir, storage) = mk_storage();
    let mut feed = Feed::with_storage(storage).unwrap();

    let data = b"abcdef";
    for &b in data {
      feed.append(&[b]).unwrap();
    }
    assert_eq!(read_bytes(&dir, Store::Data), data);
    assert_eq!(read_bytes(&dir, Store::Tree), expected_tree);

    remove_dir_all(dir).unwrap()
  }
}

#[test]
#[ignore]
fn deterministic_data_and_tree_after_replication() {
  // Port from mafintosh/hypercore when the necessary features are implemented
  unimplemented!();
}

#[test]
fn deterministic_signatures() {
  let key = hex_bytes(
    "9718a1ff1c4ca79feac551c0c7212a65e4091278ec886b88be01ee4039682238",
  );
  let keypair_bytes = hex_bytes(concat!(
    "53729c0311846cca9cc0eded07aaf9e6689705b6a0b1bb8c3a2a839b72fda383",
    "9718a1ff1c4ca79feac551c0c7212a65e4091278ec886b88be01ee4039682238"
  ));

  let expected_signatures = hex_bytes(concat!(
    "050257010000400745643235353139000000000000000000000000000000000084684e8dd76c339",
    "d6f5754e813204906ee818e6c6cdc6a816a2ac785a3e0d926ac08641a904013194fe6121847b7da",
    "d4e361965d47715428eb0a0ededbdd5909d037ff3c3614fa0100ed9264a712d3b77cbe7a4f6eadd",
    "8f342809be99dfb9154a19e278d7a5de7d2b4d890f7701a38b006469f6bab1aff66ac6125d48baf",
    "dc0711057675ed57d445ce7ed4613881be37ebc56bb40556b822e431bb4dc3517421f9a5e3ed124",
    "eb5c4db8367386d9ce12b2408613b9fec2837022772a635ffd807",
  ));

  for _ in 0..5 {
    let (dir, storage) = mk_storage();
    let keypair = mk_keypair(&keypair_bytes, &key);
    let mut feed = Feed::builder(keypair.public, storage)
      .secret_key(keypair.secret)
      .build()
      .unwrap();

    let data = b"abc";
    for &b in data {
      feed.append(&[b]).unwrap();
    }

    assert_eq!(read_bytes(&dir, Store::Data), data);
    assert_eq!(read_bytes(&dir, Store::Signatures), expected_signatures);

    remove_dir_all(dir).unwrap()
  }
}

#[test]
#[ignore]
fn deterministic_signatures_after_replication() {
  // Port from mafintosh/hypercore when the necessary features are implemented
  unimplemented!();
}

fn hex_bytes(hex: &str) -> Vec<u8> {
  HEXLOWER.decode(hex.as_bytes()).unwrap()
}

fn storage_path<P: AsRef<Path>>(dir: P, s: Store) -> PathBuf {
  let filename = match s {
    Store::Tree => "tree",
    Store::Data => "data",
    Store::Bitfield => "bitfield",
    Store::Signatures => "signatures",
    Store::Keypair => "key",
  };
  dir.as_ref().join(filename)
}

fn mk_storage() -> (PathBuf, Storage<RandomAccessDisk>) {
  let temp_dir = tempfile::tempdir().unwrap();
  let dir = temp_dir.into_path();
  let storage =
    Storage::new(|s| RandomAccessDisk::open(storage_path(dir.clone(), s)))
      .unwrap();
  (dir, storage)
}

fn read_bytes<P: AsRef<Path>>(dir: P, s: Store) -> Vec<u8> {
  let mut f = File::open(storage_path(dir, s)).unwrap();
  let mut bytes = Vec::new();
  f.read_to_end(&mut bytes).unwrap();
  bytes
}

fn mk_keypair(keypair_bytes: &[u8], public_key: &[u8]) -> Keypair {
  let keypair = Keypair::from_bytes(&keypair_bytes).unwrap();
  assert_eq!(
    keypair.secret.as_bytes().as_ref(),
    &keypair_bytes[..ed25519_dalek::SECRET_KEY_LENGTH]
  );
  assert_eq!(keypair.public.as_bytes().as_ref(), public_key);
  keypair
}