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:
Paul 2022-09-29 10:32:16 +03:00 committed by Paul
parent 1420f84932
commit 0b2988acc7
8 changed files with 1207 additions and 11 deletions

View File

@ -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},

View File

@ -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(&note.recipient().to_raw_address_bytes());
h.update(&note.value().to_bytes());
h.update(&note.note_type().to_bytes());
h.update(&note.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()
}

1094
src/issuance.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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)]

View File

@ -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;

View File

@ -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,
}
}
}
}

View File

@ -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)
}
}
}

View File

@ -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> {