Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
buffrr committed Feb 21, 2024
0 parents commit 9ddbf3b
Show file tree
Hide file tree
Showing 13 changed files with 2,609 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/target
/Cargo.lock
*.dot
*.png
.idea
*.sdb
23 changes: 23 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
workspace = { members = ["example"] }

[package]
name = "spacedb"
version = "0.1.0"
edition = "2021"
description = "A cryptographically verifiable data store and universal accumulator for the Spaces protocol."
repository = "https://github.com/spacesprotocol/spacedb"
license = "Apache-2.0"

[dependencies]
libc = { version = "0.2.150", optional = true }
bincode = { version = "2.0.0-rc.3", default-features = false, features = ["alloc"] }
hex = { version = "0.4.3", optional = true }

[dependencies.sha2]
git = "https://github.com/risc0/RustCrypto-hashes"
tag = "sha2-v0.10.6-risczero.0"
default-features = false

[features]
default = ["std"]
std = ["libc", "hex", "bincode/derive", "bincode/std"]
138 changes: 138 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# SpaceDB

<small>Note: this project is still under active development and should be considered experimental.</small>

SpaceDB is a cryptographically verifiable data store and universal accumulator for the [Spaces protocol](https://spacesprotocol.com). It's a Merkle-ized binary trie described in the [Merklix](https://blog.vermorel.com/pdf/merklix-tree-for-bitcoin-2018-07.pdf) paper and explained in detail [here](https://spacesprotocol.org/#binary-trie).


## Features

- Fast, portable, single-file database.
- MVCC-based concurrency control with multi-reader/single-writer lock-free access.
- Provides compact proofs of membership/non-membership for batches of elements through subtrees.
- Subtrees act as cryptographic accumulators and can be updated independently.
- `no_std` support, particularly for use within RISC0 zkVM and leverages SHA256 acceleration.
- Accumulator keeps a constant size state of a single 32-byte tree root.



## Usage

```rust
use spacedb::db::Database;


let db = Database::open("example.sdb")?;

// Insert some data
let mut tx = db.begin_write()?;
for i in 0..100 {
let key = format!("key{}", i);
let value = format!("value{}", i);
tx.insert(db.hash(key.as_bytes()), value.into_bytes())?;
}
tx.commit()?;

let mut snapshot = db.begin_read()?;
println!("Tree root: {}", hex::encode(snapshot.root()?));

// Prove a subset of the keys
let keys_to_prove: Vec<_> = (0..10)
.map(|i| format!("key{}", i))
// prove exclusion of some other keys
.chain((0..5).map(|i| format!("other{}", i)))
.map(|key| db.hash(key.as_bytes()))
.collect();

// Reveal relevant nodes needed to prove the specified set of keys
let mut subtree = snapshot.prove_all(&keys_to_prove)?;

// Will have the exact same root as the snapshot
println!("Subtree root: {}", hex::encode(subtree.root().unwrap()));

// Inclusion and exclusion proofs
assert!(subtree.contains(&db.hash("key0".as_bytes())).unwrap());
assert!(!subtree.contains(&db.hash("other0".as_bytes())).unwrap());

// Proving exclusion of "other100" fails since we didn't reveal
// relevant branches needed to traverse its path in this subtree
assert!(subtree.contains(&db.hash("other100".as_bytes())).is_err());

```



## Subtrees

Subtrees can function as cryptographic accumulators, allowing clients to verify and update their state without keeping a database.

```rust

// Client maintains a 32-byte tree root
let mut accumulator_root = snapshot.root()?;
assert_eq!(accumulator_root, subtree.root().unwrap(), "Roots must match");

// Update leaves
for (key, value) in subtree.iter_mut() {
*value = "new value".to_string().into_bytes();
}

// Inserting a non-existent key (must be provably absent)
let key = subtree.hash("other0".as_bytes());
subtree.insert(key, "new value".into_bytes()).unwrap();

// Updating the accumulator root
accumulator_root = subtree.root().unwrap();

```

## Using in RISC0 zkVM

Subtrees work in `no_std` environments utilizing the SHA256 accelerator when running inside the RISC0 zkVM.

```toml
[dependencies]
spacedb = { version = "0.1", default-features = false }
```




## Key Iteration

Iterate over all keys in a given snapshot:

```rust
let db = Database::open("my.sdb")?;
let snapshot = db.begin_read()?;

for (key, value) in snapshot.iter().filter_map(Result::ok) {
// do something ...
}

```



## Snapshot iteration

Iterate over all snapshots:

```rust
let db = Database::open("my.sdb")?;

for snapshot in db.iter().filter_map(Result::ok) {
let root = snapshot.root()?;
println!("Snapshot Root: {}", hex::encode(root));
}
```

## Prior Art

Merkle-ized tries, including variations like Patricia tries and Merkle prefix trees, are foundational structures that have been used in numerous projects and cryptocurrencies. Some other libraries that implement some form of Merkle-ized binary tries include
[liburkel](https://github.com/chjj/liburkel) which this library initially drew some inspiration from — although SpaceDB is generally around ~20% faster, and [multiproof,](https://github.com/gballet/multiproof-rs/tree/master) but they either lack memory safety, core features such as subtrees/accumulators needed for Spaces protocol or are unmaintained. Other popular cryptographically verifiable data stores include [Trillian](https://github.com/google/trillian) used for [Certificate Transparency](https://www.certificate-transparency.org/)


## License

This project is licensed under the [Apache 2.0](LICENSE).
11 changes: 11 additions & 0 deletions example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "example"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
spacedb = {version = "*", path = ".." }
hex = "0.4.3"

43 changes: 43 additions & 0 deletions example/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use spacedb::db::Database;

fn main() -> Result<(), std::io::Error> {
let db = Database::memory()?;

// Insert some data
let mut tx = db.begin_write()?;
for i in 0..100 {
let key = format!("key{}", i);
let value = format!("value{}", i);
tx.insert(db.hash(key.as_bytes()), value.into_bytes())?;
}
tx.commit()?;

// Get the committed snapshot
let mut snapshot = db.begin_read()?;
println!("Tree root: {}", hex::encode(snapshot.root()?));

// Prove a subset of the keys
let keys_to_prove: Vec<_> = (0..10)
.map(|i| format!("key{}", i))
// prove exclusion of some other keys
.chain((0..5).map(|i| format!("other{}", i)))
.map(|key| db.hash(key.as_bytes()))
.collect();

// reveal the relevant nodes needed to prove the specified set of keys
let subtree = snapshot.prove_all(&keys_to_prove)?;

// Will have the exact same root as the snapshot
println!("Subtree root: {}", hex::encode(subtree.root().unwrap()));

// Prove inclusion
assert!(subtree.contains(&db.hash("key0".as_bytes())).unwrap());

// Prove exclusion
assert!(!subtree.contains(&db.hash("other0".as_bytes())).unwrap());

// We don't have enough data to prove key "other100" is not in the subtree
// as the relevant branches needed to prove it are not included
assert!(subtree.contains(&db.hash("other100".as_bytes())).is_err());
Ok(())
}
Loading

0 comments on commit 9ddbf3b

Please sign in to comment.