From 9c6d1b958bd015f3fc3f8d5e5815b2bfc54e484b Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 19 Dec 2024 01:01:35 +0000 Subject: [PATCH] pczt: Add a Redactor role --- pczt/CHANGELOG.md | 3 + pczt/src/lib.rs | 5 + pczt/src/roles.rs | 2 + pczt/src/roles/redactor/mod.rs | 45 ++++ pczt/src/roles/redactor/orchard.rs | 222 +++++++++++++++++++ pczt/src/roles/redactor/sapling.rs | 284 +++++++++++++++++++++++++ pczt/src/roles/redactor/transparent.rs | 263 +++++++++++++++++++++++ 7 files changed, 824 insertions(+) create mode 100644 pczt/src/roles/redactor/mod.rs create mode 100644 pczt/src/roles/redactor/orchard.rs create mode 100644 pczt/src/roles/redactor/sapling.rs create mode 100644 pczt/src/roles/redactor/transparent.rs diff --git a/pczt/CHANGELOG.md b/pczt/CHANGELOG.md index 09850a109..c665bab8d 100644 --- a/pczt/CHANGELOG.md +++ b/pczt/CHANGELOG.md @@ -7,6 +7,9 @@ and this library adheres to Rust's notion of ## [Unreleased] +### Added +- `pczt::roles::redactor` + ### Changed - Migrated to `nonempty 0.11` diff --git a/pczt/src/lib.rs b/pczt/src/lib.rs index 374710c4f..cbdcba0bf 100644 --- a/pczt/src/lib.rs +++ b/pczt/src/lib.rs @@ -16,6 +16,11 @@ //! - Updater (anyone can contribute) //! - Adds information necessary for subsequent entities to proceed, such as key paths //! for signing spends. +//! - Redactor (anyone can execute) +//! - Removes information that is unnecessary for subsequent entities to proceed. +//! - This can be useful e.g. when creating a transaction that has inputs from multiple +//! independent Signers; each can receive a PCZT with just the information they need +//! to sign, but (e.g.) not the `alpha` values for other Signers. //! - Prover (capability holders can contribute) //! - Needs all private information for a single spend or output. //! - In practice, the Updater that adds a given spend or output will either act as diff --git a/pczt/src/roles.rs b/pczt/src/roles.rs index 8361fd9ba..9496a105e 100644 --- a/pczt/src/roles.rs +++ b/pczt/src/roles.rs @@ -7,6 +7,8 @@ pub mod verifier; pub mod updater; +pub mod redactor; + #[cfg(feature = "prover")] pub mod prover; diff --git a/pczt/src/roles/redactor/mod.rs b/pczt/src/roles/redactor/mod.rs new file mode 100644 index 000000000..c0080f1fb --- /dev/null +++ b/pczt/src/roles/redactor/mod.rs @@ -0,0 +1,45 @@ +use crate::{common::Global, Pczt}; + +pub mod orchard; +pub mod sapling; +pub mod transparent; + +pub struct Redactor { + pczt: Pczt, +} + +impl Redactor { + /// Instantiates the Redactor role with the given PCZT. + pub fn new(pczt: Pczt) -> Self { + Self { pczt } + } + + /// Redacts the global transaction details with the given closure. + pub fn redact_global_with(mut self, f: F) -> Self + where + F: FnOnce(GlobalRedactor<'_>), + { + f(GlobalRedactor(&mut self.pczt.global)); + self + } + + /// Finishes the Redactor role, returning the redacted PCZT. + pub fn finish(self) -> Pczt { + self.pczt + } +} + +/// An Redactor for the global transaction details. +pub struct GlobalRedactor<'a>(&'a mut Global); + +impl<'a> GlobalRedactor<'a> { + /// Redacts the proprietary value at the given key. + pub fn redact_proprietary(&mut self, key: &str) { + self.0.proprietary.remove(key); + } + + /// Removes all proprietary values. + pub fn clear_proprietary(&mut self) { + self.0.proprietary.clear(); + } +} diff --git a/pczt/src/roles/redactor/orchard.rs b/pczt/src/roles/redactor/orchard.rs new file mode 100644 index 000000000..5cbff1cb1 --- /dev/null +++ b/pczt/src/roles/redactor/orchard.rs @@ -0,0 +1,222 @@ +use crate::orchard::{Action, Bundle}; + +impl super::Redactor { + /// Redacts the Orchard bundle with the given closure. + pub fn redact_orchard_with(mut self, f: F) -> Self + where + F: FnOnce(OrchardRedactor<'_>), + { + f(OrchardRedactor(&mut self.pczt.orchard)); + self + } +} + +/// A Redactor for the Orchard bundle. +pub struct OrchardRedactor<'a>(&'a mut Bundle); + +impl<'a> OrchardRedactor<'a> { + /// Redacts all actions in the same way. + pub fn redact_actions(&mut self, f: F) + where + F: FnOnce(ActionRedactor<'_>), + { + f(ActionRedactor(Actions::All(&mut self.0.actions))); + } + + /// Redacts the action at the given index. + /// + /// Does nothing if the index is out of range. + pub fn redact_action(&mut self, index: usize, f: F) + where + F: FnOnce(ActionRedactor<'_>), + { + if let Some(action) = self.0.actions.get_mut(index) { + f(ActionRedactor(Actions::One(action))); + } + } + + /// Removes the proof. + pub fn clear_zkproof(&mut self) { + self.0.zkproof = None; + } + + /// Removes the proof. + pub fn clear_bsk(&mut self) { + self.0.bsk = None; + } +} + +/// A Redactor for Orchard actions. +pub struct ActionRedactor<'a>(Actions<'a>); + +enum Actions<'a> { + All(&'a mut [Action]), + One(&'a mut Action), +} + +impl<'a> ActionRedactor<'a> { + fn redact(&mut self, f: F) + where + F: Fn(&mut Action), + { + match &mut self.0 { + Actions::All(actions) => { + for action in actions.iter_mut() { + f(action); + } + } + Actions::One(action) => { + f(action); + } + } + } + + /// Removes the spend authorizing signature. + pub fn clear_spend_auth_sig(&mut self) { + self.redact(|action| { + action.spend.spend_auth_sig = None; + }); + } + + /// Removes the spend's recipient. + pub fn clear_spend_recipient(&mut self) { + self.redact(|action| { + action.spend.recipient = None; + }); + } + + /// Removes the spend's value. + pub fn clear_spend_value(&mut self) { + self.redact(|action| { + action.spend.value = None; + }); + } + + /// Removes the rho value for the note being spent. + pub fn clear_spend_rho(&mut self) { + self.redact(|action| { + action.spend.rho = None; + }); + } + + /// Removes the seed randomness for the note being spent. + pub fn clear_spend_rseed(&mut self) { + self.redact(|action| { + action.spend.rseed = None; + }); + } + + /// Removes the spend's full viewing key. + pub fn clear_spend_fvk(&mut self) { + self.redact(|action| { + action.spend.fvk = None; + }); + } + + /// Removes the witness from the spent note to the bundle's anchor. + pub fn clear_spend_witness(&mut self) { + self.redact(|action| { + action.spend.witness = None; + }); + } + + /// Removes the spend authorization randomizer. + pub fn clear_spend_alpha(&mut self) { + self.redact(|action| { + action.spend.alpha = None; + }); + } + + /// Removes the ZIP 32 derivation path at which the spending key can be found for the + /// note being spent. + pub fn clear_spend_zip32_derivation(&mut self) { + self.redact(|action| { + action.spend.zip32_derivation = None; + }); + } + + /// Removes the spending key for this spent note, if it is a dummy note. + pub fn clear_spend_dummy_sk(&mut self) { + self.redact(|action| { + action.spend.dummy_sk = None; + }); + } + + /// Redacts the spend-specific proprietary value at the given key. + pub fn redact_spend_proprietary(&mut self, key: &str) { + self.redact(|action| { + action.spend.proprietary.remove(key); + }); + } + + /// Removes all spend-specific proprietary values. + pub fn clear_spend_proprietary(&mut self) { + self.redact(|action| { + action.spend.proprietary.clear(); + }); + } + + /// Removes the output's recipient. + pub fn clear_output_recipient(&mut self) { + self.redact(|action| { + action.output.recipient = None; + }); + } + + /// Removes the output's value. + pub fn clear_output_value(&mut self) { + self.redact(|action| { + action.output.value = None; + }); + } + + /// Removes the seed randomness for the note being created. + pub fn clear_output_rseed(&mut self) { + self.redact(|action| { + action.output.rseed = None; + }); + } + + /// Removes the `ock` value used to encrypt `out_ciphertext`. + pub fn clear_output_ock(&mut self) { + self.redact(|action| { + action.output.ock = None; + }); + } + + /// Removes the ZIP 32 derivation path at which the spending key can be found for the + /// note being created. + pub fn clear_output_zip32_derivation(&mut self) { + self.redact(|action| { + action.output.zip32_derivation = None; + }); + } + + /// Removes the user-facing address to which the output is being sent, if any. + pub fn clear_output_user_address(&mut self) { + self.redact(|spend| { + spend.output.user_address = None; + }); + } + + /// Redacts the output-specific proprietary value at the given key. + pub fn redact_output_proprietary(&mut self, key: &str) { + self.redact(|action| { + action.output.proprietary.remove(key); + }); + } + + /// Removes all output-specific proprietary values. + pub fn clear_output_proprietary(&mut self) { + self.redact(|action| { + action.output.proprietary.clear(); + }); + } + + /// Removes the value commitment randomness. + pub fn clear_rcv(&mut self) { + self.redact(|action| { + action.rcv = None; + }); + } +} diff --git a/pczt/src/roles/redactor/sapling.rs b/pczt/src/roles/redactor/sapling.rs new file mode 100644 index 000000000..4499dd465 --- /dev/null +++ b/pczt/src/roles/redactor/sapling.rs @@ -0,0 +1,284 @@ +use crate::sapling::{Bundle, Output, Spend}; + +impl super::Redactor { + /// Redacts the Sapling bundle with the given closure. + pub fn redact_sapling_with(mut self, f: F) -> Self + where + F: FnOnce(SaplingRedactor<'_>), + { + f(SaplingRedactor(&mut self.pczt.sapling)); + self + } +} + +/// A Redactor for the Sapling bundle. +pub struct SaplingRedactor<'a>(&'a mut Bundle); + +impl<'a> SaplingRedactor<'a> { + /// Redacts all spends in the same way. + pub fn redact_spends(&mut self, f: F) + where + F: FnOnce(SpendRedactor<'_>), + { + f(SpendRedactor(Spends::All(&mut self.0.spends))); + } + + /// Redacts the spend at the given index. + /// + /// Does nothing if the index is out of range. + pub fn redact_spend(&mut self, index: usize, f: F) + where + F: FnOnce(SpendRedactor<'_>), + { + if let Some(spend) = self.0.spends.get_mut(index) { + f(SpendRedactor(Spends::One(spend))); + } + } + + /// Redacts all outputs in the same way. + pub fn redact_outputs(&mut self, f: F) + where + F: FnOnce(OutputRedactor<'_>), + { + f(OutputRedactor(Outputs::All(&mut self.0.outputs))); + } + + /// Redacts the output at the given index. + /// + /// Does nothing if the index is out of range. + pub fn redact_output(&mut self, index: usize, f: F) + where + F: FnOnce(OutputRedactor<'_>), + { + if let Some(output) = self.0.outputs.get_mut(index) { + f(OutputRedactor(Outputs::One(output))); + } + } + + /// Removes the proof. + pub fn clear_bsk(&mut self) { + self.0.bsk = None; + } +} + +/// A Redactor for Sapling spends. +pub struct SpendRedactor<'a>(Spends<'a>); + +enum Spends<'a> { + All(&'a mut [Spend]), + One(&'a mut Spend), +} + +impl<'a> SpendRedactor<'a> { + fn redact(&mut self, f: F) + where + F: Fn(&mut Spend), + { + match &mut self.0 { + Spends::All(spends) => { + for spend in spends.iter_mut() { + f(spend); + } + } + Spends::One(spend) => { + f(spend); + } + } + } + + /// Removes the proof. + pub fn clear_zkproof(&mut self) { + self.redact(|spend| { + spend.zkproof = None; + }); + } + + /// Removes the spend authorizing signature. + pub fn clear_spend_auth_sig(&mut self) { + self.redact(|spend| { + spend.spend_auth_sig = None; + }); + } + + /// Removes the recipient. + pub fn clear_recipient(&mut self) { + self.redact(|spend| { + spend.recipient = None; + }); + } + + /// Removes the value. + pub fn clear_value(&mut self) { + self.redact(|spend| { + spend.value = None; + }); + } + + /// Removes the note commitment randomness. + pub fn clear_rcm(&mut self) { + self.redact(|spend| { + spend.rcm = None; + }); + } + + /// Removes the seed randomness for the note being spent. + pub fn clear_rseed(&mut self) { + self.redact(|spend| { + spend.rseed = None; + }); + } + + /// Removes the value commitment randomness. + pub fn clear_rcv(&mut self) { + self.redact(|spend| { + spend.rcv = None; + }); + } + + /// Removes the proof generation key. + pub fn clear_proof_generation_key(&mut self) { + self.redact(|spend| { + spend.proof_generation_key = None; + }); + } + + /// Removes the witness from the note to the bundle's anchor. + pub fn clear_witness(&mut self) { + self.redact(|spend| { + spend.witness = None; + }); + } + + /// Removes the spend authorization randomizer. + pub fn clear_alpha(&mut self) { + self.redact(|spend| { + spend.alpha = None; + }); + } + + /// Removes the ZIP 32 derivation path at which the spending key can be found for the + /// note being spent. + pub fn clear_zip32_derivation(&mut self) { + self.redact(|spend| { + spend.zip32_derivation = None; + }); + } + + /// Removes the spend authorizing key for this spent note, if it is a dummy note. + pub fn clear_dummy_ask(&mut self) { + self.redact(|spend| { + spend.dummy_ask = None; + }); + } + + /// Redacts the proprietary value at the given key. + pub fn redact_proprietary(&mut self, key: &str) { + self.redact(|spend| { + spend.proprietary.remove(key); + }); + } + + /// Removes all proprietary values. + pub fn clear_proprietary(&mut self) { + self.redact(|spend| { + spend.proprietary.clear(); + }); + } +} + +/// A Redactor for Sapling outputs. +pub struct OutputRedactor<'a>(Outputs<'a>); + +enum Outputs<'a> { + All(&'a mut [Output]), + One(&'a mut Output), +} + +impl<'a> OutputRedactor<'a> { + fn redact(&mut self, f: F) + where + F: Fn(&mut Output), + { + match &mut self.0 { + Outputs::All(outputs) => { + for output in outputs.iter_mut() { + f(output); + } + } + Outputs::One(output) => { + f(output); + } + } + } + + /// Removes the proof. + pub fn clear_zkproof(&mut self) { + self.redact(|output| { + output.zkproof = None; + }); + } + + /// Removes the recipient. + pub fn clear_recipient(&mut self) { + self.redact(|output| { + output.recipient = None; + }); + } + + /// Removes the value. + pub fn clear_value(&mut self) { + self.redact(|output| { + output.value = None; + }); + } + + /// Removes the seed randomness for the note being created. + pub fn clear_rseed(&mut self) { + self.redact(|output| { + output.rseed = None; + }); + } + + /// Removes the value commitment randomness. + pub fn clear_rcv(&mut self) { + self.redact(|output| { + output.rcv = None; + }); + } + + /// Removes the `ock` value used to encrypt `out_ciphertext`. + pub fn clear_ock(&mut self) { + self.redact(|output| { + output.ock = None; + }); + } + + /// Removes the ZIP 32 derivation path at which the spending key can be found for the + /// note being created. + pub fn clear_zip32_derivation(&mut self) { + self.redact(|output| { + output.zip32_derivation = None; + }); + } + + /// Removes the user-facing address to which this output is being sent, if any. + pub fn clear_user_address(&mut self) { + self.redact(|output| { + output.user_address = None; + }); + } + + /// Redacts the proprietary value at the given key. + pub fn redact_proprietary(&mut self, key: &str) { + self.redact(|output| { + output.proprietary.remove(key); + }); + } + + /// Removes all proprietary values. + pub fn clear_proprietary(&mut self) { + self.redact(|output| { + output.proprietary.clear(); + }); + } +} diff --git a/pczt/src/roles/redactor/transparent.rs b/pczt/src/roles/redactor/transparent.rs new file mode 100644 index 000000000..f920b4b48 --- /dev/null +++ b/pczt/src/roles/redactor/transparent.rs @@ -0,0 +1,263 @@ +use crate::transparent::{Bundle, Input, Output}; + +impl super::Redactor { + /// Redacts the transparent bundle with the given closure. + pub fn redact_transparent_with(mut self, f: F) -> Self + where + F: FnOnce(TransparentRedactor<'_>), + { + f(TransparentRedactor(&mut self.pczt.transparent)); + self + } +} + +/// A Redactor for the transparent bundle. +pub struct TransparentRedactor<'a>(&'a mut Bundle); + +impl<'a> TransparentRedactor<'a> { + /// Redacts all inputs in the same way. + pub fn redact_inputs(&mut self, f: F) + where + F: FnOnce(InputRedactor<'_>), + { + f(InputRedactor(Inputs::All(&mut self.0.inputs))); + } + + /// Redacts the input at the given index. + /// + /// Does nothing if the index is out of range. + pub fn redact_input(&mut self, index: usize, f: F) + where + F: FnOnce(InputRedactor<'_>), + { + if let Some(input) = self.0.inputs.get_mut(index) { + f(InputRedactor(Inputs::One(input))); + } + } + + /// Redacts all outputs in the same way. + pub fn redact_outputs(&mut self, f: F) + where + F: FnOnce(OutputRedactor<'_>), + { + f(OutputRedactor(Outputs::All(&mut self.0.outputs))); + } + + /// Redacts the output at the given index. + /// + /// Does nothing if the index is out of range. + pub fn redact_output(&mut self, index: usize, f: F) + where + F: FnOnce(OutputRedactor<'_>), + { + if let Some(output) = self.0.outputs.get_mut(index) { + f(OutputRedactor(Outputs::One(output))); + } + } +} + +/// A Redactor for transparent inputs. +pub struct InputRedactor<'a>(Inputs<'a>); + +enum Inputs<'a> { + All(&'a mut [Input]), + One(&'a mut Input), +} + +impl<'a> InputRedactor<'a> { + fn redact(&mut self, f: F) + where + F: Fn(&mut Input), + { + match &mut self.0 { + Inputs::All(inputs) => { + for input in inputs.iter_mut() { + f(input); + } + } + Inputs::One(input) => { + f(input); + } + } + } + + /// Removes the `script_sig`. + pub fn clear_script_sig(&mut self) { + self.redact(|input| { + input.script_sig = None; + }); + } + + /// Removes the `redeem_script`. + pub fn clear_redeem_script(&mut self) { + self.redact(|input| { + input.redeem_script = None; + }); + } + + /// Redacts the signature for the given pubkey. + pub fn redact_partial_signature(&mut self, pubkey: [u8; 33]) { + self.redact(|input| { + input.partial_signatures.remove(&pubkey); + }); + } + + /// Removes all signatures. + pub fn clear_partial_signatures(&mut self) { + self.redact(|input| { + input.partial_signatures.clear(); + }); + } + + /// Redacts the BIP 32 derivation path for the given pubkey. + pub fn redact_bip32_derivation(&mut self, pubkey: [u8; 33]) { + self.redact(|input| { + input.bip32_derivation.remove(&pubkey); + }); + } + + /// Removes all BIP 32 derivation paths. + pub fn clear_bip32_derivation(&mut self) { + self.redact(|input| { + input.bip32_derivation.clear(); + }); + } + + /// Redacts the RIPEMD160 preimage for the given hash. + pub fn redact_ripemd160_preimage(&mut self, hash: [u8; 20]) { + self.redact(|input| { + input.ripemd160_preimages.remove(&hash); + }); + } + + /// Removes all RIPEMD160 preimages. + pub fn clear_ripemd160_preimages(&mut self) { + self.redact(|input| { + input.ripemd160_preimages.clear(); + }); + } + + /// Redacts the SHA256 preimage for the given hash. + pub fn redact_sha256_preimage(&mut self, hash: [u8; 32]) { + self.redact(|input| { + input.sha256_preimages.remove(&hash); + }); + } + + /// Removes all SHA256 preimages. + pub fn clear_sha256_preimages(&mut self) { + self.redact(|input| { + input.sha256_preimages.clear(); + }); + } + + /// Redacts the HASH160 preimage for the given hash. + pub fn redact_hash160_preimage(&mut self, hash: [u8; 20]) { + self.redact(|input| { + input.hash160_preimages.remove(&hash); + }); + } + + /// Removes all HASH160 preimages. + pub fn clear_hash160_preimages(&mut self) { + self.redact(|input| { + input.hash160_preimages.clear(); + }); + } + + /// Redacts the HASH256 preimage for the given hash. + pub fn redact_hash256_preimage(&mut self, hash: [u8; 32]) { + self.redact(|input| { + input.hash256_preimages.remove(&hash); + }); + } + + /// Removes all HASH256 preimages. + pub fn clear_hash256_preimages(&mut self) { + self.redact(|input| { + input.hash256_preimages.clear(); + }); + } + + /// Redacts the proprietary value at the given key. + pub fn redact_proprietary(&mut self, key: &str) { + self.redact(|input| { + input.proprietary.remove(key); + }); + } + + /// Removes all proprietary values. + pub fn clear_proprietary(&mut self) { + self.redact(|input| { + input.proprietary.clear(); + }); + } +} + +/// A Redactor for transparent outputs. +pub struct OutputRedactor<'a>(Outputs<'a>); + +enum Outputs<'a> { + All(&'a mut [Output]), + One(&'a mut Output), +} + +impl<'a> OutputRedactor<'a> { + fn redact(&mut self, f: F) + where + F: Fn(&mut Output), + { + match &mut self.0 { + Outputs::All(outputs) => { + for output in outputs.iter_mut() { + f(output); + } + } + Outputs::One(output) => { + f(output); + } + } + } + + /// Removes the `redeem_script`. + pub fn clear_redeem_script(&mut self) { + self.redact(|output| { + output.redeem_script = None; + }); + } + + /// Redacts the BIP 32 derivation path for the given pubkey. + pub fn redact_bip32_derivation(&mut self, pubkey: [u8; 33]) { + self.redact(|output| { + output.bip32_derivation.remove(&pubkey); + }); + } + + /// Removes all BIP 32 derivation paths. + pub fn clear_bip32_derivation(&mut self) { + self.redact(|output| { + output.bip32_derivation.clear(); + }); + } + + /// Removes the user-facing address to which this output is being sent, if any. + pub fn clear_user_address(&mut self) { + self.redact(|output| { + output.user_address = None; + }); + } + + /// Redacts the proprietary value at the given key. + pub fn redact_proprietary(&mut self, key: &str) { + self.redact(|output| { + output.proprietary.remove(key); + }); + } + + /// Removes all proprietary values. + pub fn clear_proprietary(&mut self) { + self.redact(|output| { + output.proprietary.clear(); + }); + } +}