mirror of https://github.com/zcash/orchard.git
Merge pull request #140 from nuttycom/bundle_zip244_commitments
Implement ZIP-244 txid and authorizing commitments.
This commit is contained in:
commit
8454f86d42
|
@ -1,10 +1,15 @@
|
||||||
//! Structs related to bundles of Orchard actions.
|
//! Structs related to bundles of Orchard actions.
|
||||||
|
|
||||||
|
pub mod commitments;
|
||||||
|
|
||||||
|
use std::io;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
use blake2b_simd::Hash as Blake2bHash;
|
||||||
use nonempty::NonEmpty;
|
use nonempty::NonEmpty;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
bundle::commitments::{hash_bundle_auth_data, hash_bundle_txid_data},
|
||||||
circuit::{Instance, Proof, VerifyingKey},
|
circuit::{Instance, Proof, VerifyingKey},
|
||||||
note::{ExtractedNoteCommitment, Nullifier, TransmittedNoteCiphertext},
|
note::{ExtractedNoteCommitment, Nullifier, TransmittedNoteCiphertext},
|
||||||
primitives::redpallas::{self, Binding, SpendAuth},
|
primitives::redpallas::{self, Binding, SpendAuth},
|
||||||
|
@ -142,6 +147,10 @@ pub struct Flags {
|
||||||
outputs_enabled: bool,
|
outputs_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FLAG_SPENDS_ENABLED: u8 = 0b0000_0001;
|
||||||
|
const FLAG_OUTPUTS_ENABLED: u8 = 0b0000_0010;
|
||||||
|
const FLAGS_EXPECTED_UNSET: u8 = !(FLAG_SPENDS_ENABLED | FLAG_OUTPUTS_ENABLED);
|
||||||
|
|
||||||
impl Flags {
|
impl Flags {
|
||||||
/// Construct a set of flags from its constituent parts
|
/// Construct a set of flags from its constituent parts
|
||||||
pub fn from_parts(spends_enabled: bool, outputs_enabled: bool) -> Self {
|
pub fn from_parts(spends_enabled: bool, outputs_enabled: bool) -> Self {
|
||||||
|
@ -168,6 +177,39 @@ impl Flags {
|
||||||
pub fn outputs_enabled(&self) -> bool {
|
pub fn outputs_enabled(&self) -> bool {
|
||||||
self.outputs_enabled
|
self.outputs_enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Serialize flags to a byte as defined in [Zcash Protocol Spec § 7.1: Transaction
|
||||||
|
/// Encoding And Consensus][txencoding].
|
||||||
|
///
|
||||||
|
/// [txencoding]: https://zips.z.cash/protocol/protocol.pdf#txnencoding
|
||||||
|
pub fn to_byte(&self) -> u8 {
|
||||||
|
let mut value = 0u8;
|
||||||
|
if self.spends_enabled {
|
||||||
|
value |= FLAG_SPENDS_ENABLED;
|
||||||
|
}
|
||||||
|
if self.outputs_enabled {
|
||||||
|
value |= FLAG_OUTPUTS_ENABLED;
|
||||||
|
}
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse from a single byte as defined in [Zcash Protocol Spec § 7.1: Transaction
|
||||||
|
/// Encoding And Consensus][txencoding].
|
||||||
|
///
|
||||||
|
/// [txencoding]: https://zips.z.cash/protocol/protocol.pdf#txnencoding
|
||||||
|
pub fn from_byte(value: u8) -> io::Result<Self> {
|
||||||
|
if value & FLAGS_EXPECTED_UNSET == 0 {
|
||||||
|
Ok(Self::from_parts(
|
||||||
|
value & FLAG_SPENDS_ENABLED != 0,
|
||||||
|
value & FLAG_OUTPUTS_ENABLED != 0,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"Unexpected bits set in Orchard flags value.",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defines the authorization type of an Orchard bundle.
|
/// Defines the authorization type of an Orchard bundle.
|
||||||
|
@ -242,8 +284,11 @@ impl<T: Authorization, V> Bundle<T, V> {
|
||||||
|
|
||||||
/// Computes a commitment to the effects of this bundle, suitable for inclusion within
|
/// Computes a commitment to the effects of this bundle, suitable for inclusion within
|
||||||
/// a transaction ID.
|
/// a transaction ID.
|
||||||
pub fn commitment(&self) -> BundleCommitment {
|
pub fn commitment<'a>(&'a self) -> BundleCommitment
|
||||||
todo!()
|
where
|
||||||
|
i64: From<&'a V>,
|
||||||
|
{
|
||||||
|
BundleCommitment(hash_bundle_txid_data(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new bundle by applying a transformation that might fail
|
/// Construct a new bundle by applying a transformation that might fail
|
||||||
|
@ -370,7 +415,7 @@ impl<V> Bundle<Authorized, V> {
|
||||||
///
|
///
|
||||||
/// This together with `Bundle::commitment` bind the entire bundle.
|
/// This together with `Bundle::commitment` bind the entire bundle.
|
||||||
pub fn authorizing_commitment(&self) -> BundleAuthorizingCommitment {
|
pub fn authorizing_commitment(&self) -> BundleAuthorizingCommitment {
|
||||||
todo!()
|
BundleAuthorizingCommitment(hash_bundle_auth_data(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verifies the proof for this bundle.
|
/// Verifies the proof for this bundle.
|
||||||
|
@ -386,11 +431,11 @@ impl<V> Bundle<Authorized, V> {
|
||||||
/// This commitment is non-malleable, in the sense that a bundle's commitment will only
|
/// This commitment is non-malleable, in the sense that a bundle's commitment will only
|
||||||
/// change if the effects of the bundle are altered.
|
/// change if the effects of the bundle are altered.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BundleCommitment;
|
pub struct BundleCommitment(pub Blake2bHash);
|
||||||
|
|
||||||
/// A commitment to the authorizing data within a bundle of actions.
|
/// A commitment to the authorizing data within a bundle of actions.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BundleAuthorizingCommitment;
|
pub struct BundleAuthorizingCommitment(pub Blake2bHash);
|
||||||
|
|
||||||
/// Generators for property testing.
|
/// Generators for property testing.
|
||||||
#[cfg(any(test, feature = "test-dependencies"))]
|
#[cfg(any(test, feature = "test-dependencies"))]
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
//! Utility functions for computing bundle commitments
|
||||||
|
|
||||||
|
use blake2b_simd::{Hash as Blake2bHash, Params, State};
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use crate::bundle::{Authorization, Authorized, Bundle};
|
||||||
|
|
||||||
|
const ZCASH_ORCHARD_HASH_PERSONALIZATION: &[u8; 16] = b"ZTxIdOrchardHash";
|
||||||
|
const ZCASH_ORCHARD_ACTIONS_COMPACT_HASH_PERSONALIZATION: &[u8; 16] = b"ZTxIdOrcActCHash";
|
||||||
|
const ZCASH_ORCHARD_ACTIONS_MEMOS_HASH_PERSONALIZATION: &[u8; 16] = b"ZTxIdOrcActMHash";
|
||||||
|
const ZCASH_ORCHARD_ACTIONS_NONCOMPACT_HASH_PERSONALIZATION: &[u8; 16] = b"ZTxIdOrcActNHash";
|
||||||
|
const ZCASH_ORCHARD_SIGS_HASH_PERSONALIZATION: &[u8; 16] = b"ZTxAuthOrchaHash";
|
||||||
|
|
||||||
|
fn hasher(personal: &[u8; 16]) -> State {
|
||||||
|
Params::new().hash_length(32).personal(personal).to_state()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write disjoint parts of each Orchard shielded action as 3 separate hashes:
|
||||||
|
/// * \[(nullifier, cmx, ephemeral_key, enc_ciphertext\[..52\])*\] personalized
|
||||||
|
/// with ZCASH_ORCHARD_ACTIONS_COMPACT_HASH_PERSONALIZATION
|
||||||
|
/// * \[enc_ciphertext\[52..564\]*\] (memo ciphertexts) personalized
|
||||||
|
/// with ZCASH_ORCHARD_ACTIONS_MEMOS_HASH_PERSONALIZATION
|
||||||
|
/// * \[(cv, rk, enc_ciphertext\[564..\], out_ciphertext)*\] personalized
|
||||||
|
/// with ZCASH_ORCHARD_ACTIONS_NONCOMPACT_HASH_PERSONALIZATION
|
||||||
|
/// as defined in [ZIP-244: Transaction Identifier Non-Malleability][zip244]
|
||||||
|
///
|
||||||
|
/// Then, hash these together along with (flags, value_balance_orchard, anchor_orchard),
|
||||||
|
/// 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>,
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
let mut nh = hasher(ZCASH_ORCHARD_ACTIONS_NONCOMPACT_HASH_PERSONALIZATION);
|
||||||
|
|
||||||
|
for action in bundle.actions().iter() {
|
||||||
|
ch.write_all(&action.nullifier().to_bytes()).unwrap();
|
||||||
|
ch.write_all(&action.cmx().to_bytes()).unwrap();
|
||||||
|
ch.write_all(&action.encrypted_note().epk_bytes).unwrap();
|
||||||
|
ch.write_all(&action.encrypted_note().enc_ciphertext[..52])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
mh.write_all(&action.encrypted_note().enc_ciphertext[52..564])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
nh.write_all(&action.cv_net().to_bytes()).unwrap();
|
||||||
|
nh.write_all(&<[u8; 32]>::from(action.rk())).unwrap();
|
||||||
|
nh.write_all(&action.encrypted_note().enc_ciphertext[564..])
|
||||||
|
.unwrap();
|
||||||
|
nh.write_all(&action.encrypted_note().out_ciphertext)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
h.write_all(&ch.finalize().as_bytes()).unwrap();
|
||||||
|
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())
|
||||||
|
.unwrap();
|
||||||
|
h.write_all(&bundle.anchor().to_bytes()).unwrap();
|
||||||
|
h.finalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct the commitment for the absent bundle as defined in
|
||||||
|
/// [ZIP-244: Transaction Identifier Non-Malleability][zip244]
|
||||||
|
///
|
||||||
|
/// [zip244]: https://zips.z.cash/zip-0244
|
||||||
|
pub fn hash_bundle_txid_empty() -> Blake2bHash {
|
||||||
|
hasher(ZCASH_ORCHARD_HASH_PERSONALIZATION).finalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct the commitment to the authorizing data of an
|
||||||
|
/// authorized bundle as defined in [ZIP-244: Transaction
|
||||||
|
/// Identifier Non-Malleability][zip244]
|
||||||
|
///
|
||||||
|
/// [zip244]: https://zips.z.cash/zip-0244
|
||||||
|
pub fn hash_bundle_auth_data<V>(bundle: &Bundle<Authorized, V>) -> Blake2bHash {
|
||||||
|
let mut h = hasher(ZCASH_ORCHARD_SIGS_HASH_PERSONALIZATION);
|
||||||
|
h.write_all(bundle.authorization().proof().as_ref())
|
||||||
|
.unwrap();
|
||||||
|
for action in bundle.actions().iter() {
|
||||||
|
h.write_all(&<[u8; 64]>::from(action.authorization()))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
h.write_all(&<[u8; 64]>::from(
|
||||||
|
bundle.authorization().binding_signature(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
h.finalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct the commitment for an absent bundle as defined in
|
||||||
|
/// [ZIP-244: Transaction Identifier Non-Malleability][zip244]
|
||||||
|
///
|
||||||
|
/// [zip244]: https://zips.z.cash/zip-0244
|
||||||
|
pub fn hash_bundle_auth_empty() -> Blake2bHash {
|
||||||
|
hasher(ZCASH_ORCHARD_SIGS_HASH_PERSONALIZATION).finalize()
|
||||||
|
}
|
Loading…
Reference in New Issue