[WIP]
This commit is contained in:
parent
2565892f1d
commit
176bad6245
10 changed files with 284 additions and 17 deletions
34
Cargo.toml
34
Cargo.toml
|
@ -1,13 +1,37 @@
|
||||||
[package]
|
[package]
|
||||||
name = "mtcs-garage"
|
name = "mtcs-garage"
|
||||||
|
edition = { workspace = true }
|
||||||
|
version = { workspace = true }
|
||||||
|
|
||||||
|
[workspace.package]
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
|
members = ["crates/*"]
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
|
ed25519-dalek-patched = { package = "ed25519-dalek", git = "https://github.com/sp1-patches/curve25519-dalek", branch = "patch-v4.1.1" }
|
||||||
|
sha2 = "0.10.8"
|
||||||
|
sp1-helper = { git = "https://github.com/succinctlabs/sp1.git" }
|
||||||
|
sp1-sdk = { git = "https://github.com/succinctlabs/sp1.git" }
|
||||||
|
sp1-zkvm = { git = "https://github.com/succinctlabs/sp1.git" }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.5.4", features = ["derive"] }
|
||||||
|
ed25519-dalek = "2.1.1"
|
||||||
|
hex = { version = "0.4.3", features = ["serde"] }
|
||||||
|
rand = "0.8.5"
|
||||||
|
serde = "1.0.198"
|
||||||
|
serde_json = "1.0.116"
|
||||||
|
sha2 = { workspace = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
assert_cmd = "2.0.14"
|
||||||
|
predicates = "3.1.0"
|
||||||
|
rstest = "0.19.0"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "credit5000"
|
name = "credit5000"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
ed25519-dalek = "2.1.1"
|
|
||||||
rand = "0.8.5"
|
|
||||||
sha2 = "0.10.8"
|
|
||||||
|
|
9
crates/client-guest/Cargo.toml
Normal file
9
crates/client-guest/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "mtcs-client-guest"
|
||||||
|
version = { workspace = true }
|
||||||
|
edition = { workspace = true }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ed25519-dalek-patched = { workspace = true }
|
||||||
|
mtcs-garage = { path = "../../" }
|
||||||
|
sp1-zkvm = { workspace = true }
|
56
crates/client-guest/src/main.rs
Normal file
56
crates/client-guest/src/main.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
//! Client side zkvm program.
|
||||||
|
#![no_main]
|
||||||
|
sp1_zkvm::entrypoint!(main);
|
||||||
|
|
||||||
|
use ed25519_dalek_patched::{VerifyingKey, Signature, SecretKey}
|
||||||
|
|
||||||
|
type InputLengh = u64;
|
||||||
|
|
||||||
|
type Secret = [u8: 32];
|
||||||
|
|
||||||
|
struct InputHead {
|
||||||
|
salt: [u8: 32],
|
||||||
|
from: [u8: 32],
|
||||||
|
val: u32,
|
||||||
|
to: [u8: 32],
|
||||||
|
hash: [u8: 32],
|
||||||
|
signature: [u8: 64],
|
||||||
|
public_key: [u8: 32],
|
||||||
|
root: [u8: 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Input Head length in bytes.
|
||||||
|
// Could be turned into Phantom attribute or somethin?
|
||||||
|
const HL = 260;
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let length = sp1_zkvm::io::read::<InputLengh>();
|
||||||
|
let input = sp1_zkvm::io::read::<InputHead>();
|
||||||
|
let secret = sp1_zkvm::io::read::<Secret>();
|
||||||
|
let mut my_slice = [0_u8; 32];
|
||||||
|
sp1_zkvm::io::read_slice(&mut my_slice);
|
||||||
|
let hashes = sp1_zkvm::io::read::<Vec<u8>>();
|
||||||
|
|
||||||
|
let pk = VerifyingKey::from_bytes(input.public_key).unwrap();
|
||||||
|
assert!(pk.verify_strict(
|
||||||
|
&input.from
|
||||||
|
.iter()
|
||||||
|
.chain(input.val.to_le_bytes().iter())
|
||||||
|
.chain(input.to.iter())
|
||||||
|
.cloned()
|
||||||
|
.collect(),
|
||||||
|
Signature::from_bytes(&input.signature),
|
||||||
|
).unwrap());
|
||||||
|
|
||||||
|
let mut h = Sha256::new();
|
||||||
|
let chunks = hashes.chunks(32);
|
||||||
|
chunks.fold(chunks.next().unwrap(), |acc, e| {
|
||||||
|
h.update();
|
||||||
|
assert!(*e == *h.finalize_reset());
|
||||||
|
});
|
||||||
|
assert!(input.root == *self.hashes.last().unwrap());
|
||||||
|
|
||||||
|
sp1_zkvm::io::commit_slice(&input.hash);
|
||||||
|
sp1_zkvm::io::commit_slice(&input.root);
|
||||||
|
}
|
10
crates/client-host/Cargo.toml
Normal file
10
crates/client-host/Cargo.toml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
version = { workspace = true }
|
||||||
|
name = "client-host"
|
||||||
|
edition = { workspace = true }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
sp1-sdk = { workspace = true }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
sp1-helper = { workspace = true }
|
5
crates/client-host/build.rs
Normal file
5
crates/client-host/build.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
use sp1_helper::build_program;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
build_program("../client-guest")
|
||||||
|
}
|
3
crates/client-host/rust-toolchain.toml
Normal file
3
crates/client-host/rust-toolchain.toml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "nightly-2024-04-20"
|
||||||
|
components = ["llvm-tools", "rustc-dev"]
|
7
crates/client-host/src/main.rs
Normal file
7
crates/client-host/src/main.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
//! A simple script to generate and verify the proof of a given program.
|
||||||
|
|
||||||
|
use sp1_sdk::{ProverClient, SP1Stdin};
|
||||||
|
|
||||||
|
const ELF: &[u8] = include_bytes!("../../../elf/riscv32im-succinct-zkvm-elf");
|
||||||
|
|
||||||
|
fn main() { }
|
32
src/lib.rs
32
src/lib.rs
|
@ -1,9 +1,11 @@
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
|
use dalek::{ed25519::signature::SignerMut, SigningKey};
|
||||||
use ed25519_dalek as dalek;
|
use ed25519_dalek as dalek;
|
||||||
use ed25519_dalek::Verifier;
|
use ed25519_dalek::Verifier;
|
||||||
use rand;
|
use rand;
|
||||||
use sha2::{digest::Digest, Sha256};
|
use sha2::{digest::Digest, Sha256};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// TODO: Use hashes instead of public addresses.
|
/// TODO: Use hashes instead of public addresses.
|
||||||
/// TODO: Don't forget document endiannes.
|
/// TODO: Don't forget document endiannes.
|
||||||
|
@ -16,6 +18,7 @@ use sha2::{digest::Digest, Sha256};
|
||||||
/// much.
|
/// much.
|
||||||
|
|
||||||
pub type Address = [u8; 32];
|
pub type Address = [u8; 32];
|
||||||
|
pub type Secret = [u8; 32];
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
pub struct Obligation {
|
pub struct Obligation {
|
||||||
|
@ -25,7 +28,7 @@ pub struct Obligation {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Obligation {
|
impl Obligation {
|
||||||
fn serialize(self) -> [u8; 68] {
|
pub fn serialize(self) -> [u8; 68] {
|
||||||
let mut arr = [0; 68];
|
let mut arr = [0; 68];
|
||||||
arr[..32].clone_from_slice(&self.from);
|
arr[..32].clone_from_slice(&self.from);
|
||||||
arr[32..36].clone_from_slice(&self.value.to_le_bytes());
|
arr[32..36].clone_from_slice(&self.value.to_le_bytes());
|
||||||
|
@ -44,7 +47,7 @@ pub struct AuthCert {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuthCert {
|
impl AuthCert {
|
||||||
pub fn new(
|
pub fn maybe_from_signature(
|
||||||
subject: &Obligation,
|
subject: &Obligation,
|
||||||
signature: &Signature,
|
signature: &Signature,
|
||||||
pk: &Address,
|
pk: &Address,
|
||||||
|
@ -59,6 +62,12 @@ impl AuthCert {
|
||||||
signature: *signature,
|
signature: *signature,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
pub fn from_secret(subject: &Obligation, secret: &Secret) -> Self {
|
||||||
|
Self {
|
||||||
|
subject: subject.clone(),
|
||||||
|
signature: SigningKey::from_bytes(secret).sign(&subject.serialize()).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type PseudoAnonID = [u8; 32];
|
pub type PseudoAnonID = [u8; 32];
|
||||||
|
@ -138,16 +147,17 @@ impl Cert {
|
||||||
hash: h.finalize().into(),
|
hash: h.finalize().into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn zkvm_serialize(&self) -> Vec<u8> {
|
pub fn zkvm_serialize(&self) -> Vec<u8> { // , pk: Address
|
||||||
// First 8 bytes are the length of the message,
|
// First 8 bytes are the length of the message,
|
||||||
// followed by 228 bytes of fields of defined length,
|
// followed by 260 bytes of fields of defined length,
|
||||||
// the rest is membership proof.
|
// the rest is membership proof.
|
||||||
(228 + 32 * self.membership.hashes.len())
|
(260 + 32 * self.membership.hashes.len())
|
||||||
.to_le_bytes() // 8 bytes; NOTE: Machine specific! (from usize)
|
.to_le_bytes() // 8 bytes; NOTE: Machine specific! (from usize)
|
||||||
.iter()
|
.iter()
|
||||||
.chain(self.salt.iter()) // 32 bytes
|
.chain(self.salt.iter()) // 32 bytes
|
||||||
.chain(self.subject.serialize().iter()) // 68 bytes
|
.chain(self.subject.serialize().iter()) // 68 bytes
|
||||||
.chain(self.hash.iter()) // 32 bytes
|
.chain(self.hash.iter()) // 32 bytes
|
||||||
|
.chain(self.salt.iter()) // 32 bytes
|
||||||
.chain(self.authenticity.signature.iter()) // 64 bytes
|
.chain(self.authenticity.signature.iter()) // 64 bytes
|
||||||
.chain(self.membership.root.iter()) // 32 bytes
|
.chain(self.membership.root.iter()) // 32 bytes
|
||||||
.chain(self.membership.hashes.iter().flatten()) // tail
|
.chain(self.membership.hashes.iter().flatten()) // tail
|
||||||
|
@ -224,15 +234,15 @@ mod tests {
|
||||||
fn authenticity_cert_new() {
|
fn authenticity_cert_new() {
|
||||||
let tob = rand_obligation();
|
let tob = rand_obligation();
|
||||||
let sig = tob.f.sk.sign(&tob.o.serialize()[..]).to_bytes();
|
let sig = tob.f.sk.sign(&tob.o.serialize()[..]).to_bytes();
|
||||||
let cert = AuthCert::new(&tob.o, &sig, &tob.f.sk.verifying_key().to_bytes());
|
let cert = AuthCert::maybe_from_signature(&tob.o, &sig, &tob.f.sk.verifying_key().to_bytes());
|
||||||
assert!(cert.is_ok());
|
assert!(cert.is_ok());
|
||||||
assert_eq!(cert.as_ref().unwrap().subject, tob.o);
|
assert_eq!(cert.as_ref().unwrap().subject, tob.o);
|
||||||
assert_eq!(cert.as_ref().unwrap().signature, sig);
|
assert_eq!(cert.as_ref().unwrap().signature, sig);
|
||||||
// Why this sometime fails??
|
// Why this sometime fails??
|
||||||
// assert!(AuthCert::new(&tob.o, &[0; 64], &[0; 32]).is_err());
|
// assert!(AuthCert::new(&tob.o, &[0; 64], &[0; 32]).is_err());
|
||||||
assert!(AuthCert::new(&tob.o, &[0; 64], &[1; 32]).is_err());
|
assert!(AuthCert::maybe_from_signature(&tob.o, &[0; 64], &[1; 32]).is_err());
|
||||||
assert!(AuthCert::new(&tob.o, &sig, &tob.o.from).is_err());
|
assert!(AuthCert::maybe_from_signature(&tob.o, &sig, &tob.o.from).is_err());
|
||||||
assert!(AuthCert::new(&tob.o, &sig, &tob.o.to).is_err());
|
assert!(AuthCert::maybe_from_signature(&tob.o, &sig, &tob.o.to).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -268,7 +278,7 @@ mod tests {
|
||||||
salt: rand::random(),
|
salt: rand::random(),
|
||||||
hash: rand::random(),
|
hash: rand::random(),
|
||||||
};
|
};
|
||||||
assert!(c.zkvm_serialize().len() > 228);
|
assert!(c.zkvm_serialize().len() > 260);
|
||||||
assert!(c.zkvm_serialize().len() < 228 + (nodes * 32) * 2);
|
assert!(c.zkvm_serialize().len() < 260 + (nodes * 32) * 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
111
src/main.rs
111
src/main.rs
|
@ -1 +1,110 @@
|
||||||
fn main() {}
|
use clap::Parser;
|
||||||
|
// use sp1_sdk::{utils, ProverClient, SP1Stdin};
|
||||||
|
use hex;
|
||||||
|
use mtcs_garage::{AuthCert, Cert, MembershipProof, Obligation};
|
||||||
|
|
||||||
|
// const ELF: &[u8] = include_bytes!("../elf/riscv32im-succinct-zkvm-elf");
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
struct Args {
|
||||||
|
#[arg(short, long)]
|
||||||
|
secret: String,
|
||||||
|
|
||||||
|
#[arg(short, long)]
|
||||||
|
from: String,
|
||||||
|
|
||||||
|
#[arg(short, long)]
|
||||||
|
to: String,
|
||||||
|
|
||||||
|
#[arg(short, long)]
|
||||||
|
value: u32,
|
||||||
|
|
||||||
|
// #[arg(short, long)]
|
||||||
|
// cache: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = Args::parse();
|
||||||
|
let (from, to) = (
|
||||||
|
hex::decode(args.from).unwrap().try_into().unwrap(),
|
||||||
|
hex::decode(args.to).unwrap().try_into().unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let obligation = Obligation { from, to, value: args.value };
|
||||||
|
let cert = Cert {
|
||||||
|
subject: obligation,
|
||||||
|
authenticity: AuthCert::from_secret(
|
||||||
|
&obligation,
|
||||||
|
&hex::decode(args.secret).unwrap().try_into().unwrap(),
|
||||||
|
),
|
||||||
|
hash: [0; 32],
|
||||||
|
salt: [0; 32],
|
||||||
|
membership: MembershipProof::new(vec![from, to])
|
||||||
|
};
|
||||||
|
|
||||||
|
// utils::setup_tracer();
|
||||||
|
|
||||||
|
// let mut stdin = SP1Stdin::new();
|
||||||
|
// stdin.write(&cert.zkvm_serialize());
|
||||||
|
// let client = ProverClient::new();
|
||||||
|
// let (pk, vk) = client.prover.setup(ELF);
|
||||||
|
// let proof = client.prove(&pk, stdin);
|
||||||
|
// match proof {
|
||||||
|
// Ok(p) => println!("coool"),
|
||||||
|
// Err(_) => println!("not cool"),
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn main() {
|
||||||
|
|
||||||
|
// let nodes = rand::thread_rng().gen_range(0..100);
|
||||||
|
// let o = rand_obligation();
|
||||||
|
// let mp = MembershipProof::new(
|
||||||
|
// std::iter::from_fn(|| Some(make_address()))
|
||||||
|
// .take(nodes)
|
||||||
|
// .collect(),
|
||||||
|
// );
|
||||||
|
// let c = Cert {
|
||||||
|
// subject: o.o,
|
||||||
|
// authenticity: AuthCert {
|
||||||
|
// subject: o.o,
|
||||||
|
// signature: rand::random(),
|
||||||
|
// },
|
||||||
|
// membership: mp,
|
||||||
|
// salt: rand::random(),
|
||||||
|
// hash: rand::random(),
|
||||||
|
// };
|
||||||
|
|
||||||
|
|
||||||
|
// utils::setup_tracer();
|
||||||
|
// let mut stdin = SP1Stdin::new();
|
||||||
|
|
||||||
|
|
||||||
|
// stdin.write(&data_str);
|
||||||
|
// stdin.write(&key);
|
||||||
|
// stdin.write(&initial_account_state);
|
||||||
|
// stdin.write(&transactions);
|
||||||
|
|
||||||
|
// let mut proof = SP1Prover::prove(JSON_ELF, stdin).expect("proving failed");
|
||||||
|
|
||||||
|
// // Read output.
|
||||||
|
// let val = proof.stdout.read::<String>();
|
||||||
|
// println!("Value of {} is {}", key, val);
|
||||||
|
|
||||||
|
// let account_state = proof.stdout.read::<Account>();
|
||||||
|
// println!(
|
||||||
|
// "Final account state: {}",
|
||||||
|
// serde_json::to_string(&account_state).unwrap()
|
||||||
|
// );
|
||||||
|
|
||||||
|
// // Verify proof.
|
||||||
|
// SP1Verifier::verify(JSON_ELF, &proof).expect("verification failed");
|
||||||
|
|
||||||
|
// // Save proof.
|
||||||
|
// proof
|
||||||
|
// .save("proof-with-io.json")
|
||||||
|
// .expect("saving proof failed");
|
||||||
|
|
||||||
|
// println!("successfully generated and verified proof for the program!")
|
||||||
|
// }
|
||||||
|
|
34
tests/test_cli.rs
Normal file
34
tests/test_cli.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
use assert_cmd::prelude::*;
|
||||||
|
use predicates::prelude::*;
|
||||||
|
use rand;
|
||||||
|
|
||||||
|
use std::process::Command;
|
||||||
|
use rstest::*;
|
||||||
|
|
||||||
|
use mtcs_garage::*;
|
||||||
|
use hex;
|
||||||
|
|
||||||
|
|
||||||
|
#[fixture]
|
||||||
|
fn cmd() -> Command {
|
||||||
|
Command::cargo_bin("credit5000").unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn cli_showes_help(mut cmd: Command) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
cmd.arg("--help");
|
||||||
|
cmd.assert().stdout(predicate::str::contains("Usage: credit5000"));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn cli_stuff(mut cmd: Command) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let o = cmd.args([
|
||||||
|
"--secret", &hex::encode(rand::random::<Secret>()),
|
||||||
|
"--from", &hex::encode(rand::random::<Address>()),
|
||||||
|
"--to", &hex::encode(rand::random::<Address>()),
|
||||||
|
"--value", "1000",
|
||||||
|
]);
|
||||||
|
cmd.assert().stdout(predicate::str::contains("coool"));
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in a new issue