| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- use bellman::groth16;
- use bls12_381::Bls12;
- use ff::{Field, PrimeField};
- use group::Group;
- use rand::rngs::OsRng;
- use std::io;
- use std::path::Path;
- use sapvi::crypto::{
- coin::Coin,
- create_mint_proof, create_spend_proof, load_params,
- merkle::{CommitmentTree, IncrementalWitness},
- note::Note,
- save_params, setup_mint_prover, setup_spend_prover, verify_mint_proof, verify_spend_proof,
- MintRevealedValues, SpendRevealedValues,
- };
- use sapvi::error::{Error, Result};
- use sapvi::serial::{Decodable, Encodable, VarInt};
- use sapvi::state::{state_transition, ProgramState, StateUpdates};
- use sapvi::tx;
- struct MemoryState {
- mint_pvk: groth16::PreparedVerifyingKey<Bls12>,
- spend_pvk: groth16::PreparedVerifyingKey<Bls12>,
- cashier_public: jubjub::SubgroupPoint,
- }
- impl ProgramState for MemoryState {
- fn is_valid_cashier_public_key(&self, public: &jubjub::SubgroupPoint) -> bool {
- public == &self.cashier_public
- }
- fn mint_pvk(&self) -> &groth16::PreparedVerifyingKey<Bls12> {
- &self.mint_pvk
- }
- fn spend_pvk(&self) -> &groth16::PreparedVerifyingKey<Bls12> {
- &self.spend_pvk
- }
- }
- impl MemoryState {
- fn apply(updates: StateUpdates) {}
- }
- fn main() {
- // Auto create trusted ceremony parameters if they don't exist
- if !Path::new("mint.params").exists() {
- let params = setup_mint_prover();
- save_params("mint.params", ¶ms);
- }
- if !Path::new("spend.params").exists() {
- let params = setup_spend_prover();
- save_params("spend.params", ¶ms);
- }
- // Load trusted setup parameters
- let (mint_params, mint_pvk) = load_params("mint.params").expect("params should load");
- let (spend_params, spend_pvk) = load_params("spend.params").expect("params should load");
- // Cashier creates a secret key
- let cashier_secret = jubjub::Fr::random(&mut OsRng);
- // This is their public key
- let cashier_public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * cashier_secret;
- let state = MemoryState {
- mint_pvk,
- spend_pvk,
- cashier_public,
- };
- // Wallet 1 creates a secret key
- let secret = jubjub::Fr::random(&mut OsRng);
- // This is their public key
- let public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret;
- // Step 1: Cashier deposits to wallet1's address
- // Create the deposit for 110 BTC
- // Clear inputs are visible to everyone on the network
- let builder = tx::TransactionBuilder {
- clear_inputs: vec![tx::TransactionBuilderClearInputInfo {
- value: 110,
- signature_secret: cashier_secret,
- }],
- inputs: vec![],
- outputs: vec![tx::TransactionBuilderOutputInfo { value: 110, public }],
- };
- // We will 'compile' the tx, and then serialize it to this Vec<u8>
- let mut tx_data = vec![];
- {
- // Build the tx
- let tx = builder.build(&mint_params, &spend_params);
- // Now serialize it
- tx.encode(&mut tx_data).expect("encode tx");
- }
- // Step 1 is completed.
- // Tx data is posted to the blockchain
- // Step 2: wallet1 receive's payment from the cashier
- // Wallet1 is receiving tx, and for every new coin it finds, it adds to its merkle tree
- let mut tree = CommitmentTree::empty();
- // Here we simulate 5 fake random coins, adding them to our tree.
- for i in 0..5 {
- let cmu = Coin::new(bls12_381::Scalar::random(&mut OsRng).to_repr());
- tree.append(cmu);
- }
- // Now we receive the tx data
- let note = {
- let txx = tx::Transaction::decode(&tx_data[..]).unwrap();
- let tx = tx::Transaction::decode(&tx_data[..]).unwrap();
- let update = state_transition(&state, txx).expect("step 2 state transition failed");
- // Check the tx verifies correctly
- //assert!(tx.verify(&mint_pvk, &spend_pvk));
- // Add the new coins to the merkle tree
- tree.append(Coin::new(tx.outputs[0].revealed.coin))
- .expect("append merkle");
- // Now for every new tx we receive, the wallets should iterate over all outputs
- // and try to decrypt the coin's note.
- // If they can successfully decrypt it, then it's a coin destined for us.
- // Try to decrypt output note
- let note = tx.outputs[0]
- .enc_note
- .decrypt(&secret)
- .expect("note should be destined for us");
- // This contains the secret attributes so we can spend the coin
- note
- };
- // Wallet1 has received payment from the cashier.
- // Step 2 is complete.
- // We need to keep track of the witness for this coin.
- // This allows us to prove inclusion of the coin in the merkle tree with ZK.
- // Just as we update the merkle tree with every new coin, so we do the same with the witness.
- // Derive the current witness from the current tree.
- // This is done right after we add our coin to the tree (but before any other coins are added)
- let mut witness = IncrementalWitness::from_tree(&tree);
- // Check this is the 6th coin we added
- assert_eq!(witness.position(), 5);
- assert_eq!(tree.root(), witness.root());
- // Add some more random coins in
- for i in 0..10 {
- let cmu = Coin::new(bls12_381::Scalar::random(&mut OsRng).to_repr());
- tree.append(cmu);
- witness.append(cmu);
- assert_eq!(tree.root(), witness.root());
- }
- // TODO: Some stupid glue code. Need to put this somewhere else.
- let merkle_path = witness.path().unwrap();
- let auth_path: Vec<(bls12_381::Scalar, bool)> = merkle_path
- .auth_path
- .iter()
- .map(|(node, b)| ((*node).into(), *b))
- .collect();
- // Step 3: wallet1 sends payment to wallet2
- // Wallet1 now wishes to send the coin to wallet2
- let secret2 = jubjub::Fr::random(&mut OsRng);
- // This is their public key
- let public2 = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret2;
- // Make a spend tx
- // Get the coin we're spending from the previous tx
- let coin = {
- let tx = tx::Transaction::decode(&tx_data[..]).unwrap();
- tx.outputs[0].revealed.coin
- };
- // Construct a new tx spending the coin
- // We need the decrypted note and our private key
- let builder = tx::TransactionBuilder {
- clear_inputs: vec![],
- inputs: vec![tx::TransactionBuilderInputInfo {
- coin,
- merkle_path: auth_path,
- merkle_root: tree,
- secret,
- note,
- }],
- // We can add more outputs to this list.
- // The only constraint is that sum(value in) == sum(value out)
- outputs: vec![tx::TransactionBuilderOutputInfo {
- value: 110,
- public: public2,
- }],
- };
- // Build the tx
- let mut tx_data = vec![];
- {
- let tx = builder.build(&mint_params, &spend_params);
- tx.encode(&mut tx_data).expect("encode tx");
- }
- // Verify it's valid
- {
- let tx = tx::Transaction::decode(&tx_data[..]).unwrap();
- //assert!(tx.verify(&mint_pvk, &spend_pvk));
- }
- }
|