diff --git a/cl/Cargo.toml b/cl/Cargo.toml index 279c094..3722186 100644 --- a/cl/Cargo.toml +++ b/cl/Cargo.toml @@ -10,9 +10,17 @@ serde = {version="1.0", features = ["derive"]} bincode = "1.3.3" risc0-groth16 = "1.0.1" blake2 = "0.10.6" -jubjub = "0.10.0" +# jubjub = "0.10.0" group = "0.13.0" rand_core = "0.6.0" rand_chacha = "0.3.1" lazy_static = "1.4.0" hex = "0.4.3" +k256 = {version = "0.13.3", features = ["serde", "hash2curve"]} +# [dependencies.k256] +# git = "https://github.com/risc0/RustCrypto-elliptic-curves" +# tag = "k256/v0.13.3-risczero.0" + + + + diff --git a/cl/src/balance.rs b/cl/src/balance.rs index fe19c43..54791eb 100644 --- a/cl/src/balance.rs +++ b/cl/src/balance.rs @@ -1,28 +1,32 @@ -use group::{ff::Field, GroupEncoding}; -use jubjub::{Scalar, SubgroupPoint}; use lazy_static::lazy_static; use rand_core::RngCore; use serde::{Deserialize, Serialize}; +use k256::{ + elliptic_curve::{ + group::GroupEncoding, Field + }, + ProjectivePoint, Scalar, AffinePoint +}; + lazy_static! { - static ref PEDERSON_COMMITMENT_BLINDING_POINT: SubgroupPoint = + static ref PEDERSON_COMMITMENT_BLINDING_POINT: ProjectivePoint = crate::crypto::hash_to_curve(b"NOMOS_CL_PEDERSON_COMMITMENT_BLINDING"); } #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] -pub struct Balance(#[serde(with = "serde_point")] pub SubgroupPoint); +pub struct Balance(pub AffinePoint); #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct BalanceWitness { pub value: u64, pub unit: String, - #[serde(with = "serde_scalar")] pub blinding: Scalar, } impl Balance { - pub fn to_bytes(&self) -> [u8; 32] { - self.0.to_bytes() + pub fn to_bytes(&self) -> [u8; 33] { + self.0.to_bytes().into() } } @@ -40,117 +44,119 @@ impl BalanceWitness { } pub fn commit(&self) -> Balance { - Balance(balance(self.value, &self.unit, self.blinding)) + Balance(balance(self.value, &self.unit, self.blinding).into()) } - pub fn unit_point(&self) -> SubgroupPoint { + pub fn unit_point(&self) -> ProjectivePoint { unit_point(&self.unit) } } -pub fn unit_point(unit: &str) -> SubgroupPoint { +pub fn unit_point(unit: &str) -> ProjectivePoint { crate::crypto::hash_to_curve(unit.as_bytes()) } -pub fn balance(value: u64, unit: &str, blinding: Scalar) -> SubgroupPoint { +pub fn balance(value: u64, unit: &str, blinding: Scalar) -> ProjectivePoint { let value_scalar = Scalar::from(value); unit_point(unit) * value_scalar + *PEDERSON_COMMITMENT_BLINDING_POINT * blinding } -mod serde_scalar { - use super::Scalar; - use serde::de::{self, Visitor}; - use serde::{Deserializer, Serializer}; - use std::fmt; +// mod serde_scalar { +// use super::Scalar; +// use serde::de::{self, Visitor}; +// use serde::{Deserializer, Serializer}; +// use std::fmt; - // Serialize a SubgroupPoint by converting it to bytes. - pub fn serialize(scalar: &Scalar, serializer: S) -> Result - where - S: Serializer, - { - let bytes = scalar.to_bytes(); - serializer.serialize_bytes(&bytes) - } +// // Serialize a SubgroupPoint by converting it to bytes. +// pub fn serialize(scalar: &Scalar, serializer: S) -> Result +// where +// S: Serializer, +// { +// let bytes = scalar.to_bytes(); +// serializer.serialize_bytes(&bytes) +// } - // Deserialize a SubgroupPoint by converting it from bytes. - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct BytesVisitor; +// // Deserialize a SubgroupPoint by converting it from bytes. +// pub fn deserialize<'de, D>(deserializer: D) -> Result +// where +// D: Deserializer<'de>, +// { +// struct BytesVisitor; - impl<'de> Visitor<'de> for BytesVisitor { - type Value = Scalar; +// impl<'de> Visitor<'de> for BytesVisitor { +// type Value = Scalar; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a valid Scalar in byte representation") - } +// fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { +// formatter.write_str("a valid Scalar in byte representation") +// } - fn visit_bytes(self, v: &[u8]) -> Result - where - E: de::Error, - { - let mut bytes = ::Repr::default(); - assert_eq!(bytes.len(), v.len()); - bytes.copy_from_slice(v); +// fn visit_bytes(self, v: &[u8]) -> Result +// where +// E: de::Error, +// { +// let mut bytes = ::Repr::default(); +// assert_eq!(bytes.len(), v.len()); +// bytes.copy_from_slice(v); - Ok(Scalar::from_bytes(&bytes).unwrap()) - } - } +// Ok(Scalar::from_bytes(&bytes).unwrap()) +// } +// } - deserializer.deserialize_bytes(BytesVisitor) - } -} +// deserializer.deserialize_bytes(BytesVisitor) +// } +// } -mod serde_point { - use super::SubgroupPoint; - use group::GroupEncoding; - use serde::de::{self, Visitor}; - use serde::{Deserializer, Serializer}; - use std::fmt; +// mod serde_point { +// use super::SubgroupPoint; +// use group::GroupEncoding; +// use serde::de::{self, Visitor}; +// use serde::{Deserializer, Serializer}; +// use std::fmt; - // Serialize a SubgroupPoint by converting it to bytes. - pub fn serialize(point: &SubgroupPoint, serializer: S) -> Result - where - S: Serializer, - { - let bytes = point.to_bytes(); - serializer.serialize_bytes(&bytes) - } +// // Serialize a SubgroupPoint by converting it to bytes. +// pub fn serialize(point: &SubgroupPoint, serializer: S) -> Result +// where +// S: Serializer, +// { +// let bytes = point.to_bytes(); +// serializer.serialize_bytes(&bytes) +// } - // Deserialize a SubgroupPoint by converting it from bytes. - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct BytesVisitor; +// // Deserialize a SubgroupPoint by converting it from bytes. +// pub fn deserialize<'de, D>(deserializer: D) -> Result +// where +// D: Deserializer<'de>, +// { +// struct BytesVisitor; - impl<'de> Visitor<'de> for BytesVisitor { - type Value = SubgroupPoint; +// impl<'de> Visitor<'de> for BytesVisitor { +// type Value = SubgroupPoint; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a valid SubgroupPoint in byte representation") - } +// fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { +// formatter.write_str("a valid SubgroupPoint in byte representation") +// } - fn visit_bytes(self, v: &[u8]) -> Result - where - E: de::Error, - { - let mut bytes = ::Repr::default(); - assert_eq!(bytes.len(), v.len()); - bytes.copy_from_slice(v); +// fn visit_bytes(self, v: &[u8]) -> Result +// where +// E: de::Error, +// { +// let mut bytes = ::Repr::default(); +// assert_eq!(bytes.len(), v.len()); +// bytes.copy_from_slice(v); - Ok(SubgroupPoint::from_bytes(&bytes).unwrap()) - } - } +// Ok(SubgroupPoint::from_bytes(&bytes).unwrap()) +// } +// } + +// deserializer.deserialize_bytes(BytesVisitor) +// } +// } - deserializer.deserialize_bytes(BytesVisitor) - } -} #[cfg(test)] mod test { use crate::test_util::seed_rng; + use k256::elliptic_curve::group::prime::PrimeCurveAffine; use super::*; @@ -168,20 +174,20 @@ mod test { #[test] fn test_balance_blinding() { // balances are blinded - let r1 = Scalar::from(12); - let r2 = Scalar::from(8); + let r1 = Scalar::from(12u32); + let r2 = Scalar::from(8u32); let a_w = BalanceWitness::new(10, "NMO", r1); let b_w = BalanceWitness::new(10, "NMO", r2); let a = a_w.commit(); let b = b_w.commit(); assert_ne!(a, b); - assert_eq!(a.0 - b.0, BalanceWitness::new(0, "NMO", r1 - r2).commit().0); + assert_eq!(a.0.to_curve() - b.0.to_curve(), BalanceWitness::new(0, "NMO", r1 - r2).commit().0.to_curve()); } #[test] fn test_balance_units() { // Unit's differentiate between values. - let r = Scalar::from(1337); + let r = Scalar::from(1337u32); let nmo = BalanceWitness::new(10, "NMO", r); let eth = BalanceWitness::new(10, "ETH", r); assert_ne!(nmo.commit(), eth.commit()); @@ -192,18 +198,18 @@ mod test { let mut rng = seed_rng(0); let r1 = Scalar::random(&mut rng); let r2 = Scalar::random(&mut rng); - let ten = BalanceWitness::new(10, "NMO", 0.into()); - let eight = BalanceWitness::new(8, "NMO", 0.into()); - let two = BalanceWitness::new(2, "NMO", 0.into()); + let ten = BalanceWitness::new(10, "NMO", 0u32.into()); + let eight = BalanceWitness::new(8, "NMO", 0u32.into()); + let two = BalanceWitness::new(2, "NMO", 0u32.into()); // Values of same unit are homomorphic - assert_eq!(ten.commit().0 - eight.commit().0, two.commit().0); + assert_eq!(ten.commit().0.to_curve() - eight.commit().0.to_curve(), two.commit().0.to_curve()); // Blinding factors are also homomorphic. assert_eq!( - BalanceWitness::new(10, "NMO", r1).commit().0 - - BalanceWitness::new(10, "NMO", r2).commit().0, - BalanceWitness::new(0, "NMO", r1 - r2).commit().0 + BalanceWitness::new(10, "NMO", r1).commit().0.to_curve() + - BalanceWitness::new(10, "NMO", r2).commit().0.to_curve(), + BalanceWitness::new(0, "NMO", r1 - r2).commit().0.to_curve() ); } } diff --git a/cl/src/bundle.rs b/cl/src/bundle.rs index 55a6b09..4edcdb2 100644 --- a/cl/src/bundle.rs +++ b/cl/src/bundle.rs @@ -1,8 +1,9 @@ use std::collections::BTreeSet; -use jubjub::{Scalar, SubgroupPoint}; use serde::{Deserialize, Serialize}; +use k256::{Scalar, ProjectivePoint}; + use crate::{ error::Error, note::NoteCommitment, @@ -30,7 +31,7 @@ pub struct BundleProof { } impl Bundle { - pub fn balance(&self) -> SubgroupPoint { + pub fn balance(&self) -> ProjectivePoint { self.partials.iter().map(|ptx| ptx.balance()).sum() } diff --git a/cl/src/crypto.rs b/cl/src/crypto.rs index ecc3a3f..8f40569 100644 --- a/cl/src/crypto.rs +++ b/cl/src/crypto.rs @@ -1,13 +1,8 @@ -use blake2::{Blake2s256, Digest}; -use group::Group; -use jubjub::SubgroupPoint; -use rand_chacha::ChaCha20Rng; -use rand_core::SeedableRng; +use k256::{Secp256k1, ProjectivePoint, elliptic_curve::{ + hash2curve::{GroupDigest, ExpandMsgXmd}, +},sha2::Sha256 +}; -pub fn hash_to_curve(bytes: &[u8]) -> SubgroupPoint { - let mut hasher = Blake2s256::new(); - hasher.update(b"NOMOS_HASH_TO_CURVE"); - hasher.update(bytes); - let seed: [u8; 32] = hasher.finalize().into(); - SubgroupPoint::random(ChaCha20Rng::from_seed(seed)) +pub fn hash_to_curve(bytes: &[u8]) -> ProjectivePoint { + Secp256k1::hash_from_bytes::>(&[bytes], &[b"NOMOS_HASH_TO_CURVE"]).unwrap() } diff --git a/cl/src/input.rs b/cl/src/input.rs index afc3075..92530c6 100644 --- a/cl/src/input.rs +++ b/cl/src/input.rs @@ -105,11 +105,11 @@ impl Input { && death_constraint_is_satisfied } - pub fn to_bytes(&self) -> [u8; 96] { - let mut bytes = [0u8; 96]; + pub fn to_bytes(&self) -> [u8; 97] { + let mut bytes = [0u8; 97]; bytes[..32].copy_from_slice(self.note_comm.as_bytes()); bytes[32..64].copy_from_slice(self.nullifier.as_bytes()); - bytes[64..96].copy_from_slice(&self.balance.to_bytes()); + bytes[64..97].copy_from_slice(&self.balance.to_bytes()); bytes } } diff --git a/cl/src/note.rs b/cl/src/note.rs index abb2876..98d2a42 100644 --- a/cl/src/note.rs +++ b/cl/src/note.rs @@ -1,7 +1,7 @@ use blake2::{Blake2s256, Digest}; -use group::GroupEncoding; use rand_core::RngCore; use serde::{Deserialize, Serialize}; +use k256::elliptic_curve::group::GroupEncoding; use crate::{ balance::{Balance, BalanceWitness}, diff --git a/cl/src/output.rs b/cl/src/output.rs index f25c609..846f697 100644 --- a/cl/src/output.rs +++ b/cl/src/output.rs @@ -61,10 +61,10 @@ impl Output { && self.balance == witness.note.balance() } - pub fn to_bytes(&self) -> [u8; 64] { - let mut bytes = [0u8; 64]; + pub fn to_bytes(&self) -> [u8; 65] { + let mut bytes = [0u8; 65]; bytes[..32].copy_from_slice(self.note_comm.as_bytes()); - bytes[32..64].copy_from_slice(&self.balance.to_bytes()); + bytes[32..65].copy_from_slice(&self.balance.to_bytes()); bytes } } diff --git a/cl/src/partial_tx.rs b/cl/src/partial_tx.rs index c08cb24..9dcecd5 100644 --- a/cl/src/partial_tx.rs +++ b/cl/src/partial_tx.rs @@ -1,17 +1,18 @@ use std::collections::BTreeSet; -use jubjub::SubgroupPoint; use rand_core::RngCore; use risc0_groth16::ProofJson; use serde::{Deserialize, Serialize}; +use k256::ProjectivePoint; +use k256::elliptic_curve::group::prime::PrimeCurveAffine; use crate::error::Error; use crate::input::{Input, InputProof, InputWitness}; use crate::merkle; use crate::output::{Output, OutputProof, OutputWitness}; -const MAX_INPUTS: usize = 32; -const MAX_OUTPUTS: usize = 32; +const MAX_INPUTS: usize = 8; +const MAX_OUTPUTS: usize = 8; /// The partial transaction commitment couples an input to a partial transaction. /// Prevents partial tx unbundling. @@ -157,9 +158,9 @@ impl PartialTx { .all(|(o, p)| o.verify(p)) } - pub fn balance(&self) -> SubgroupPoint { - let in_sum: SubgroupPoint = self.inputs.iter().map(|i| i.balance.0).sum(); - let out_sum: SubgroupPoint = self.outputs.iter().map(|o| o.balance.0).sum(); + pub fn balance(&self) -> ProjectivePoint { + let in_sum: ProjectivePoint = self.inputs.iter().map(|i| i.balance.0.to_curve()).sum(); + let out_sum: ProjectivePoint = self.outputs.iter().map(|o| o.balance.0.to_curve()).sum(); out_sum - in_sum } diff --git a/goas/Cargo.toml b/goas/Cargo.toml index 8aa6d3c..662a95a 100644 --- a/goas/Cargo.toml +++ b/goas/Cargo.toml @@ -9,3 +9,10 @@ opt-level = 3 [profile.release] debug = 1 lto = true + +[patch.crates-io] +# Placing these patch statement in the workspace Cargo.toml will add RISC Zero SHA-256 and bigint +# multiplication accelerator support for all downstream usages of the following crates. +# sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.6-risczero.0" } +k256 = { git = "https://github.com/risc0/RustCrypto-elliptic-curves", tag = "k256/v0.13.3-risczero.0" } +crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.2-risczero.0" } diff --git a/goas/methods/guest/Cargo.toml b/goas/methods/guest/Cargo.toml index 4bbf258..6786a21 100644 --- a/goas/methods/guest/Cargo.toml +++ b/goas/methods/guest/Cargo.toml @@ -1,3 +1,5 @@ + + [package] name = "method" version = "0.1.0" diff --git a/goas/methods/guest/src/main.rs b/goas/methods/guest/src/main.rs index a2f6543..21e9da9 100644 --- a/goas/methods/guest/src/main.rs +++ b/goas/methods/guest/src/main.rs @@ -2,7 +2,6 @@ use blake2::{Blake2s256, Digest}; use risc0_zkvm::guest::env; use common::*; use cl::merkle; -use cl::note::NoteWitness; use cl::input::InputWitness; use cl::output::OutputWitness; @@ -24,22 +23,29 @@ fn execute( mut journal: Journal, ) { // verify ptx/cl preconditions - + eprintln!("start exec: {}", env::cycle_count()); assert_eq!(ptx_root, merkle::node(input_root, output_root)); - + eprintln!("ptx_root: {}", env::cycle_count()); + // Glue the zone and the cl together, specifically, it verifies the note requesting // a transfer is included as part of the same transaction in the cl - assert!(merkle::verify_path(merkle::leaf(&in_note.commit().to_bytes()), &in_ptx_path, input_root)); + let in_comm = in_note.commit().to_bytes(); + eprintln!("input comm: {}", env::cycle_count()); + + assert!(merkle::verify_path(merkle::leaf(&in_comm), &in_ptx_path, input_root)); + eprintln!("input merkle path: {}", env::cycle_count()); // check the commitments match the actual data let state_cm = calculate_state_hash(&state); let journal_cm = calculate_journal_hash(&journal); let state_root = merkle::node(state_cm, journal_cm); assert_eq!(state_root, in_note.note.state); + eprintln!("input state root: {}", env::cycle_count()); // then run the state transition function let state = stf(state, input); journal.push(input); + eprintln!("stf: {}", env::cycle_count()); // verifying ptx/cl postconditions @@ -48,11 +54,16 @@ fn execute( let out_state_root = merkle::node(out_state_cm, out_journal_cm); // TODO: verify death constraints are propagated assert_eq!(out_state_root, out_note.note.state); + eprintln!("out state root: {}", env::cycle_count()); // Glue the zone and the cl together, specifically, it verifies an output note // containing the zone state is included as part of the same transaction in the cl // (this is done in the death condition to disallow burning) - assert!(merkle::verify_path(merkle::leaf(&out_note.commit().to_bytes()), &out_ptx_path, output_root)); + let out_comm = out_note.commit().to_bytes(); + eprintln!("output comm: {}", env::cycle_count()); + + assert!(merkle::verify_path(merkle::leaf(&out_comm), &out_ptx_path, output_root)); + eprintln!("out merkle proof: {}", env::cycle_count()); } fn main() { @@ -70,6 +81,7 @@ fn main() { let state: State = env::read(); let journal: Journal = env::read(); + eprintln!("parse input: {}", env::cycle_count()); execute(ptx_root, input_root, output_root, in_ptx_path, out_ptx_path, in_note, out_note, input, state, journal); }