Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions packages/wasm-utxo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ ff = "0.13"
# shard/cap pair in build_ironwood_witness_from_shard. orchard's public API does not re-export
# these, so this must be a direct dependency.
incrementalmerkletree = "0.8"
# Pallas field arithmetic: `set_spend_auth_randomizer` parses `alpha` as a Pallas scalar to
# check `rk == ak.randomize(alpha)`, and the witness tests synthesize arbitrary canonical field
# elements (leaf/sibling hashes) without needing a real note-commitment tree. Version pinned to
# match orchard 0.15's own dependency (see Cargo.lock); already in-tree via orchard.
pasta_curves = "0.5"
# `LocatedPrunableTree::witness`/`PrunableTree::root_hash` do the real, pruning-aware Merkle
# folding for build_ironwood_witness_from_shard, so the shard/cap wire shape and witnessing math
# match what a real note-commitment-tree store (see wasm-privacy-coin's `ShardTree` usage) actually
Expand All @@ -109,10 +114,6 @@ hex = "0.4"
wasm-bindgen-test = "0.3"
rstest = "0.26.1"
pastey = "0.1"
# Pallas field arithmetic, used only in the ironwood_build witness tests to synthesize arbitrary
# canonical field elements (leaf/sibling hashes) without needing a real note-commitment tree.
# Version pinned to match orchard 0.15's own dependency (see Cargo.lock).
pasta_curves = "0.5"

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
# 11.2 is the first release with the `Transaction::V6` / Ironwood (`ironwood_shielded_data`)
Expand Down
6 changes: 4 additions & 2 deletions packages/wasm-utxo/cli/src/psbt/combine_ironwood_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ pub fn handle_combine_ironwood_proof_command(
(Some(proof), false) => {
let proof = hex::decode(&proof).context("invalid --proof hex")?;
psbt.combine_ironwood_proof(proof, OsRng)
.map_err(|e| anyhow!(e))
}
(None, true) => psbt.combine_ironwood_proof_locally(OsRng),
(None, true) => psbt
.combine_ironwood_proof_locally(OsRng)
.map_err(|e| anyhow!(e)),
(None, false) => bail!("expected exactly one of --proof or --local-proof"),
}
.map_err(|e| anyhow!(e))
.context("failed to combine Ironwood proof")?;

println!("{}", hex::encode(tx_bytes));
Expand Down
15 changes: 8 additions & 7 deletions packages/wasm-utxo/js/fixedScriptWallet/ZcashIronwoodTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ export type ZcashShieldedOutputRequest = {

/**
* The randomizer state of a shielded spend: `placeholder` while the spend still carries the
* wallet's spend-validating key as its `rk`, `installed` once the signer delivered the
* randomized `(alpha, rk)` pair.
* Constructor's fixed placeholder `rk`, `installed` once the signer delivered its randomized
* `rk`. The PSBT never stores the wallet's `ak` or the randomizer `alpha`.
*/
export const IronwoodRandomizerState = ["placeholder", "installed"] as const;
export type IronwoodRandomizerState = (typeof IronwoodRandomizerState)[number];
Expand Down Expand Up @@ -90,12 +90,13 @@ export type IronwoodShieldedStatus = {
inputKind: ZcashInputKind;
/** Action indices whose `out_ciphertext` is still pending. */
pendingOutCiphertexts: number[];
/** Action indices whose randomizer `(alpha, rk)` is still pending. */
/** Action indices whose randomized `rk` is still pending. */
pendingRandomizers: number[];
/** Action indices whose spend-auth signature is still pending. */
pendingSpendSignatures: number[];
/** Whether a Halo2 proof is installed in the PCZT. */
hasProof: boolean;
/** Whether the PSBT is ready for `combineProof`. */
readyToCombine: boolean;
/**
* Whether the PSBT is ready for its proof: `getProofRequest(randomizers)` succeeds, and
* `combineProof(proof)` then needs only the proof.
*/
readyForProof: boolean;
};
13 changes: 13 additions & 0 deletions packages/wasm-utxo/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub enum WasmUtxoError {
Ironwood(crate::zcash::ironwood_build::IronwoodBuildError),
Zip317(crate::zcash::zip317::Zip317Error),
VerifyIronwood(crate::zcash::ironwood_build::IronwoodVerifyError),
IronwoodPsbt(crate::fixed_script_wallet::bitgo_psbt::zcash::error::IronwoodPsbtError),
VerifyV6Signature(VerifyV6SignatureError),
}

Expand All @@ -46,11 +47,22 @@ impl fmt::Display for WasmUtxoError {
WasmUtxoError::Ironwood(e) => write!(f, "{}", e),
WasmUtxoError::Zip317(e) => write!(f, "{}", e),
WasmUtxoError::VerifyIronwood(e) => write!(f, "{}", e),
WasmUtxoError::IronwoodPsbt(e) => write!(f, "{}", e),
WasmUtxoError::VerifyV6Signature(e) => write!(f, "{}", e),
}
}
}

impl From<crate::fixed_script_wallet::bitgo_psbt::zcash::error::IronwoodPsbtError>
for WasmUtxoError
{
fn from(
error: crate::fixed_script_wallet::bitgo_psbt::zcash::error::IronwoodPsbtError,
) -> Self {
WasmUtxoError::IronwoodPsbt(error)
}
}

impl WasmErrorCode for WasmUtxoError {
fn code(&self) -> String {
match self {
Expand All @@ -62,6 +74,7 @@ impl WasmErrorCode for WasmUtxoError {
WasmUtxoError::Ironwood(e) => e.code(),
WasmUtxoError::Zip317(e) => e.code(),
WasmUtxoError::VerifyIronwood(e) => e.code(),
WasmUtxoError::IronwoodPsbt(e) => e.code(),
WasmUtxoError::VerifyV6Signature(e) => e.code(),
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub enum IronwoodPsbtError {
action_index: usize,
},
/// Randomizers installed: these real spends still carry the placeholder `rk` — the signer's
/// randomizers must be installed before the proof request.
/// randomized `rk`s must be installed before any sighash or the proof request.
RandomizerPending {
/// The pending real spends' action indices.
action_indices: Vec<usize>,
Expand All @@ -53,9 +53,27 @@ pub enum IronwoodPsbtError {
action_indices: Vec<usize>,
},
/// The bundle is frozen: a spend-auth signature (or a transparent
/// partial signature, as defense in depth) exists, so ciphertexts can no longer be
/// installed.
/// partial signature, as defense in depth) exists, so ciphertexts and randomizers can no
/// longer be installed.
AlreadySigned,
/// The batch names the same action index more than once.
DuplicateAction {
/// The repeated action index.
action_index: usize,
},
/// The proof request was given no `alpha` for these real spends: the PSBT never stores
/// the randomizers, so the prover needs one per real spend out of band.
RandomizerMissing {
/// The real spends' action indices without an `alpha`.
action_indices: Vec<usize>,
},
/// The proof is not the canonical size for the bundle's action count.
ProofLength {
/// `orchard::Proof::expected_proof_size` for the bundle's actions.
expected: usize,
/// The supplied proof's length.
actual: usize,
},
/// The operation has nothing to act on: there is no pending item, or the batch is empty.
NothingPending,
/// The Ironwood Constructor rejected the request; the wrapped
Expand All @@ -70,6 +88,9 @@ pub enum IronwoodPsbtError {
/// [`IronwoodVerifyError`](crate::zcash::ironwood_build::IronwoodVerifyError) carries
/// the failing check (and its own `IronwoodVerifyError.<check>` code).
Verify(crate::zcash::ironwood_build::IronwoodVerifyError),
/// Encoding the final v6 transaction failed; the wrapped
/// [`ZcashV6Error`](crate::zcash::v6::ZcashV6Error) carries the reason.
Encode(crate::zcash::v6::ZcashV6Error),
}

impl std::fmt::Display for IronwoodPsbtError {
Expand Down Expand Up @@ -116,15 +137,31 @@ impl std::fmt::Display for IronwoodPsbtError {
),
IronwoodPsbtError::AlreadySigned => write!(
f,
"the bundle is frozen: a signature exists, so out_ciphertexts can no longer \
be installed"
"the bundle is frozen: a signature exists, so out_ciphertexts and randomizers \
can no longer be installed"
),
IronwoodPsbtError::DuplicateAction { action_index } => {
write!(
f,
"action {action_index} appears more than once in the batch"
)
}
IronwoodPsbtError::RandomizerMissing { action_indices } => write!(
f,
"no alpha was supplied for real spends {action_indices:?}; the proof request \
needs one per real spend"
),
IronwoodPsbtError::ProofLength { expected, actual } => write!(
f,
"the proof is {actual} bytes; a bundle of this many actions needs {expected}"
),
IronwoodPsbtError::NothingPending => {
write!(f, "there is nothing pending for this operation")
}
IronwoodPsbtError::Build(e) => write!(f, "{e}"),
IronwoodPsbtError::Pczt(e) => write!(f, "{e}"),
IronwoodPsbtError::Verify(e) => write!(f, "{e}"),
IronwoodPsbtError::Encode(e) => write!(f, "{e}"),
}
}
}
Expand All @@ -137,6 +174,12 @@ impl From<crate::zcash::ironwood_build::IronwoodBuildError> for IronwoodPsbtErro
}
}

impl From<crate::zcash::v6::ZcashV6Error> for IronwoodPsbtError {
fn from(error: crate::zcash::v6::ZcashV6Error) -> Self {
IronwoodPsbtError::Encode(error)
}
}

impl From<crate::zcash::ironwood_pczt::IronwoodPcztError> for IronwoodPsbtError {
fn from(error: crate::zcash::ironwood_pczt::IronwoodPcztError) -> Self {
IronwoodPsbtError::Pczt(error)
Expand Down
Loading
Loading