mirror of https://github.com/zcash/orchard.git
Issuance (#12)
- added IssueBundle and IssueAction - added a builder for IssueBundle - added verify_issue_bundle() for consensus verification. - unit tests.
This commit is contained in:
parent
1420f84932
commit
0b2988acc7
|
@ -881,7 +881,6 @@ mod tests {
|
|||
use rand::rngs::OsRng;
|
||||
|
||||
use super::Builder;
|
||||
// use crate::keys::{IssuerAuthorizingKey, IssuerValidatingKey};
|
||||
use crate::note::NoteType;
|
||||
use crate::{
|
||||
bundle::{Authorized, Bundle, Flags},
|
||||
|
|
|
@ -3,12 +3,15 @@
|
|||
use blake2b_simd::{Hash as Blake2bHash, Params, State};
|
||||
|
||||
use crate::bundle::{Authorization, Authorized, Bundle};
|
||||
use crate::issuance::{IssueAuth, IssueBundle, Signed};
|
||||
|
||||
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";
|
||||
const ZCASH_ORCHARD_ZSA_ISSUE_PERSONALIZATION: &[u8; 16] = b"ZTxIdOrcZSAIssue";
|
||||
const ZCASH_ORCHARD_ZSA_ISSUE_SIG_PERSONALIZATION: &[u8; 16] = b"ZTxAuthZSAOrHash";
|
||||
|
||||
fn hasher(personal: &[u8; 16]) -> State {
|
||||
Params::new().hash_length(32).personal(personal).to_state()
|
||||
|
@ -90,3 +93,30 @@ pub(crate) fn hash_bundle_auth_data<V>(bundle: &Bundle<Authorized, V>) -> Blake2
|
|||
pub fn hash_bundle_auth_empty() -> Blake2bHash {
|
||||
hasher(ZCASH_ORCHARD_SIGS_HASH_PERSONALIZATION).finalize()
|
||||
}
|
||||
|
||||
/// Construct the commitment for the issue bundle
|
||||
pub(crate) fn hash_issue_bundle_txid_data<A: IssueAuth>(bundle: &IssueBundle<A>) -> Blake2bHash {
|
||||
let mut h = hasher(ZCASH_ORCHARD_ZSA_ISSUE_PERSONALIZATION);
|
||||
|
||||
for action in bundle.actions().iter() {
|
||||
for note in action.notes().iter() {
|
||||
h.update(¬e.recipient().to_raw_address_bytes());
|
||||
h.update(¬e.value().to_bytes());
|
||||
h.update(¬e.note_type().to_bytes());
|
||||
h.update(¬e.rho().to_bytes());
|
||||
h.update(note.rseed().as_bytes());
|
||||
}
|
||||
h.update(action.asset_desc().as_bytes());
|
||||
h.update(&[u8::from(action.is_finalized())]);
|
||||
}
|
||||
h.update(&bundle.ik().to_bytes());
|
||||
h.finalize()
|
||||
}
|
||||
|
||||
/// Construct the commitment to the authorizing data of an
|
||||
/// authorized issue bundle
|
||||
pub(crate) fn hash_issue_bundle_auth_data(bundle: &IssueBundle<Signed>) -> Blake2bHash {
|
||||
let mut h = hasher(ZCASH_ORCHARD_ZSA_ISSUE_SIG_PERSONALIZATION);
|
||||
h.update(&<[u8; 64]>::from(bundle.authorization().signature()));
|
||||
h.finalize()
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
42
src/keys.rs
42
src/keys.rs
|
@ -12,7 +12,7 @@ use group::{
|
|||
Curve, GroupEncoding,
|
||||
};
|
||||
use pasta_curves::pallas;
|
||||
use rand::RngCore;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
use zcash_note_encryption::EphemeralKeyBytes;
|
||||
|
||||
|
@ -209,6 +209,15 @@ impl IssuerAuthorizingKey {
|
|||
fn derive_inner(sk: &SpendingKey) -> pallas::Scalar {
|
||||
to_scalar(PrfExpand::ZsaIsk.expand(&sk.0))
|
||||
}
|
||||
|
||||
/// Sign the provided message using the `IssuerAuthorizingKey`.
|
||||
pub fn sign(
|
||||
&self,
|
||||
rng: &mut (impl RngCore + CryptoRng),
|
||||
msg: &[u8],
|
||||
) -> redpallas::Signature<SpendAuth> {
|
||||
self.0.sign(rng, msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&SpendingKey> for IssuerAuthorizingKey {
|
||||
|
@ -270,6 +279,15 @@ impl IssuerValidatingKey {
|
|||
.and_then(check_structural_validity)
|
||||
.map(IssuerValidatingKey)
|
||||
}
|
||||
|
||||
/// Verifies a purported `signature` over `msg` made by this verification key.
|
||||
pub fn verify(
|
||||
&self,
|
||||
msg: &[u8],
|
||||
signature: &redpallas::Signature<SpendAuth>,
|
||||
) -> Result<(), reddsa::Error> {
|
||||
self.0.verify(msg, signature)
|
||||
}
|
||||
}
|
||||
|
||||
/// A function to check structural validity of the validating keys for authorizing transfers and
|
||||
|
@ -1024,9 +1042,12 @@ impl SharedSecret {
|
|||
#[cfg(any(test, feature = "test-dependencies"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
|
||||
pub mod testing {
|
||||
use super::{
|
||||
DiversifierIndex, DiversifierKey, EphemeralSecretKey, IssuerAuthorizingKey,
|
||||
IssuerValidatingKey, SpendingKey,
|
||||
};
|
||||
use proptest::prelude::*;
|
||||
|
||||
use super::{DiversifierIndex, DiversifierKey, EphemeralSecretKey, SpendingKey};
|
||||
use rand::{rngs::StdRng, SeedableRng};
|
||||
|
||||
prop_compose! {
|
||||
/// Generate a uniformly distributed Orchard spending key.
|
||||
|
@ -1073,6 +1094,21 @@ pub mod testing {
|
|||
DiversifierIndex::from(d_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
/// Generate a uniformly distributed RedDSA issuer authorizing key.
|
||||
pub fn arb_issuer_authorizing_key()(rng_seed in prop::array::uniform32(prop::num::u8::ANY)) -> IssuerAuthorizingKey {
|
||||
let mut rng = StdRng::from_seed(rng_seed);
|
||||
IssuerAuthorizingKey::from(&SpendingKey::random(&mut rng))
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
/// Generate a uniformly distributed RedDSA issuer validating key.
|
||||
pub fn arb_issuer_validating_key()(isk in arb_issuer_authorizing_key()) -> IssuerValidatingKey {
|
||||
IssuerValidatingKey::from(&isk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -22,6 +22,7 @@ pub mod builder;
|
|||
pub mod bundle;
|
||||
pub mod circuit;
|
||||
mod constants;
|
||||
pub mod issuance;
|
||||
pub mod keys;
|
||||
pub mod note;
|
||||
pub mod note_encryption;
|
||||
|
|
22
src/note.rs
22
src/note.rs
|
@ -173,6 +173,7 @@ impl Note {
|
|||
/// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes].
|
||||
///
|
||||
/// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes
|
||||
/// TODO zsa: remove note_type
|
||||
pub(crate) fn dummy(
|
||||
rng: &mut impl RngCore,
|
||||
rho: Option<Nullifier>,
|
||||
|
@ -296,6 +297,8 @@ pub mod testing {
|
|||
use proptest::prelude::*;
|
||||
|
||||
use crate::note::note_type::testing::arb_note_type;
|
||||
use crate::note::note_type::testing::zsa_note_type;
|
||||
use crate::value::testing::arb_note_value;
|
||||
use crate::{
|
||||
address::testing::arb_address, note::nullifier::testing::arb_nullifier, value::NoteValue,
|
||||
};
|
||||
|
@ -326,4 +329,23 @@ pub mod testing {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
/// Generate an arbitrary ZSA note
|
||||
pub fn arb_zsa_note()(
|
||||
recipient in arb_address(),
|
||||
value in arb_note_value(),
|
||||
rho in arb_nullifier(),
|
||||
rseed in arb_rseed(),
|
||||
note_type in zsa_note_type(),
|
||||
) -> Note {
|
||||
Note {
|
||||
recipient,
|
||||
value,
|
||||
note_type,
|
||||
rho,
|
||||
rseed,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,12 +38,12 @@ impl NoteType {
|
|||
///
|
||||
/// [notetypes]: https://zips.z.cash/protocol/nu5.pdf#notetypes
|
||||
#[allow(non_snake_case)]
|
||||
pub fn derive(ik: &IssuerValidatingKey, assetDesc: Vec<u8>) -> Self {
|
||||
assert!(assetDesc.len() < MAX_ASSET_DESCRIPTION_SIZE);
|
||||
pub fn derive(ik: &IssuerValidatingKey, asset_desc: &str) -> Self {
|
||||
assert!(!asset_desc.is_empty() && asset_desc.len() <= MAX_ASSET_DESCRIPTION_SIZE);
|
||||
|
||||
let mut s = vec![];
|
||||
s.extend(ik.to_bytes());
|
||||
s.extend(assetDesc);
|
||||
s.extend(asset_desc.as_bytes());
|
||||
|
||||
NoteType(assetID_hasher(s))
|
||||
}
|
||||
|
@ -92,15 +92,13 @@ pub mod testing {
|
|||
pub fn arb_note_type()(
|
||||
is_native in prop::bool::ANY,
|
||||
sk in arb_spending_key(),
|
||||
bytes32a in prop::array::uniform32(prop::num::u8::ANY),
|
||||
bytes32b in prop::array::uniform32(prop::num::u8::ANY),
|
||||
str in "[A-Za-z]{255}",
|
||||
) -> NoteType {
|
||||
if is_native {
|
||||
NoteType::native()
|
||||
} else {
|
||||
let bytes64 = [bytes32a, bytes32b].concat();
|
||||
let isk = IssuerAuthorizingKey::from(&sk);
|
||||
NoteType::derive(&IssuerValidatingKey::from(&isk), bytes64)
|
||||
NoteType::derive(&IssuerValidatingKey::from(&isk), &str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -112,4 +110,15 @@ pub mod testing {
|
|||
NoteType::native()
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
/// Generate the ZSA note type
|
||||
pub fn zsa_note_type()(
|
||||
sk in arb_spending_key(),
|
||||
str in "[A-Za-z]{255}"
|
||||
) -> NoteType {
|
||||
let isk = IssuerAuthorizingKey::from(&sk);
|
||||
NoteType::derive(&IssuerValidatingKey::from(&isk), &str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,6 +116,11 @@ impl NoteValue {
|
|||
pub(crate) fn to_le_bits(self) -> BitArray<[u8; 8], Lsb0> {
|
||||
BitArray::<_, Lsb0>::new(self.0.to_le_bytes())
|
||||
}
|
||||
|
||||
/// The minimum, greater than zero, note value that can not be split further.
|
||||
pub fn unsplittable() -> Self {
|
||||
NoteValue(1u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&NoteValue> for Assigned<pallas::Base> {
|
||||
|
|
Loading…
Reference in New Issue