mirror of https://github.com/zcash/orchard.git
Add `SpendInfo::new` and `Circuit::from_action_context`
Author: Tomas Krnak <tomas@krnak.cz> Signed-off-by: Daira Hopwood <daira@jacaranda.org>
This commit is contained in:
parent
f206b3f5d4
commit
5fbbded49e
|
@ -13,6 +13,8 @@ and this project adheres to Rust's notion of
|
||||||
- `orchard::note`:
|
- `orchard::note`:
|
||||||
- `RandomSeed`
|
- `RandomSeed`
|
||||||
- `Note::{from_parts, rseed}`
|
- `Note::{from_parts, rseed}`
|
||||||
|
- `orchard::builder::SpendInfo::new`
|
||||||
|
- `orchard::circuit::Circuit::from_action_context`
|
||||||
|
|
||||||
## [0.2.0] - 2022-06-24
|
## [0.2.0] - 2022-06-24
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -4,7 +4,6 @@ use core::fmt;
|
||||||
use core::iter;
|
use core::iter;
|
||||||
|
|
||||||
use ff::Field;
|
use ff::Field;
|
||||||
use halo2_proofs::circuit::Value;
|
|
||||||
use nonempty::NonEmpty;
|
use nonempty::NonEmpty;
|
||||||
use pasta_curves::pallas;
|
use pasta_curves::pallas;
|
||||||
use rand::{prelude::SliceRandom, CryptoRng, RngCore};
|
use rand::{prelude::SliceRandom, CryptoRng, RngCore};
|
||||||
|
@ -58,15 +57,35 @@ impl From<value::OverflowError> for Error {
|
||||||
|
|
||||||
/// Information about a specific note to be spent in an [`Action`].
|
/// Information about a specific note to be spent in an [`Action`].
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct SpendInfo {
|
pub struct SpendInfo {
|
||||||
dummy_sk: Option<SpendingKey>,
|
pub(crate) dummy_sk: Option<SpendingKey>,
|
||||||
fvk: FullViewingKey,
|
pub(crate) fvk: FullViewingKey,
|
||||||
scope: Scope,
|
pub(crate) scope: Scope,
|
||||||
note: Note,
|
pub(crate) note: Note,
|
||||||
merkle_path: MerklePath,
|
pub(crate) merkle_path: MerklePath,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpendInfo {
|
impl SpendInfo {
|
||||||
|
/// This constructor is public to enable creation of custom builders.
|
||||||
|
/// If you are not creating a custom builder, use [`Builder::add_spend`] instead.
|
||||||
|
///
|
||||||
|
/// Creates a `SpendInfo` from note, full viewing key owning the note,
|
||||||
|
/// and merkle path witness of the note.
|
||||||
|
///
|
||||||
|
/// Returns `None` if the `fvk` does not own the `note`.
|
||||||
|
///
|
||||||
|
/// [`Builder::add_spend`]: Builder::add_spend
|
||||||
|
pub fn new(fvk: FullViewingKey, note: Note, merkle_path: MerklePath) -> Option<Self> {
|
||||||
|
let scope = fvk.scope_for_address(¬e.recipient())?;
|
||||||
|
Some(SpendInfo {
|
||||||
|
dummy_sk: None,
|
||||||
|
fvk,
|
||||||
|
scope,
|
||||||
|
note,
|
||||||
|
merkle_path,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes].
|
/// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes].
|
||||||
///
|
///
|
||||||
/// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes
|
/// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes
|
||||||
|
@ -144,10 +163,6 @@ impl ActionInfo {
|
||||||
let cv_net = ValueCommitment::derive(v_net, self.rcv.clone());
|
let cv_net = ValueCommitment::derive(v_net, self.rcv.clone());
|
||||||
|
|
||||||
let nf_old = self.spend.note.nullifier(&self.spend.fvk);
|
let nf_old = self.spend.note.nullifier(&self.spend.fvk);
|
||||||
let sender_address = self.spend.note.recipient();
|
|
||||||
let rho_old = self.spend.note.rho();
|
|
||||||
let psi_old = self.spend.note.rseed().psi(&rho_old);
|
|
||||||
let rcm_old = self.spend.note.rseed().rcm(&rho_old);
|
|
||||||
let ak: SpendValidatingKey = self.spend.fvk.clone().into();
|
let ak: SpendValidatingKey = self.spend.fvk.clone().into();
|
||||||
let alpha = pallas::Scalar::random(&mut rng);
|
let alpha = pallas::Scalar::random(&mut rng);
|
||||||
let rk = ak.randomize(&alpha);
|
let rk = ak.randomize(&alpha);
|
||||||
|
@ -182,33 +197,10 @@ impl ActionInfo {
|
||||||
cv_net,
|
cv_net,
|
||||||
SigningMetadata {
|
SigningMetadata {
|
||||||
dummy_ask: self.spend.dummy_sk.as_ref().map(SpendAuthorizingKey::from),
|
dummy_ask: self.spend.dummy_sk.as_ref().map(SpendAuthorizingKey::from),
|
||||||
parts: SigningParts {
|
parts: SigningParts { ak, alpha },
|
||||||
ak: ak.clone(),
|
|
||||||
alpha,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Circuit {
|
Circuit::from_action_context_unchecked(self.spend, note, alpha, self.rcv),
|
||||||
path: Value::known(self.spend.merkle_path.auth_path()),
|
|
||||||
pos: Value::known(self.spend.merkle_path.position()),
|
|
||||||
g_d_old: Value::known(sender_address.g_d()),
|
|
||||||
pk_d_old: Value::known(*sender_address.pk_d()),
|
|
||||||
v_old: Value::known(self.spend.note.value()),
|
|
||||||
rho_old: Value::known(rho_old),
|
|
||||||
psi_old: Value::known(psi_old),
|
|
||||||
rcm_old: Value::known(rcm_old),
|
|
||||||
cm_old: Value::known(self.spend.note.commitment()),
|
|
||||||
alpha: Value::known(alpha),
|
|
||||||
ak: Value::known(ak),
|
|
||||||
nk: Value::known(*self.spend.fvk.nk()),
|
|
||||||
rivk: Value::known(self.spend.fvk.rivk(self.spend.scope)),
|
|
||||||
g_d_new: Value::known(note.recipient().g_d()),
|
|
||||||
pk_d_new: Value::known(*note.recipient().pk_d()),
|
|
||||||
v_new: Value::known(note.value()),
|
|
||||||
psi_new: Value::known(note.rseed().psi(¬e.rho())),
|
|
||||||
rcm_new: Value::known(note.rseed().rcm(¬e.rho())),
|
|
||||||
rcv: Value::known(self.rcv),
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ use self::{
|
||||||
note_commit::{NoteCommitChip, NoteCommitConfig},
|
note_commit::{NoteCommitChip, NoteCommitConfig},
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
builder::SpendInfo,
|
||||||
constants::{
|
constants::{
|
||||||
OrchardCommitDomains, OrchardFixedBases, OrchardFixedBasesFull, OrchardHashDomains,
|
OrchardCommitDomains, OrchardFixedBases, OrchardFixedBasesFull, OrchardHashDomains,
|
||||||
MERKLE_DEPTH_ORCHARD,
|
MERKLE_DEPTH_ORCHARD,
|
||||||
|
@ -35,7 +36,7 @@ use crate::{
|
||||||
note::{
|
note::{
|
||||||
commitment::{NoteCommitTrapdoor, NoteCommitment},
|
commitment::{NoteCommitTrapdoor, NoteCommitment},
|
||||||
nullifier::Nullifier,
|
nullifier::Nullifier,
|
||||||
ExtractedNoteCommitment,
|
ExtractedNoteCommitment, Note,
|
||||||
},
|
},
|
||||||
primitives::redpallas::{SpendAuth, VerificationKey},
|
primitives::redpallas::{SpendAuth, VerificationKey},
|
||||||
spec::NonIdentityPallasPoint,
|
spec::NonIdentityPallasPoint,
|
||||||
|
@ -120,6 +121,71 @@ pub struct Circuit {
|
||||||
pub(crate) rcv: Value<ValueCommitTrapdoor>,
|
pub(crate) rcv: Value<ValueCommitTrapdoor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Circuit {
|
||||||
|
/// This constructor is public to enable creation of custom builders.
|
||||||
|
/// If you are not creating a custom builder, use [`Builder`] to compose
|
||||||
|
/// and authorize a transaction.
|
||||||
|
///
|
||||||
|
/// Constructs a `Circuit` from the following components:
|
||||||
|
/// - `spend`: [`SpendInfo`] of the note spent in scope of the action
|
||||||
|
/// - `output_note`: a note created in scope of the action
|
||||||
|
/// - `alpha`: a scalar used for randomization of the action spend validating key
|
||||||
|
/// - `rcv`: trapdoor for the action value commitment
|
||||||
|
///
|
||||||
|
/// Returns `None` if the `rho` of the `output_note` is not equal
|
||||||
|
/// to the nullifier of the spent note.
|
||||||
|
///
|
||||||
|
/// [`SpendInfo`]: crate::builder::SpendInfo
|
||||||
|
/// [`Builder`]: crate::builder::Builder
|
||||||
|
pub fn from_action_context(
|
||||||
|
spend: SpendInfo,
|
||||||
|
output_note: Note,
|
||||||
|
alpha: pallas::Scalar,
|
||||||
|
rcv: ValueCommitTrapdoor,
|
||||||
|
) -> Option<Circuit> {
|
||||||
|
(spend.note.nullifier(&spend.fvk) == output_note.rho())
|
||||||
|
.then(|| Self::from_action_context_unchecked(spend, output_note, alpha, rcv))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_action_context_unchecked(
|
||||||
|
spend: SpendInfo,
|
||||||
|
output_note: Note,
|
||||||
|
alpha: pallas::Scalar,
|
||||||
|
rcv: ValueCommitTrapdoor,
|
||||||
|
) -> Circuit {
|
||||||
|
let sender_address = spend.note.recipient();
|
||||||
|
let rho_old = spend.note.rho();
|
||||||
|
let psi_old = spend.note.rseed().psi(&rho_old);
|
||||||
|
let rcm_old = spend.note.rseed().rcm(&rho_old);
|
||||||
|
|
||||||
|
let rho_new = output_note.rho();
|
||||||
|
let psi_new = output_note.rseed().psi(&rho_new);
|
||||||
|
let rcm_new = output_note.rseed().rcm(&rho_new);
|
||||||
|
|
||||||
|
Circuit {
|
||||||
|
path: Value::known(spend.merkle_path.auth_path()),
|
||||||
|
pos: Value::known(spend.merkle_path.position()),
|
||||||
|
g_d_old: Value::known(sender_address.g_d()),
|
||||||
|
pk_d_old: Value::known(*sender_address.pk_d()),
|
||||||
|
v_old: Value::known(spend.note.value()),
|
||||||
|
rho_old: Value::known(rho_old),
|
||||||
|
psi_old: Value::known(psi_old),
|
||||||
|
rcm_old: Value::known(rcm_old),
|
||||||
|
cm_old: Value::known(spend.note.commitment()),
|
||||||
|
alpha: Value::known(alpha),
|
||||||
|
ak: Value::known(spend.fvk.clone().into()),
|
||||||
|
nk: Value::known(*spend.fvk.nk()),
|
||||||
|
rivk: Value::known(spend.fvk.rivk(spend.scope)),
|
||||||
|
g_d_new: Value::known(output_note.recipient().g_d()),
|
||||||
|
pk_d_new: Value::known(*output_note.recipient().pk_d()),
|
||||||
|
v_new: Value::known(output_note.value()),
|
||||||
|
psi_new: Value::known(psi_new),
|
||||||
|
rcm_new: Value::known(rcm_new),
|
||||||
|
rcv: Value::known(rcv),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl plonk::Circuit<pallas::Base> for Circuit {
|
impl plonk::Circuit<pallas::Base> for Circuit {
|
||||||
type Config = Config;
|
type Config = Config;
|
||||||
type FloorPlanner = floor_planner::V1;
|
type FloorPlanner = floor_planner::V1;
|
||||||
|
|
Loading…
Reference in New Issue