From 3995e4f831db67a92eb52f24ac2431d829d1d848 Mon Sep 17 00:00:00 2001 From: Nickqiaoo Date: Tue, 19 Dec 2023 22:34:12 +0800 Subject: [PATCH] feat:add deposit --- Cargo.toml | 4 +- src/circuit.rs | 172 ------------------------------------------------ src/coin.rs | 77 ---------------------- src/deposit.rs | 20 +----- src/main.rs | 5 +- src/merkle.rs | 7 ++ src/mint.rs | 37 ----------- src/pour.rs | 124 ---------------------------------- src/transfer.rs | 63 ++++++++++++++++++ src/verify.rs | 19 ++++++ src/wallet.rs | 2 +- src/wallets.rs | 2 +- src/withdraw.rs | 1 + 13 files changed, 99 insertions(+), 434 deletions(-) delete mode 100644 src/circuit.rs delete mode 100644 src/coin.rs create mode 100644 src/merkle.rs delete mode 100644 src/mint.rs delete mode 100644 src/pour.rs create mode 100644 src/verify.rs create mode 100644 src/withdraw.rs diff --git a/Cargo.toml b/Cargo.toml index bb43ecc..fc264b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,6 @@ ecies = "0.2.6" halo2_proofs = "0.3.0" plotters = { version = "0.3.0", default-features = true, optional = true } pasta_curves = "0.5" -orchard = "0.6.0" \ No newline at end of file +orchard = "0.6.0" +bridgetree = "0.4" +lazy_static = "1.4" \ No newline at end of file diff --git a/src/circuit.rs b/src/circuit.rs deleted file mode 100644 index 19dc1e3..0000000 --- a/src/circuit.rs +++ /dev/null @@ -1,172 +0,0 @@ -use crate::coin::Coin; -use halo2_gadgets::sha256::{BlockWord,Bits,AssignedBits, Table16Chip, Table16Config}; -use halo2_proofs::{ - arithmetic::Field, - circuit::{AssignedCell, Layouter, SimpleFloorPlanner, Value}, - plonk::{Advice, Assigned, Circuit, Column, ConstraintSystem, Error, Instance, Selector}, - poly::Rotation, -}; -use pasta_curves::{arithmetic::CurveAffine, pallas, vesta}; -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize)] -pub struct InstanceX { - pub rt: Vec, - pub old_sn: Vec, - pub new_cm: Vec, - pub public_value: u64, - pub h_sig: Vec, - pub h: Vec, -} - -pub struct WitnessA { - pub path: Vec>, - pub old_coin: Coin, - pub secret_key: String, - pub new_coin: Coin, -} - -pub fn create_proof(x: &InstanceX, a: &WitnessA) -> Vec { - vec![] -} - -#[derive(Debug, Clone)] -struct CircuitConfig { - advice: Column, - instance: Column, - sha_config: Table16Config, -} - -#[derive(Default)] -struct PourCircuit { - pk: [BlockWord;16], - sk: [BlockWord;8], -} - -impl PourCircuit { - fn load_private( - &self, - config: &CircuitConfig, - mut layouter: impl Layouter, - values: [BlockWord;16] - ) -> Result>, Error> { - layouter.assign_region( - || "assign private values", - |mut region| { - values - .iter() - .enumerate() - .map(|(i, value)| { - // Check that each cell of the input is a binary value - // Assign the private input value to an advice cell - region - .assign_advice(|| "assign private input", config.advice, i, || -> Bits<32> {value.0.into()}) - } - ) - .collect() - } - ) - } -} - - -impl Circuit for PourCircuit { - type Config = CircuitConfig; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let sha_config = Table16Chip::configure(meta); - - let advice = meta.advice_column(); - let instance = meta.instance_column(); - - meta.enable_equality(instance); - meta.enable_equality(advice); - - CircuitConfig { - advice, - instance, - sha_config, - } - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - Table16Chip::load(config.sha_config.clone(), &mut layouter)?; - let table16_chip = Table16Chip::construct(config.sha_config); - - let a = self.load_private(&config, layouter, self.pk); - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use halo2_proofs::{dev::MockProver, pasta::Fp}; - #[test] - fn test_chap_1() { - // ANCHOR: test-circuit - // The number of rows in our circuit cannot exceed 2^k. Since our example - // circuit is very small, we can pick a very small value here. - let k = 5; - - // Prepare the private and public inputs to the circuit! - let c = Fp::from(1); - let a = Fp::from(2); - let b = Fp::from(3); - let out = c * a.square() * b.square(); - println!("out=:{:?}", out); - - // Instantiate the circuit with the private inputs. - let circuit = PourCircuit {}; - - // Arrange the public input. We expose the multiplication result in row 0 - // of the instance column, so we position it there in our public inputs. - let mut public_inputs = vec![out]; - - // Given the correct public input, our circuit will verify. - let prover = MockProver::run(k, &circuit, vec![public_inputs.clone()]).unwrap(); - assert_eq!(prover.verify(), Ok(())); - - // If we try some other public input, the proof will fail! - public_inputs[0] += Fp::one(); - let prover = MockProver::run(k, &circuit, vec![public_inputs]).unwrap(); - assert!(prover.verify().is_err()); - println!("\n\n\n!!!!!OHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH!!!!!\n simple example success !\n!!!!!OHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH!!!!!\n\n\n") - // ANCHOR_END: test-circuit - } - - #[cfg(feature = "dev-graph")] - //cargo test plot_chap_1_circuit --features dev-graph - #[test] - fn plot_chap_1_circuit() { - // Instantiate the circuit with the private inputs. - let circuit = PourCircuit::::default(); - // Create the area you want to draw on. - // Use SVGBackend if you want to render to .svg instead. - use plotters::prelude::*; - let root = BitMapBackend::new("./chap_1_simple.png", (1024, 768)).into_drawing_area(); - root.fill(&WHITE).unwrap(); - let root = root - .titled("Simple Circuit without chip", ("sans-serif", 60)) - .unwrap(); - halo2_proofs::dev::CircuitLayout::default() - // You can optionally render only a section of the circuit. - // .view_width(0..2) - // .view_height(0..16) - // You can hide labels, which can be useful with smaller areas. - .show_labels(true) - // Render the circuit onto your area! - // The first argument is the size parameter for the circuit. - .render(5, &circuit, &root) - .unwrap(); - } -} diff --git a/src/coin.rs b/src/coin.rs deleted file mode 100644 index 9ca0a5f..0000000 --- a/src/coin.rs +++ /dev/null @@ -1,77 +0,0 @@ -use rand::Rng; -use secp256k1::{PublicKey, Secp256k1}; -use sha2::{Digest, Sha256}; - -#[derive(Clone)] -pub struct Coin { - addr_pk: String, - pub rho: Vec, - pub v: u64, - pub r: Vec, - cm: Vec, -} - -impl Coin { - pub fn new(public_key: &String, value: u64) -> Self { - let rho = generate_random_bytes(256); - let r = generate_random_bytes(384); - - let k = Self::get_k_inner(public_key, &rho, &r); - let cm = Self::get_cm(&k, value); - Coin { - addr_pk: public_key.clone(), - rho, - v: value, - r, - cm, - } - } - pub fn get_k(&self) -> Vec { - Self::get_k_inner(&self.addr_pk, &self.rho, &self.r) - } - - //H(r || H(pk || rho)) - fn get_k_inner(public_key: &String, rho: &Vec, r: &Vec) -> Vec { - let public_key_bytes = hex::decode(public_key).expect("Failed to decode public key"); - let secp = Secp256k1::new(); - let public_key = PublicKey::from_slice(&public_key_bytes).expect("Invalid public key"); - - let mut combined_data = Vec::new(); - combined_data.extend_from_slice(&public_key.serialize_uncompressed()); - combined_data.extend(rho); - - let midk = Sha256::digest(&combined_data).to_vec(); - let truncated_hash: Vec = midk.iter().take(128 / 8).cloned().collect(); - - combined_data = Vec::new(); - combined_data.extend(truncated_hash); - combined_data.extend(r); - - Sha256::digest(&combined_data).to_vec() - } - - pub fn cm(&self) -> Vec { - self.cm.clone() - } - - pub fn get_cm(k: &Vec, v: u64) -> Vec { - let zero_padding: Vec = vec![0; 192 / 8]; - - let mut combined_data = Vec::new(); - combined_data.extend(k); - combined_data.extend(zero_padding); - combined_data.extend_from_slice(&v.to_be_bytes()); - - Sha256::digest(&combined_data).to_vec() - } -} - -pub fn generate_random_bytes(bits: usize) -> Vec { - let mut rng = rand::thread_rng(); - - let byte_count = (bits + 7) / 8; - - let random_bytes: Vec = (0..byte_count).map(|_| rng.gen()).collect(); - - random_bytes -} diff --git a/src/deposit.rs b/src/deposit.rs index fe6d332..dedae11 100644 --- a/src/deposit.rs +++ b/src/deposit.rs @@ -1,4 +1,4 @@ -use crate::{wallets::Wallets}; +use crate::{wallets::Wallets, merkle,}; use rand::rngs::OsRng; use orchard::{ builder::Builder, @@ -27,11 +27,11 @@ pub fn deposit(address: String, value: u64) -> Bundle { // Create a shielding bundle. let shielding_bundle: Bundle<_, i64> = { // Use the empty tree. - let anchor = MerkleHashOrchard::empty_root(32.into()).into(); + let anchor = merkle::MERKLE.root(0).unwrap().into(); let mut builder = Builder::new(Flags::from_parts(false, true), anchor); assert_eq!( - builder.add_recipient(None, recipient, NoteValue::from_raw(5000), None), + builder.add_recipient(None, recipient, NoteValue::from_raw(value), None), Ok(()) ); let unauthorized = builder.build(&mut rng).unwrap(); @@ -40,18 +40,4 @@ pub fn deposit(address: String, value: u64) -> Bundle { proven.apply_signatures(rng, sighash, &[]).unwrap() }; shielding_bundle -} - -pub fn verify_bundle(bundle: &Bundle) { - let vk = VerifyingKey::build(); - assert!(matches!(bundle.verify_proof(&vk), Ok(()))); - let sighash: [u8; 32] = bundle.commitment().into(); - let bvk = bundle.binding_validating_key(); - for action in bundle.actions() { - assert_eq!(action.rk().verify(&sighash, action.authorization()), Ok(())); - } - assert_eq!( - bvk.verify(&sighash, bundle.authorization().binding_signature()), - Ok(()) - ); } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 477d64f..85a8ebe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,12 +2,8 @@ use structopt::StructOpt; mod block; mod blockchain; -mod circuit; mod cli; -mod coin; mod iterator; -mod mint; -mod pour; mod pow; mod transaction; mod transaction_input; @@ -15,6 +11,7 @@ mod transaction_output; mod wallet; mod wallets; mod deposit; +mod merkle; mod transfer; fn main() { diff --git a/src/merkle.rs b/src/merkle.rs new file mode 100644 index 0000000..dbfb7bc --- /dev/null +++ b/src/merkle.rs @@ -0,0 +1,7 @@ +use bridgetree::BridgeTree; +use orchard::tree::{MerkleHashOrchard, MerklePath}; +use lazy_static::lazy_static; + +lazy_static! { + pub static ref MERKLE:BridgeTree:: = BridgeTree::::new(100); +} diff --git a/src/mint.rs b/src/mint.rs deleted file mode 100644 index 87e81a6..0000000 --- a/src/mint.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::{coin::Coin, wallets::Wallets}; - -pub struct TXMint { - v: u64, - k: Vec, - cm: Vec, -} - -pub struct MintTransaction { - pub id: Vec, - pub vout: TXMint, -} - -pub fn mint(address: String, value: u64) -> (Coin, MintTransaction) { - let wallets = Wallets::new(); - let wallet = wallets.get_wallet(&address).unwrap(); - - let c = Coin::new(&wallet.public_key, value); - let cm = c.cm(); - let k = c.get_k(); - ( - c, - MintTransaction { - id: vec![], - vout: TXMint { - v: value, - k, - cm: cm, - }, - }, - ) -} - -pub fn verify_mint(tx: &MintTransaction) -> bool { - let cm = Coin::get_cm(&tx.vout.k, tx.vout.v); - cm == tx.vout.cm -} diff --git a/src/pour.rs b/src/pour.rs deleted file mode 100644 index 6b7837f..0000000 --- a/src/pour.rs +++ /dev/null @@ -1,124 +0,0 @@ -use crate::{ - circuit::{self, WitnessA, InstanceX}, - coin::Coin, - wallet::Wallet, - wallets::Wallets, -}; -use ecies::{decrypt, encrypt}; -use sha2::{Digest, Sha256}; - -pub struct TXPour { - rt: Vec, - old_sn: Vec, - new_cm: Vec, - public_value: u64, - info: String, - pk_sig: String, - h: Vec, - pi_pour: Vec, - c_info: Vec, - sigma: Vec, -} - -pub struct PourTransaction { - pub id: Vec, - pub vout: TXPour, -} - -pub fn pour( - merkle_root: &Vec, - old_coin: &Coin, - old_adress: String, - merkle_path: &Vec>, - new_value: u64, - new_address: String, - public_value: u64, - info: String, -) -> (Coin, PourTransaction) { - let wallets = Wallets::new(); - let wallet_new = wallets.get_wallet(&new_address).unwrap(); - let wallet_old = wallets.get_wallet(&old_adress).unwrap(); - - let mut sn_msg = wallet_old.private_key.as_bytes().to_vec(); - sn_msg.extend(&old_coin.rho); - let old_sn = Sha256::digest(sn_msg).to_vec(); - - let c = Coin::new(&wallet_new.public_key, new_value); - let c_info = create_c_info(&wallet_new.public_key, &c.rho, c.v, &c.r); - - let sig_wallet = Wallet::new(); - let h_sig = Sha256::digest(&sig_wallet.public_key).to_vec(); - - let mut h_msg = wallet_old.private_key.as_bytes().to_vec(); - h_msg.extend(&h_sig); - let h = Sha256::digest(h_msg).to_vec(); - - let wx = InstanceX { - rt: merkle_root.clone(), - old_sn: old_sn.clone(), - new_cm: c.cm(), - public_value, - h_sig: h_sig.clone(), - h: h.clone(), - }; - let wa = WitnessA { - path: merkle_path.clone(), - old_coin: old_coin.clone(), - secret_key: wallet_old.private_key.clone(), - new_coin: c.clone(), - }; - - let pi_pour = circuit::create_proof(&wx, &wa); - let sigma = create_sig(&sig_wallet.private_key, &wx, &pi_pour, &info, &c_info); - let cm = c.cm(); - ( - c, - PourTransaction { - id: vec![], - vout: TXPour { - rt: merkle_root.clone(), - old_sn, - new_cm: cm, - public_value, - info, - pk_sig: sig_wallet.public_key, - h, - pi_pour, - c_info, - sigma, - }, - }, - ) -} - -fn create_c_info(public_key: &String, rho: &Vec, v: u64, r: &Vec) -> Vec { - let mut message = Vec::new(); - message.extend(rho); - message.extend(&v.to_be_bytes().to_vec()); - message.extend(r); - - encrypt(public_key.as_bytes(), &message).unwrap() -} - -fn create_sig( - sk: &String, - x: &InstanceX, - pi_pour: &Vec, - info: &String, - c_info: &Vec, -) -> Vec { - let priv_key = secp256k1::SecretKey::from_slice(hex::decode(sk).unwrap().as_slice()).unwrap(); - - let mut msg = serde_json::to_string(x).unwrap(); - msg = format!("{}{:?}{}{:?}", msg, pi_pour, info, c_info); - let sig_message = secp256k1::Message::from_digest_slice(&msg.as_bytes()).unwrap(); - - let secp = secp256k1::Secp256k1::new(); - secp.sign_ecdsa(&sig_message, &priv_key) - .serialize_compact() - .to_vec() -} - -pub fn verify_pour(tx: &PourTransaction) -> bool { - false -} diff --git a/src/transfer.rs b/src/transfer.rs index 6a17e20..018ecf7 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -1,3 +1,66 @@ +use orchard::{ + builder::Builder, + bundle::{Authorized, Flags}, + circuit::{ProvingKey, VerifyingKey}, + keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendAuthorizingKey, SpendingKey}, + note::ExtractedNoteCommitment, + note_encryption::OrchardDomain, + tree::{MerkleHashOrchard, MerklePath}, + value::NoteValue, + Bundle, +}; +use crate::{wallets::Wallets}; +use rand::rngs::OsRng; + pub fn transfer(address: String, value: u64) -> Bundle { + let wallets = Wallets::new(); + let wallet = wallets.get_wallet(&address).unwrap(); + let mut rng = OsRng; + let pk = ProvingKey::build(); + + let sk = wallet.sk(); + let fvk = FullViewingKey::from(&sk); + let recipient = fvk.address_at(0u32, Scope::External); + + let shielded_bundle: Bundle<_, i64> = { + let ivk = PreparedIncomingViewingKey::new(&fvk.to_ivk(Scope::External)); + let (note, _, _) = shielding_bundle + .actions() + .iter() + .find_map(|action| { + let domain = OrchardDomain::for_action(action); + try_note_decryption(&domain, &ivk, action) + }) + .unwrap(); + + // Use the tree with a single leaf. + let cmx: ExtractedNoteCommitment = note.commitment().into(); + let leaf = MerkleHashOrchard::from_cmx(&cmx); + let mut tree = BridgeTree::::new(100); + tree.append(leaf); + let position = tree.mark().unwrap(); + let root = tree.root(0).unwrap(); + let auth_path = tree.witness(position, 0).unwrap(); + let merkle_path = MerklePath::from_parts( + u64::from(position).try_into().unwrap(), + auth_path[..].try_into().unwrap(), + ); + let anchor = root.into(); + assert_eq!(anchor, merkle_path.root(cmx)); + + let mut builder = Builder::new(Flags::from_parts(true, true), anchor); + assert_eq!(builder.add_spend(fvk, note, merkle_path), Ok(())); + assert_eq!( + builder.add_recipient(None, recipient, NoteValue::from_raw(5000), None), + Ok(()) + ); + let unauthorized = builder.build(&mut rng).unwrap(); + let sighash = unauthorized.commitment().into(); + let proven = unauthorized.create_proof(&pk, &mut rng).unwrap(); + proven + .apply_signatures(rng, sighash, &[SpendAuthorizingKey::from(&sk)]) + .unwrap() + }; + shielded_bundle } \ No newline at end of file diff --git a/src/verify.rs b/src/verify.rs new file mode 100644 index 0000000..98117a7 --- /dev/null +++ b/src/verify.rs @@ -0,0 +1,19 @@ +use orchard::{ + bundle::Authorized, + circuit::VerifyingKey, + Bundle, +}; + +pub fn verify_bundle(bundle: &Bundle) { + let vk = VerifyingKey::build(); + assert!(matches!(bundle.verify_proof(&vk), Ok(()))); + let sighash: [u8; 32] = bundle.commitment().into(); + let bvk = bundle.binding_validating_key(); + for action in bundle.actions() { + assert_eq!(action.rk().verify(&sighash, action.authorization()), Ok(())); + } + assert_eq!( + bvk.verify(&sighash, bundle.authorization().binding_signature()), + Ok(()) + ); +} \ No newline at end of file diff --git a/src/wallet.rs b/src/wallet.rs index 3aab807..b37a9ca 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -10,7 +10,7 @@ use orchard::keys; const VERSION: u8 = 0x00; pub(crate) const CHECKSUM_LENGTH: usize = 4; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] pub struct Wallet { pub private_key: String, pub public_key: String, diff --git a/src/wallets.rs b/src/wallets.rs index 2f85bac..dfe9c2e 100644 --- a/src/wallets.rs +++ b/src/wallets.rs @@ -25,7 +25,7 @@ impl Wallets { let address = wallet.get_address(); let zaddr = wallet.get_z_address(); - self.wallets.insert(address.clone(), wallet); + self.wallets.insert(address.clone(), wallet.clone()); self.zwallets.insert(zaddr.clone(), wallet); address diff --git a/src/withdraw.rs b/src/withdraw.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/withdraw.rs @@ -0,0 +1 @@ +