mirror of https://github.com/zcash/halo2.git
commit
e8f65a2158
|
@ -20,7 +20,7 @@ jobs:
|
|||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --verbose --all
|
||||
args: --verbose
|
||||
|
||||
bitrot:
|
||||
name: Bitrot check
|
||||
|
@ -37,7 +37,7 @@ jobs:
|
|||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --all --benches
|
||||
args: --benches
|
||||
|
||||
book:
|
||||
name: Book tests
|
||||
|
@ -156,4 +156,4 @@ jobs:
|
|||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
args: -- --check
|
||||
|
|
|
@ -26,6 +26,7 @@ ff = "0.9"
|
|||
fpe = "0.4"
|
||||
group = "0.9"
|
||||
rand = "0.8"
|
||||
rand_7 = { package = "rand", version = "0.7" }
|
||||
nonempty = "0.6"
|
||||
subtle = "2.3"
|
||||
|
||||
|
@ -39,7 +40,7 @@ rev = "b55a6960dfafd7f767e2820ddf1adaa499322f98"
|
|||
|
||||
[dependencies.reddsa]
|
||||
git = "https://github.com/str4d/redjubjub.git"
|
||||
rev = "62a26a02fda5165b68a4b4773aa18ea6eb749dbc"
|
||||
rev = "f1e76dbc9abf2b68cc609e874fe39f2a15b75b12"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
|
|
|
@ -0,0 +1,476 @@
|
|||
//! Logic for building Orchard components of transactions.
|
||||
|
||||
use std::iter;
|
||||
|
||||
use ff::Field;
|
||||
use nonempty::NonEmpty;
|
||||
use pasta_curves::pallas;
|
||||
use rand::RngCore;
|
||||
|
||||
use crate::{
|
||||
bundle::{Action, Authorization, Authorized, Bundle, Flags},
|
||||
circuit::{Circuit, Proof, ProvingKey},
|
||||
keys::{
|
||||
FullViewingKey, OutgoingViewingKey, SpendAuthorizingKey, SpendValidatingKey, SpendingKey,
|
||||
},
|
||||
primitives::redpallas::{self, Binding, SpendAuth},
|
||||
tree::{Anchor, MerklePath},
|
||||
value::{self, NoteValue, ValueCommitTrapdoor, ValueCommitment, ValueSum},
|
||||
Address, EncryptedNote, Note,
|
||||
};
|
||||
|
||||
const MIN_ACTIONS: usize = 2;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
MissingSignatures,
|
||||
Proof(halo2::plonk::Error),
|
||||
ValueSum(value::OverflowError),
|
||||
}
|
||||
|
||||
impl From<halo2::plonk::Error> for Error {
|
||||
fn from(e: halo2::plonk::Error) -> Self {
|
||||
Error::Proof(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<value::OverflowError> for Error {
|
||||
fn from(e: value::OverflowError) -> Self {
|
||||
Error::ValueSum(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about a specific note to be spent in an [`Action`].
|
||||
#[derive(Debug)]
|
||||
struct SpendInfo {
|
||||
dummy_sk: Option<SpendingKey>,
|
||||
fvk: FullViewingKey,
|
||||
note: Note,
|
||||
merkle_path: MerklePath,
|
||||
}
|
||||
|
||||
impl SpendInfo {
|
||||
/// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes].
|
||||
///
|
||||
/// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes
|
||||
fn dummy(rng: &mut impl RngCore) -> Self {
|
||||
let (sk, fvk, note) = Note::dummy(rng, None);
|
||||
let merkle_path = MerklePath::dummy(rng);
|
||||
|
||||
SpendInfo {
|
||||
dummy_sk: Some(sk),
|
||||
fvk,
|
||||
note,
|
||||
merkle_path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about a specific recipient to receive funds in an [`Action`].
|
||||
#[derive(Debug)]
|
||||
struct RecipientInfo {
|
||||
ovk: Option<OutgoingViewingKey>,
|
||||
recipient: Address,
|
||||
value: NoteValue,
|
||||
memo: Option<()>,
|
||||
}
|
||||
|
||||
impl RecipientInfo {
|
||||
/// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes].
|
||||
///
|
||||
/// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes
|
||||
fn dummy(rng: &mut impl RngCore) -> Self {
|
||||
let fvk: FullViewingKey = (&SpendingKey::random(rng)).into();
|
||||
let recipient = fvk.default_address();
|
||||
|
||||
RecipientInfo {
|
||||
ovk: None,
|
||||
recipient,
|
||||
value: NoteValue::zero(),
|
||||
memo: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about a specific [`Action`] we plan to build.
|
||||
#[derive(Debug)]
|
||||
struct ActionInfo {
|
||||
spend: SpendInfo,
|
||||
output: RecipientInfo,
|
||||
rcv: ValueCommitTrapdoor,
|
||||
}
|
||||
|
||||
impl ActionInfo {
|
||||
fn new(spend: SpendInfo, output: RecipientInfo, rng: impl RngCore) -> Self {
|
||||
ActionInfo {
|
||||
spend,
|
||||
output,
|
||||
rcv: ValueCommitTrapdoor::random(rng),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the value sum for this action.
|
||||
fn value_sum(&self) -> Result<ValueSum, value::OverflowError> {
|
||||
self.spend.note.value() - self.output.value
|
||||
}
|
||||
|
||||
/// Builds the action.
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend].
|
||||
///
|
||||
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
|
||||
fn build(self, mut rng: impl RngCore) -> (Action<SigningMetadata>, Circuit) {
|
||||
let v_net = self.value_sum().expect("already checked this");
|
||||
let cv_net = ValueCommitment::derive(v_net, self.rcv);
|
||||
|
||||
let nf_old = self.spend.note.nullifier(&self.spend.fvk);
|
||||
let ak: SpendValidatingKey = self.spend.fvk.into();
|
||||
let alpha = pallas::Scalar::random(&mut rng);
|
||||
let rk = ak.randomize(&alpha);
|
||||
|
||||
let note = Note::new(
|
||||
self.output.recipient,
|
||||
self.output.value,
|
||||
nf_old.clone(),
|
||||
rng,
|
||||
);
|
||||
let cm_new = note.commitment();
|
||||
|
||||
// TODO: Note encryption
|
||||
let encrypted_note = EncryptedNote;
|
||||
|
||||
(
|
||||
Action::from_parts(
|
||||
nf_old,
|
||||
rk,
|
||||
cm_new.into(),
|
||||
encrypted_note,
|
||||
cv_net,
|
||||
SigningMetadata {
|
||||
dummy_ask: self.spend.dummy_sk.as_ref().map(SpendAuthorizingKey::from),
|
||||
ak,
|
||||
},
|
||||
),
|
||||
Circuit {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder that constructs a [`Bundle`] from a set of notes to be spent, and recipients
|
||||
/// to receive funds.
|
||||
pub struct Builder {
|
||||
spends: Vec<SpendInfo>,
|
||||
recipients: Vec<RecipientInfo>,
|
||||
flags: Flags,
|
||||
anchor: Anchor,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
pub fn new(flags: Flags, anchor: Anchor) -> Self {
|
||||
Builder {
|
||||
spends: vec![],
|
||||
recipients: vec![],
|
||||
flags,
|
||||
anchor,
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a note to be spent in this transaction.
|
||||
///
|
||||
/// Returns an error if the given Merkle path does not have the required anchor for
|
||||
/// the given note.
|
||||
pub fn add_spend(
|
||||
&mut self,
|
||||
fvk: FullViewingKey,
|
||||
note: Note,
|
||||
merkle_path: MerklePath,
|
||||
) -> Result<(), &'static str> {
|
||||
if !self.flags.spends_enabled() {
|
||||
return Err("Spends are not enabled for this builder");
|
||||
}
|
||||
|
||||
// Consistency check: all anchors must be equal.
|
||||
let _cm = note.commitment();
|
||||
// TODO: Once we have tree logic.
|
||||
// let path_root: bls12_381::Scalar = merkle_path.root(cmu).into();
|
||||
// if path_root != anchor {
|
||||
// return Err(Error::AnchorMismatch);
|
||||
// }
|
||||
|
||||
self.spends.push(SpendInfo {
|
||||
dummy_sk: None,
|
||||
fvk,
|
||||
note,
|
||||
merkle_path,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds an address which will receive funds in this transaction.
|
||||
pub fn add_recipient(
|
||||
&mut self,
|
||||
ovk: Option<OutgoingViewingKey>,
|
||||
recipient: Address,
|
||||
value: NoteValue,
|
||||
memo: Option<()>,
|
||||
) -> Result<(), &'static str> {
|
||||
if !self.flags.outputs_enabled() {
|
||||
return Err("Outputs are not enabled for this builder");
|
||||
}
|
||||
|
||||
self.recipients.push(RecipientInfo {
|
||||
ovk,
|
||||
recipient,
|
||||
value,
|
||||
memo,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Builds a bundle containing the given spent notes and recipients.
|
||||
///
|
||||
/// This API assumes that none of the notes being spent are controlled by (threshold)
|
||||
/// multisignatures, and immediately constructs the bundle proof.
|
||||
fn build(
|
||||
mut self,
|
||||
mut rng: impl RngCore,
|
||||
pk: &ProvingKey,
|
||||
) -> Result<Bundle<Unauthorized>, Error> {
|
||||
// Pair up the spends and recipients, extending with dummy values as necessary.
|
||||
//
|
||||
// TODO: Do we want to shuffle the order like we do for Sapling? And if we do, do
|
||||
// we need the extra logic for mapping the user-provided input order to the
|
||||
// shuffled order?
|
||||
let pre_actions: Vec<_> = {
|
||||
let num_spends = self.spends.len();
|
||||
let num_recipients = self.recipients.len();
|
||||
let num_actions = [num_spends, num_recipients, MIN_ACTIONS]
|
||||
.iter()
|
||||
.max()
|
||||
.cloned()
|
||||
.unwrap();
|
||||
|
||||
self.spends.extend(
|
||||
iter::repeat_with(|| SpendInfo::dummy(&mut rng)).take(num_actions - num_spends),
|
||||
);
|
||||
self.recipients.extend(
|
||||
iter::repeat_with(|| RecipientInfo::dummy(&mut rng))
|
||||
.take(num_actions - num_recipients),
|
||||
);
|
||||
|
||||
self.spends
|
||||
.into_iter()
|
||||
.zip(self.recipients.into_iter())
|
||||
.map(|(spend, recipient)| ActionInfo::new(spend, recipient, &mut rng))
|
||||
.collect()
|
||||
};
|
||||
|
||||
// Move some things out of self that we will need.
|
||||
let flags = self.flags;
|
||||
let anchor = self.anchor;
|
||||
|
||||
// Determine the value balance for this bundle, ensuring it is valid.
|
||||
let value_balance: ValueSum = pre_actions
|
||||
.iter()
|
||||
.fold(Ok(ValueSum::zero()), |acc, action| {
|
||||
acc? + action.value_sum()?
|
||||
})?;
|
||||
|
||||
// Compute the transaction binding signing key.
|
||||
let bsk = pre_actions
|
||||
.iter()
|
||||
.map(|a| &a.rcv)
|
||||
.sum::<ValueCommitTrapdoor>()
|
||||
.into_bsk();
|
||||
|
||||
// Create the actions.
|
||||
let (actions, circuits): (Vec<_>, Vec<_>) =
|
||||
pre_actions.into_iter().map(|a| a.build(&mut rng)).unzip();
|
||||
|
||||
// Verify that bsk and bvk are consistent.
|
||||
let bvk = (actions.iter().map(|a| a.cv_net()).sum::<ValueCommitment>()
|
||||
- ValueCommitment::derive(value_balance, ValueCommitTrapdoor::zero()))
|
||||
.into_bvk();
|
||||
assert_eq!(redpallas::VerificationKey::from(&bsk), bvk);
|
||||
|
||||
// Create the proof.
|
||||
let instances: Vec<_> = actions
|
||||
.iter()
|
||||
.map(|a| a.to_instance(flags, anchor.clone()))
|
||||
.collect();
|
||||
let proof = Proof::create(pk, &circuits, &instances)?;
|
||||
|
||||
Ok(Bundle::from_parts(
|
||||
NonEmpty::from_vec(actions).unwrap(),
|
||||
flags,
|
||||
value_balance,
|
||||
anchor,
|
||||
Unauthorized { proof, bsk },
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Marker for an unauthorized bundle, with a proof but no signatures.
|
||||
#[derive(Debug)]
|
||||
pub struct Unauthorized {
|
||||
proof: Proof,
|
||||
bsk: redpallas::SigningKey<Binding>,
|
||||
}
|
||||
|
||||
impl Authorization for Unauthorized {
|
||||
type SpendAuth = SigningMetadata;
|
||||
}
|
||||
|
||||
/// Container for metadata needed to sign an [`Action`].
|
||||
#[derive(Debug)]
|
||||
pub struct SigningMetadata {
|
||||
/// If this action is spending a dummy note, this field holds that note's spend
|
||||
/// authorizing key.
|
||||
///
|
||||
/// These keys are used automatically in [`Bundle<Unauthorized>::prepare`] or
|
||||
/// [`Bundle<Unauthorized>::apply_signatures`] to sign dummy spends.
|
||||
dummy_ask: Option<SpendAuthorizingKey>,
|
||||
/// The spend validating key for this action. Used to match spend authorizing keys to
|
||||
/// actions they can create signatures for.
|
||||
ak: SpendValidatingKey,
|
||||
}
|
||||
|
||||
/// Marker for a partially-authorized bundle, in the process of being signed.
|
||||
#[derive(Debug)]
|
||||
pub struct PartiallyAuthorized {
|
||||
proof: Proof,
|
||||
binding_signature: redpallas::Signature<Binding>,
|
||||
sighash: [u8; 32],
|
||||
}
|
||||
|
||||
impl Authorization for PartiallyAuthorized {
|
||||
type SpendAuth = (Option<redpallas::Signature<SpendAuth>>, SpendValidatingKey);
|
||||
}
|
||||
|
||||
impl Bundle<Unauthorized> {
|
||||
/// Loads the sighash into this bundle, preparing it for signing.
|
||||
///
|
||||
/// This API ensures that all signatures are created over the same sighash.
|
||||
pub fn prepare<R: rand_7::RngCore + rand_7::CryptoRng>(
|
||||
self,
|
||||
mut rng: R,
|
||||
sighash: [u8; 32],
|
||||
) -> Bundle<PartiallyAuthorized> {
|
||||
self.authorize(
|
||||
&mut rng,
|
||||
|rng, _, SigningMetadata { dummy_ask, ak }| {
|
||||
(
|
||||
// We can create signatures for dummy spends immediately.
|
||||
dummy_ask.map(|ask| ask.sign(rng, &sighash)),
|
||||
ak,
|
||||
)
|
||||
},
|
||||
|rng, unauth| PartiallyAuthorized {
|
||||
proof: unauth.proof,
|
||||
binding_signature: unauth.bsk.sign(rng, &sighash),
|
||||
sighash,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Applies signatures to this bundle, in order to authorize it.
|
||||
pub fn apply_signatures<R: rand_7::RngCore + rand_7::CryptoRng>(
|
||||
self,
|
||||
mut rng: R,
|
||||
sighash: [u8; 32],
|
||||
signing_keys: &[SpendAuthorizingKey],
|
||||
) -> Result<Bundle<Authorized>, Error> {
|
||||
signing_keys
|
||||
.iter()
|
||||
.fold(self.prepare(&mut rng, sighash), |partial, ask| {
|
||||
partial.sign(&mut rng, ask)
|
||||
})
|
||||
.finalize()
|
||||
}
|
||||
}
|
||||
|
||||
impl Bundle<PartiallyAuthorized> {
|
||||
/// Signs this bundle with the given [`SpendAuthorizingKey`].
|
||||
///
|
||||
/// This will apply signatures for all notes controlled by this spending key.
|
||||
pub fn sign<R: rand_7::RngCore + rand_7::CryptoRng>(
|
||||
self,
|
||||
mut rng: R,
|
||||
ask: &SpendAuthorizingKey,
|
||||
) -> Self {
|
||||
let expected_ak = ask.into();
|
||||
self.authorize(
|
||||
&mut rng,
|
||||
|rng, partial, (sig, ak)| {
|
||||
(
|
||||
sig.or_else(|| {
|
||||
if ak == expected_ak {
|
||||
Some(ask.sign(rng, &partial.sighash))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
ak,
|
||||
)
|
||||
},
|
||||
|_, partial| partial,
|
||||
)
|
||||
}
|
||||
|
||||
/// Finalizes this bundle, enabling it to be included in a transaction.
|
||||
///
|
||||
/// Returns an error if any signatures are missing.
|
||||
pub fn finalize(self) -> Result<Bundle<Authorized>, Error> {
|
||||
self.try_authorize(
|
||||
&mut (),
|
||||
|_, _, (sig, _)| match sig {
|
||||
Some(sig) => Ok(sig),
|
||||
None => Err(Error::MissingSignatures),
|
||||
},
|
||||
|_, partial| {
|
||||
Ok(Authorized::from_parts(
|
||||
partial.proof,
|
||||
partial.binding_signature,
|
||||
))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use super::Builder;
|
||||
use crate::{
|
||||
bundle::Flags,
|
||||
circuit::ProvingKey,
|
||||
keys::{FullViewingKey, SpendingKey},
|
||||
tree::Anchor,
|
||||
value::{NoteValue, ValueSum},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn shielding_bundle() {
|
||||
let pk = ProvingKey::build();
|
||||
let mut rng = OsRng;
|
||||
|
||||
let sk = SpendingKey::random(&mut rng);
|
||||
let fvk = FullViewingKey::from(&sk);
|
||||
let recipient = fvk.default_address();
|
||||
|
||||
let mut builder = Builder::new(Flags::from_parts(true, true), Anchor);
|
||||
builder
|
||||
.add_recipient(None, recipient, NoteValue::from_raw(5000), None)
|
||||
.unwrap();
|
||||
let bundle = dbg!(builder
|
||||
.build(&mut rng, &pk)
|
||||
.unwrap()
|
||||
.prepare(rand_7::rngs::OsRng, [0; 32]))
|
||||
.finalize()
|
||||
.unwrap();
|
||||
assert_eq!(bundle.value_balance(), &ValueSum::from_raw(-5000))
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
use nonempty::NonEmpty;
|
||||
|
||||
use crate::{
|
||||
circuit::Proof,
|
||||
circuit::{Instance, Proof},
|
||||
note::{EncryptedNote, ExtractedNoteCommitment, Nullifier},
|
||||
primitives::redpallas::{self, Binding, SpendAuth},
|
||||
tree::Anchor,
|
||||
|
@ -107,6 +107,18 @@ impl<T> Action<T> {
|
|||
authorization: step(self.authorization)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn to_instance(&self, flags: Flags, anchor: Anchor) -> Instance {
|
||||
Instance {
|
||||
anchor,
|
||||
cv_net: self.cv_net.clone(),
|
||||
nf_old: self.nf.clone(),
|
||||
rk: self.rk.clone(),
|
||||
cmx: self.cmx.clone(),
|
||||
enable_spend: flags.spends_enabled,
|
||||
enable_output: flags.outputs_enabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Orchard-specific flags.
|
||||
|
|
19
src/keys.rs
19
src/keys.rs
|
@ -68,13 +68,22 @@ impl SpendingKey {
|
|||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct SpendAuthorizingKey(redpallas::SigningKey<SpendAuth>);
|
||||
pub struct SpendAuthorizingKey(redpallas::SigningKey<SpendAuth>);
|
||||
|
||||
impl SpendAuthorizingKey {
|
||||
/// Derives ask from sk. Internal use only, does not enforce all constraints.
|
||||
fn derive_inner(sk: &SpendingKey) -> pallas::Scalar {
|
||||
to_scalar(prf_expand(&sk.0, &[0x06]))
|
||||
}
|
||||
|
||||
/// Creates a spend authorization signature over the given message.
|
||||
pub fn sign<R: rand_7::RngCore + rand_7::CryptoRng>(
|
||||
&self,
|
||||
rng: R,
|
||||
msg: &[u8],
|
||||
) -> redpallas::Signature<SpendAuth> {
|
||||
self.0.sign(rng, msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&SpendingKey> for SpendAuthorizingKey {
|
||||
|
@ -101,7 +110,7 @@ impl From<&SpendingKey> for SpendAuthorizingKey {
|
|||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct SpendValidatingKey(redpallas::VerificationKey<SpendAuth>);
|
||||
pub struct SpendValidatingKey(redpallas::VerificationKey<SpendAuth>);
|
||||
|
||||
impl From<&SpendAuthorizingKey> for SpendValidatingKey {
|
||||
fn from(ask: &SpendAuthorizingKey) -> Self {
|
||||
|
@ -109,6 +118,12 @@ impl From<&SpendAuthorizingKey> for SpendValidatingKey {
|
|||
}
|
||||
}
|
||||
|
||||
impl PartialEq for SpendValidatingKey {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
<[u8; 32]>::from(&self.0).eq(&<[u8; 32]>::from(&other.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl SpendValidatingKey {
|
||||
/// Randomizes this spend validating key with the given `randomizer`.
|
||||
pub fn randomize(&self, randomizer: &pallas::Scalar) -> redpallas::VerificationKey<SpendAuth> {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#![deny(unsafe_code)]
|
||||
|
||||
mod address;
|
||||
mod builder;
|
||||
pub mod bundle;
|
||||
mod circuit;
|
||||
mod constants;
|
||||
|
|
23
src/note.rs
23
src/note.rs
|
@ -16,8 +16,8 @@ mod nullifier;
|
|||
pub use self::nullifier::Nullifier;
|
||||
|
||||
/// The ZIP 212 seed randomness for a note.
|
||||
#[derive(Debug)]
|
||||
struct RandomSeed([u8; 32]);
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct RandomSeed([u8; 32]);
|
||||
|
||||
impl RandomSeed {
|
||||
pub(crate) fn random(rng: &mut impl RngCore) -> Self {
|
||||
|
@ -60,6 +60,25 @@ pub struct Note {
|
|||
}
|
||||
|
||||
impl Note {
|
||||
/// Generates a new note.
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend].
|
||||
///
|
||||
/// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend
|
||||
pub(crate) fn new(
|
||||
recipient: Address,
|
||||
value: NoteValue,
|
||||
rho: Nullifier,
|
||||
mut rng: impl RngCore,
|
||||
) -> Self {
|
||||
Note {
|
||||
recipient,
|
||||
value,
|
||||
rho,
|
||||
rseed: RandomSeed::random(&mut rng),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a dummy spent note.
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes].
|
||||
|
|
|
@ -56,7 +56,7 @@ impl NoteCommitment {
|
|||
}
|
||||
|
||||
/// The x-coordinate of the commitment to a note.
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ExtractedNoteCommitment(pub(super) pallas::Base);
|
||||
|
||||
impl From<NoteCommitment> for ExtractedNoteCommitment {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
use pasta_curves::pallas;
|
||||
use rand_7::{CryptoRng, RngCore};
|
||||
|
||||
/// A RedPallas signature type.
|
||||
pub trait SigType: reddsa::SigType + private::Sealed {}
|
||||
|
@ -39,8 +40,15 @@ impl<T: SigType> TryFrom<[u8; 32]> for SigningKey<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: SigType> SigningKey<T> {
|
||||
/// Creates a signature of type `T` on `msg` using this `SigningKey`.
|
||||
pub fn sign<R: RngCore + CryptoRng>(&self, rng: R, msg: &[u8]) -> Signature<T> {
|
||||
Signature(self.0.sign(rng, msg))
|
||||
}
|
||||
}
|
||||
|
||||
/// A RedPallas verification key.
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VerificationKey<T: SigType>(reddsa::VerificationKey<T>);
|
||||
|
||||
impl<T: SigType> From<VerificationKey<T>> for [u8; 32] {
|
||||
|
|
15
src/tree.rs
15
src/tree.rs
|
@ -1,3 +1,16 @@
|
|||
use rand::RngCore;
|
||||
|
||||
/// The root of an Orchard commitment tree.
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Anchor;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MerklePath;
|
||||
|
||||
impl MerklePath {
|
||||
/// Generates a dummy Merkle path for use in dummy spent notes.
|
||||
pub(crate) fn dummy(_rng: &mut impl RngCore) -> Self {
|
||||
// TODO
|
||||
MerklePath
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,10 +76,15 @@ impl Sub for NoteValue {
|
|||
}
|
||||
|
||||
/// A sum of Orchard note values.
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub struct ValueSum(i64);
|
||||
|
||||
impl ValueSum {
|
||||
pub(crate) fn zero() -> Self {
|
||||
// Default for i64 is zero.
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Creates a value sum from its raw numeric value.
|
||||
///
|
||||
/// This only enforces that the value is a signed 63-bit integer. Callers should
|
||||
|
|
Loading…
Reference in New Issue