use blake2b_simd::Hash as Blake2bHash;
use orchard::primitives::redpallas;
use rand_core::OsRng;
use ::transparent::sighash::{SIGHASH_ANYONECANPAY, SIGHASH_NONE, SIGHASH_SINGLE};
use zcash_primitives::transaction::{
TransactionData, TxDigests, sighash::SignableInput, txid::TxIdDigester,
};
use crate::{
ExtractError, ParsedPczt, Pczt,
common::{
FLAG_HAS_SIGHASH_SINGLE, FLAG_SHIELDED_MODIFIABLE, FLAG_TRANSPARENT_INPUTS_MODIFIABLE,
FLAG_TRANSPARENT_OUTPUTS_MODIFIABLE, Global,
},
};
pub use crate::EffectsOnly;
use crate::sighash;
pub struct Signer {
global: Global,
transparent: transparent::pczt::Bundle,
sapling: sapling::pczt::Bundle,
orchard: orchard::pczt::Bundle,
tx_data: TransactionData<EffectsOnly>,
txid_parts: TxDigests<Blake2bHash>,
shielded_sighash: [u8; 32],
secp: secp256k1::Secp256k1<secp256k1::All>,
}
impl Signer {
pub fn new(pczt: Pczt) -> Result<Self, Error> {
let ParsedPczt {
global,
transparent,
sapling,
orchard,
tx_data,
} = pczt.extract_tx_data(
|t| {
t.extract_effects()
.map_err(ExtractError::TransparentExtract)
},
|s| s.extract_effects().map_err(ExtractError::SaplingExtract),
|o| o.extract_effects().map_err(ExtractError::OrchardExtract),
)?;
let txid_parts = tx_data.digest(TxIdDigester);
let shielded_sighash = sighash(&tx_data, &SignableInput::Shielded, &txid_parts);
Ok(Self {
global,
transparent,
sapling,
orchard,
tx_data,
txid_parts,
shielded_sighash,
secp: secp256k1::Secp256k1::new(),
})
}
pub fn shielded_sighash(&self) -> [u8; 32] {
self.shielded_sighash
}
pub fn transparent_sighash(&self, index: usize) -> Result<[u8; 32], Error> {
let input = self
.transparent
.inputs()
.get(index)
.ok_or(Error::InvalidIndex)?;
input.with_signable_input(index, |signable_input| {
Ok(sighash(
&self.tx_data,
&SignableInput::Transparent(signable_input),
&self.txid_parts,
))
})
}
pub fn sign_transparent(
&mut self,
index: usize,
sk: &secp256k1::SecretKey,
) -> Result<(), Error> {
self.generate_or_append_transparent_signature(index, |input, tx_data, txid_parts, secp| {
input.sign(
index,
|input| sighash(tx_data, &SignableInput::Transparent(input), txid_parts),
sk,
secp,
)
})
}
pub fn append_transparent_signature(
&mut self,
index: usize,
signature: secp256k1::ecdsa::Signature,
) -> Result<(), Error> {
self.generate_or_append_transparent_signature(index, |input, tx_data, txid_parts, secp| {
input.append_signature(
index,
|input| sighash(tx_data, &SignableInput::Transparent(input), txid_parts),
signature,
secp,
)
})
}
fn generate_or_append_transparent_signature<F>(
&mut self,
index: usize,
f: F,
) -> Result<(), Error>
where
F: FnOnce(
&mut transparent::pczt::Input,
&TransactionData<EffectsOnly>,
&TxDigests<Blake2bHash>,
&secp256k1::Secp256k1<secp256k1::All>,
) -> Result<(), transparent::pczt::SignerError>,
{
let input = self
.transparent
.inputs_mut()
.get_mut(index)
.ok_or(Error::InvalidIndex)?;
f(input, &self.tx_data, &self.txid_parts, &self.secp).map_err(Error::TransparentSign)?;
if input.sighash_type().encode() & SIGHASH_ANYONECANPAY == 0 {
self.global.tx_modifiable &= !FLAG_TRANSPARENT_INPUTS_MODIFIABLE;
}
if (input.sighash_type().encode() & !SIGHASH_ANYONECANPAY) != SIGHASH_NONE {
self.global.tx_modifiable &= !FLAG_TRANSPARENT_OUTPUTS_MODIFIABLE;
}
if (input.sighash_type().encode() & !SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE {
self.global.tx_modifiable |= FLAG_HAS_SIGHASH_SINGLE;
}
self.global.tx_modifiable &= !FLAG_SHIELDED_MODIFIABLE;
Ok(())
}
pub fn sign_sapling(
&mut self,
index: usize,
ask: &sapling::keys::SpendAuthorizingKey,
) -> Result<(), Error> {
self.generate_or_apply_sapling_signature(index, |spend, shielded_sighash| {
spend.sign(shielded_sighash, ask, OsRng)
})
}
pub fn apply_sapling_signature(
&mut self,
index: usize,
signature: redjubjub::Signature<redjubjub::SpendAuth>,
) -> Result<(), Error> {
self.generate_or_apply_sapling_signature(index, |spend, shielded_sighash| {
spend.apply_signature(shielded_sighash, signature)
})
}
fn generate_or_apply_sapling_signature<F>(&mut self, index: usize, f: F) -> Result<(), Error>
where
F: FnOnce(&mut sapling::pczt::Spend, [u8; 32]) -> Result<(), sapling::pczt::SignerError>,
{
let spend = self
.sapling
.spends_mut()
.get_mut(index)
.ok_or(Error::InvalidIndex)?;
match spend.verify_nullifier(None) {
Err(
sapling::pczt::VerifyError::MissingRecipient
| sapling::pczt::VerifyError::MissingValue
| sapling::pczt::VerifyError::MissingRandomSeed,
) => Ok(()),
r => r,
}
.map_err(Error::SaplingVerify)?;
f(spend, self.shielded_sighash).map_err(Error::SaplingSign)?;
self.global.tx_modifiable &= !(FLAG_TRANSPARENT_INPUTS_MODIFIABLE
| FLAG_TRANSPARENT_OUTPUTS_MODIFIABLE
| FLAG_SHIELDED_MODIFIABLE);
Ok(())
}
pub fn sign_orchard(
&mut self,
index: usize,
ask: &orchard::keys::SpendAuthorizingKey,
) -> Result<(), Error> {
self.generate_or_apply_orchard_signature(index, |spend, shielded_sighash| {
spend.sign(shielded_sighash, ask, OsRng)
})
}
pub fn apply_orchard_signature(
&mut self,
index: usize,
signature: redpallas::Signature<redpallas::SpendAuth>,
) -> Result<(), Error> {
self.generate_or_apply_orchard_signature(index, |action, shielded_sighash| {
action.apply_signature(shielded_sighash, signature)
})
}
fn generate_or_apply_orchard_signature<F>(&mut self, index: usize, f: F) -> Result<(), Error>
where
F: FnOnce(&mut orchard::pczt::Action, [u8; 32]) -> Result<(), orchard::pczt::SignerError>,
{
let action = self
.orchard
.actions_mut()
.get_mut(index)
.ok_or(Error::InvalidIndex)?;
match action.spend().verify_nullifier(None) {
Err(
orchard::pczt::VerifyError::MissingRecipient
| orchard::pczt::VerifyError::MissingValue
| orchard::pczt::VerifyError::MissingRho
| orchard::pczt::VerifyError::MissingRandomSeed,
) => Ok(()),
r => r,
}
.map_err(Error::OrchardVerify)?;
f(action, self.shielded_sighash).map_err(Error::OrchardSign)?;
self.global.tx_modifiable &= !(FLAG_TRANSPARENT_INPUTS_MODIFIABLE
| FLAG_TRANSPARENT_OUTPUTS_MODIFIABLE
| FLAG_SHIELDED_MODIFIABLE);
Ok(())
}
pub fn finish(self) -> Pczt {
Pczt {
global: self.global,
transparent: crate::transparent::Bundle::serialize_from(self.transparent),
sapling: crate::sapling::Bundle::serialize_from(self.sapling),
orchard: crate::orchard::Bundle::serialize_from(self.orchard),
}
}
}
#[derive(Debug)]
pub enum Error {
Extract(crate::ExtractError),
InvalidIndex,
OrchardSign(orchard::pczt::SignerError),
OrchardVerify(orchard::pczt::VerifyError),
SaplingSign(sapling::pczt::SignerError),
SaplingVerify(sapling::pczt::VerifyError),
TransparentSign(transparent::pczt::SignerError),
}
impl From<crate::ExtractError> for Error {
fn from(e: crate::ExtractError) -> Self {
Error::Extract(e)
}
}