mirror of https://github.com/zcash/orchard.git
Merge pull request #285 from zcash/bundle-builder-test
Functional test for building and verifying bundles
This commit is contained in:
commit
4dc1ae059a
|
@ -2,6 +2,7 @@
|
|||
|
||||
pub mod commitments;
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::io;
|
||||
|
||||
use blake2b_simd::Hash as Blake2bHash;
|
||||
|
@ -298,15 +299,6 @@ impl<T: Authorization, V> Bundle<T, V> {
|
|||
&self.authorization
|
||||
}
|
||||
|
||||
/// Computes a commitment to the effects of this bundle, suitable for inclusion within
|
||||
/// a transaction ID.
|
||||
pub fn commitment<'a>(&'a self) -> BundleCommitment
|
||||
where
|
||||
i64: From<&'a V>,
|
||||
{
|
||||
BundleCommitment(hash_bundle_txid_data(self))
|
||||
}
|
||||
|
||||
/// Construct a new bundle by applying a transformation that might fail
|
||||
/// to the value balance.
|
||||
pub fn try_map_value_balance<V0, E, F: FnOnce(V) -> Result<V0, E>>(
|
||||
|
@ -405,7 +397,13 @@ impl<T: Authorization, V> Bundle<T, V> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Authorization, V: Copy + Into<ValueSum>> Bundle<T, V> {
|
||||
impl<T: Authorization, V: Copy + Into<i64>> Bundle<T, V> {
|
||||
/// Computes a commitment to the effects of this bundle, suitable for inclusion within
|
||||
/// a transaction ID.
|
||||
pub fn commitment(&self) -> BundleCommitment {
|
||||
BundleCommitment(hash_bundle_txid_data(self))
|
||||
}
|
||||
|
||||
/// Returns the transaction binding validating key for this bundle.
|
||||
///
|
||||
/// This can be used to validate the [`Authorized::binding_signature`] returned from
|
||||
|
@ -416,7 +414,10 @@ impl<T: Authorization, V: Copy + Into<ValueSum>> Bundle<T, V> {
|
|||
.iter()
|
||||
.map(|a| a.cv_net())
|
||||
.sum::<ValueCommitment>()
|
||||
- ValueCommitment::derive(self.value_balance.into(), ValueCommitTrapdoor::zero()))
|
||||
- ValueCommitment::derive(
|
||||
ValueSum::from_raw(self.value_balance.into()),
|
||||
ValueCommitTrapdoor::zero(),
|
||||
))
|
||||
.into_bvk()
|
||||
}
|
||||
}
|
||||
|
@ -500,6 +501,13 @@ impl<V: DynamicUsage> DynamicUsage for Bundle<Authorized, V> {
|
|||
#[derive(Debug)]
|
||||
pub struct BundleCommitment(pub Blake2bHash);
|
||||
|
||||
impl From<BundleCommitment> for [u8; 32] {
|
||||
fn from(commitment: BundleCommitment) -> Self {
|
||||
// The commitment uses BLAKE2b-256.
|
||||
commitment.0.as_bytes().try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// A commitment to the authorizing data within a bundle of actions.
|
||||
#[derive(Debug)]
|
||||
pub struct BundleAuthorizingCommitment(pub Blake2bHash);
|
||||
|
|
|
@ -28,10 +28,9 @@ fn hasher(personal: &[u8; 16]) -> State {
|
|||
/// personalized with ZCASH_ORCHARD_ACTIONS_HASH_PERSONALIZATION
|
||||
///
|
||||
/// [zip244]: https://zips.z.cash/zip-0244
|
||||
pub fn hash_bundle_txid_data<'a, A: Authorization, V>(bundle: &'a Bundle<A, V>) -> Blake2bHash
|
||||
where
|
||||
i64: From<&'a V>,
|
||||
{
|
||||
pub fn hash_bundle_txid_data<A: Authorization, V: Copy + Into<i64>>(
|
||||
bundle: &Bundle<A, V>,
|
||||
) -> Blake2bHash {
|
||||
let mut h = hasher(ZCASH_ORCHARD_HASH_PERSONALIZATION);
|
||||
let mut ch = hasher(ZCASH_ORCHARD_ACTIONS_COMPACT_HASH_PERSONALIZATION);
|
||||
let mut mh = hasher(ZCASH_ORCHARD_ACTIONS_MEMOS_HASH_PERSONALIZATION);
|
||||
|
@ -59,7 +58,7 @@ where
|
|||
h.write_all(&mh.finalize().as_bytes()).unwrap();
|
||||
h.write_all(&nh.finalize().as_bytes()).unwrap();
|
||||
h.write_all(&[bundle.flags().to_byte()]).unwrap();
|
||||
h.write_all(&<i64>::from(bundle.value_balance()).to_le_bytes())
|
||||
h.write_all(&(*bundle.value_balance()).into().to_le_bytes())
|
||||
.unwrap();
|
||||
h.write_all(&bundle.anchor().to_bytes()).unwrap();
|
||||
h.finalize()
|
||||
|
|
|
@ -147,6 +147,13 @@ impl VerificationKey<Binding> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: SigType> VerificationKey<T> {
|
||||
/// Verifies a purported `signature` over `msg` made by this verification key.
|
||||
pub fn verify(&self, msg: &[u8], signature: &Signature<T>) -> Result<(), reddsa::Error> {
|
||||
self.0.verify(msg, &signature.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// A RedPallas signature.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Signature<T: SigType>(reddsa::Signature<T>);
|
||||
|
|
12
src/tree.rs
12
src/tree.rs
|
@ -107,11 +107,19 @@ impl MerklePath {
|
|||
|
||||
/// Instantiates a new Merkle path given a leaf position and authentication path.
|
||||
pub(crate) fn new(position: u32, auth_path: [pallas::Base; MERKLE_DEPTH_ORCHARD]) -> Self {
|
||||
Self {
|
||||
Self::from_parts(
|
||||
position,
|
||||
auth_path: gen_const_array_with_default(MerkleHashOrchard::empty_leaf(), |i| {
|
||||
gen_const_array_with_default(MerkleHashOrchard::empty_leaf(), |i| {
|
||||
MerkleHashOrchard(auth_path[i])
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
/// Instantiates a new Merkle path given a leaf position and authentication path.
|
||||
pub fn from_parts(position: u32, auth_path: [MerkleHashOrchard; MERKLE_DEPTH_ORCHARD]) -> Self {
|
||||
Self {
|
||||
position,
|
||||
auth_path,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
use std::convert::TryInto;
|
||||
|
||||
use incrementalmerkletree::{bridgetree::BridgeTree, Frontier, Hashable, Tree};
|
||||
use orchard::{
|
||||
builder::Builder,
|
||||
bundle::{Authorized, Flags},
|
||||
circuit::{ProvingKey, VerifyingKey},
|
||||
keys::{FullViewingKey, IncomingViewingKey, 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<Authorized, i64>, 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);
|
||||
|
||||
// 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(Flags::from_parts(false, true), anchor);
|
||||
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).unwrap();
|
||||
proven.apply_signatures(&mut 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 = IncomingViewingKey::from(&fvk);
|
||||
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::<MerkleHashOrchard, 32>::new(0);
|
||||
tree.append(&leaf);
|
||||
tree.witness();
|
||||
let (position, auth_path) = tree.authentication_path(&leaf).unwrap();
|
||||
let merkle_path = MerklePath::from_parts(
|
||||
u64::from(position).try_into().unwrap(),
|
||||
auth_path[..].try_into().unwrap(),
|
||||
);
|
||||
let anchor = tree.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).unwrap();
|
||||
proven
|
||||
.apply_signatures(&mut rng, sighash, &[SpendAuthorizingKey::from(&sk)])
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
// Verify the shielded bundle.
|
||||
verify_bundle(&shielded_bundle, &vk);
|
||||
}
|
Loading…
Reference in New Issue