-
Notifications
You must be signed in to change notification settings - Fork 38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement PCZT support #440
base: main
Are you sure you want to change the base?
Conversation
/// | ||
/// - This is chosen by the Constructor. | ||
/// - This is required by the IO Finalizer, and is cleared by it once used. | ||
/// - Signers MUST reject PCZTs that contain `dummy_sk` values. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- I both named this
dummy_sk
and added a "MUST reject" here because I do not want PCZTs to get serialized with non-dummy spending keys. - I stored the dummy note's
sk
instead of the smaller-scopedask
, because in Orchard we impose validity requirements at parse time ofsk
(specifically to ensure that a validivk
is produced), and we already had APIs for handling that parsing here. If desired this can be scoped down todummy_ask
, but given that (again) this field is only for dummy notes, I thinksk
should be fine here.
pub fn parse( | ||
actions: Vec<Action>, | ||
flags: u8, | ||
value_sum: (u64, bool), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided on this serialization format for ValueSum
(rather than e.g. i128
) because it has no edge cases; all values are valid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK e7a2945 with minor questions / naming nits. Overall this looks excellent.
/// This is initialized by the Creator, and updated by the Constructor as spends or | ||
/// outputs are added to the PCZT. It enables per-spend and per-output values to be | ||
/// redacted from the PCZT after they are no longer necessary. | ||
pub(crate) value_sum: ValueSum, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't the fact that a mut getter is exposed for actions
make it so that this can get out of sync with the actions that have been added? Would it be safer to have explicit add_action
and redact_action
methods?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The mut getter here is actually for the Fun Third Option: mutating existing actions so that signatures can be applied. In the PCZT format PR I implemented all the roles as consuming / transforming, but the orchard
APIs are what get used under the hood to do the transformations, and for the scale of PCZTs a mapping-based API with the necessary granularity would be ridiculously verbose and onerous to work with.
If we don't want to expose the dual capabilities of "modify existing Actions" and "add / remove Actions" here, then I can replace this with a method that returns &mut [Action]
instead (hiding the Vec
nature and only exposing the modification capability). That would be sufficient for now as the "add Actions" capability is currently provided by Builder
(I have no plans to enable piece-wise PCZT building in this first pass).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hiding the Vec
nature (for mutation) sounds like the right approach to me. Right now it presents a footgun, which we can eliminate.
/// TODO: This could be merged with `rseed` into a tuple. `recipient` and `value` are | ||
/// separate because they might need to be independently redacted. (For which role?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// TODO: This could be merged with `rseed` into a tuple. `recipient` and `value` are | |
/// separate because they might need to be independently redacted. (For which role?) | |
// TODO: This could be merged with `rseed` into a tuple. `recipient` and `value` are | |
// separate because they might need to be independently redacted. (For which role?) |
let (anchor, merkle_path) = { | ||
let cmx: ExtractedNoteCommitment = note.commitment().into(); | ||
let leaf = MerkleHashOrchard::from_cmx(&cmx); | ||
let mut tree = BridgeTree::<MerkleHashOrchard, u32, 32>::new(100); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: since BridgeTree
is no longer being maintained, we should move away from using it.
|
||
/// Authorizing data for a bundle of actions that is just missing a binding signature. | ||
#[derive(Debug)] | ||
pub struct Unbound { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this name isn't particularly intuitive to me, but I don't have a better suggestion; maybe BindingSigInputs
or something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name appears primarily in type signatures, i.e. Bundle<Unbound, ZatBalance>
; I chose it to make sense there (same as Authorized
/ Unauthorized
/ EffectsOnly
).
/// Verifies the given sighash with every `spend_auth_sig`, and then binds the bundle. | ||
/// | ||
/// Returns `None` if the given sighash does not validate against every `spend_auth_sig`. | ||
pub fn bind<R: RngCore + CryptoRng>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe apply_binding_sig
or bind_to_sighash
?
|
||
// Add signatures to dummy spends. | ||
for action in self.actions.iter_mut() { | ||
if let Some(sk) = action.spend.dummy_sk.take() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Point the take()
out.
/// The seed randomness for the output. | ||
/// | ||
/// - This is set by the Constructor. | ||
/// - This is required by the Prover, instead of disclosing `shared_secret` to them. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Remove
shared_secret
reference. - Describe how the Signer can use
rseed
andrecipient
to decrypt and verifyenc_ciphertext
(in the case whereock
is not present, orOvkPolicy::None
was used to make the data unrecoverable from chain by the sender).
|
||
// Run the Creator and Constructor roles. | ||
let mut builder = Builder::new(BundleType::DEFAULT, anchor); | ||
builder.add_spend(fvk, note, merkle_path).unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modify this test to send an output to a different recipient (with change), and then verify that the PCZT contains sufficient information to decrypt and check enc_ciphertext
.
sighash: [u8; 32], | ||
rng: R, | ||
) -> Option<crate::Bundle<Authorized, V>> { | ||
let bound = self.map_authorization( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this to after the check so we don't bother making the signature if the binding would be wrong.
MissingBindingSignatureSigningKey, | ||
/// The Transaction Extractor role requires `zkproof` to be set. | ||
MissingProof, | ||
/// The Transaction Extractor role requires all `zkproof` fields to be set. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// The Transaction Extractor role requires all `zkproof` fields to be set. | |
/// The Transaction Extractor role requires all `spend_auth_sig` fields to be set. |
Ok(redpallas::Signature::from( | ||
action | ||
.spend | ||
.spend_auth_sig |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copy-pasta: this is already a redpallas::Signature
.
.map(|r| { | ||
Address::from_raw_address_bytes(r) | ||
.into_option() | ||
.ok_or(ParseError::InvalidSpendRecipient) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename this error to InvalidRecipient
.
No description provided.