use bridgetree::BridgeTree; use incrementalmerkletree::Hashable; use orchard::{ builder::{Builder, BundleType}, bundle::{Authorized, Flags}, circuit::{ProvingKey, VerifyingKey}, keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendAuthorizingKey, SpendingKey}, note::ExtractedNoteCommitment, note_encryption::OrchardDomain, tree::{MerkleHashOrchard, MerklePath}, value::NoteValue, Bundle, }; use rand::rngs::OsRng; use zcash_note_encryption::try_note_decryption; fn verify_bundle(bundle: &Bundle, vk: &VerifyingKey) { 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(()) ); } #[test] fn bundle_chain() { let mut rng = OsRng; let pk = ProvingKey::build(); let vk = VerifyingKey::build(); let sk = SpendingKey::from_bytes([0; 32]).unwrap(); let fvk = FullViewingKey::from(&sk); let recipient = fvk.address_at(0u32, Scope::External); // Create a shielding bundle. let shielding_bundle: Bundle<_, i64> = { // Use the empty tree. let anchor = MerkleHashOrchard::empty_root(32.into()).into(); let mut builder = Builder::new( BundleType::Transactional { flags: Flags::SPENDS_DISABLED, bundle_required: false, }, anchor, ); let note_value = NoteValue::from_raw(5000); assert_eq!( builder.add_output(None, recipient, note_value, None), Ok(()) ); let (unauthorized, bundle_meta) = builder.build(&mut rng).unwrap().unwrap(); assert_eq!( unauthorized .decrypt_output_with_key( bundle_meta .output_action_index(0) .expect("Output 0 can be found"), &fvk.to_ivk(Scope::External) ) .map(|(note, _, _)| note.value()), Some(note_value) ); let sighash = unauthorized.commitment().into(); let proven = unauthorized.create_proof(&pk, &mut rng).unwrap(); proven.apply_signatures(rng, sighash, &[]).unwrap() }; // Verify the shielding bundle. verify_bundle(&shielding_bundle, &vk); // Create a shielded bundle spending the previous output. 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(BundleType::DEFAULT, anchor); assert_eq!(builder.add_spend(fvk, note, merkle_path), Ok(())); assert_eq!( builder.add_output(None, recipient, NoteValue::from_raw(5000), None), Ok(()) ); let (unauthorized, _) = builder.build(&mut rng).unwrap().unwrap(); let sighash = unauthorized.commitment().into(); let proven = unauthorized.create_proof(&pk, &mut rng).unwrap(); proven .apply_signatures(rng, sighash, &[SpendAuthorizingKey::from(&sk)]) .unwrap() }; // Verify the shielded bundle. verify_bundle(&shielded_bundle, &vk); }