Skip to content

Commit

Permalink
Merge pull request #472 from nuttycom/zip_244/hw_wallet_commitments
Browse files Browse the repository at this point in the history
Make transparent signatures commit to all input amounts & scripts.
  • Loading branch information
nuttycom authored Jan 19, 2022
2 parents c910ffd + eaa3ec5 commit 81c69dd
Show file tree
Hide file tree
Showing 11 changed files with 1,475 additions and 1,283 deletions.
4 changes: 2 additions & 2 deletions zcash_primitives/src/legacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ impl Shl<&[u8]> for Script {
/// A transparent address corresponding to either a public key or a `Script`.
#[derive(Debug, PartialEq, PartialOrd, Hash, Clone)]
pub enum TransparentAddress {
PublicKey([u8; 20]),
Script([u8; 20]),
PublicKey([u8; 20]), // TODO: Rename to PublicKeyHash
Script([u8; 20]), // TODO: Rename to ScriptHash
}

impl TransparentAddress {
Expand Down
10 changes: 3 additions & 7 deletions zcash_primitives/src/transaction/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use crate::{
},
transparent::{self, builder::TransparentBuilder},
},
sighash::{signature_hash, SignableInput, SIGHASH_ALL},
sighash::{signature_hash, SignableInput},
txid::TxIdDigester,
Transaction, TransactionData, TxVersion, Unauthorized,
},
Expand Down Expand Up @@ -372,12 +372,8 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
// the commitment being signed is shared across all Sapling inputs; once
// V4 transactions are deprecated this should just be the txid, but
// for now we need to continue to compute it here.
let shielded_sig_commitment = signature_hash(
&unauthed_tx,
SIGHASH_ALL,
&SignableInput::Shielded,
&txid_parts,
);
let shielded_sig_commitment =
signature_hash(&unauthed_tx, &SignableInput::Shielded, &txid_parts);

let (sapling_bundle, tx_metadata) = match unauthed_tx
.sapling_bundle
Expand Down
2 changes: 1 addition & 1 deletion zcash_primitives/src/transaction/components/sprout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const PHGR_PROOF_SIZE: usize = 33 + 33 + 65 + 33 + 33 + 33 + 33 + 33;
const ZC_NUM_JS_INPUTS: usize = 2;
const ZC_NUM_JS_OUTPUTS: usize = 2;

#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Bundle {
pub joinsplits: Vec<JsDescription>,
pub joinsplit_pubkey: [u8; 32],
Expand Down
61 changes: 46 additions & 15 deletions zcash_primitives/src/transaction/components/transparent/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@ use std::fmt;
use blake2b_simd::Hash as Blake2bHash;

use crate::{
legacy::TransparentAddress,
transaction::components::{
amount::Amount,
transparent::{self, Authorization, Authorized, Bundle, TxIn, TxOut},
legacy::{Script, TransparentAddress},
transaction::{
components::{
amount::Amount,
transparent::{self, Authorization, Authorized, Bundle, TxIn, TxOut},
},
sighash::TransparentAuthorizingContext,
},
};

#[cfg(feature = "transparent-inputs")]
use crate::{
legacy::Script,
transaction::{
self as tx,
components::OutPoint,
sighash::{signature_hash, SignableInput, SIGHASH_ALL},
TransactionData, TxDigests,
},
use crate::transaction::{
self as tx,
components::OutPoint,
sighash::{signature_hash, SignableInput, SIGHASH_ALL},
TransactionData, TxDigests,
};

#[derive(Debug, PartialEq)]
Expand Down Expand Up @@ -188,6 +188,32 @@ impl TxIn<Unauthorized> {
}
}

#[cfg(not(feature = "transparent-inputs"))]
impl TransparentAuthorizingContext for Unauthorized {
fn input_amounts(&self) -> Vec<Amount> {
vec![]
}

fn input_scriptpubkeys(&self) -> Vec<Script> {
vec![]
}
}

#[cfg(feature = "transparent-inputs")]
impl TransparentAuthorizingContext for Unauthorized {
fn input_amounts(&self) -> Vec<Amount> {
return self.inputs.iter().map(|txin| txin.coin.value).collect();
}

fn input_scriptpubkeys(&self) -> Vec<Script> {
return self
.inputs
.iter()
.map(|txin| txin.coin.script_pubkey.clone())
.collect();
}
}

impl Bundle<Unauthorized> {
pub fn apply_signatures(
self,
Expand All @@ -200,11 +226,16 @@ impl Bundle<Unauthorized> {
.inputs
.iter()
.enumerate()
.map(|(i, info)| {
.map(|(index, info)| {
let sighash = signature_hash(
mtx,
SIGHASH_ALL,
&SignableInput::transparent(i, &info.coin.script_pubkey, info.coin.value),
&SignableInput::Transparent {
hash_type: SIGHASH_ALL,
index,
script_code: &info.coin.script_pubkey, // for p2pkh, always the same as script_pubkey
script_pubkey: &info.coin.script_pubkey,
value: info.coin.value,
},
txid_parts_cache,
);

Expand Down
7 changes: 5 additions & 2 deletions zcash_primitives/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,10 @@ impl<A: Authorization> TransactionData<A> {
self.consensus_branch_id
}

pub fn lock_time(&self) -> u32 {
self.lock_time
}

pub fn expiry_height(&self) -> BlockHeight {
self.expiry_height
}
Expand Down Expand Up @@ -924,10 +928,9 @@ impl Transaction {

#[derive(Clone)]
pub struct TransparentDigests<A> {
pub prevout_digest: A,
pub prevouts_digest: A,
pub sequence_digest: A,
pub outputs_digest: A,
pub per_input_digest: Option<A>,
}

#[derive(Clone)]
Expand Down
135 changes: 49 additions & 86 deletions zcash_primitives/src/transaction/sighash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::convert::TryInto;
use super::{
components::{
sapling::{self, GrothProofBytes},
Amount,
transparent, Amount,
},
sighash_v4::v4_signature_hash,
sighash_v5::v5_signature_hash,
Expand All @@ -15,93 +15,37 @@ use super::{
#[cfg(feature = "zfuture")]
use crate::extensions::transparent::Precondition;

pub const SIGHASH_ALL: u32 = 1;
pub const SIGHASH_NONE: u32 = 2;
pub const SIGHASH_SINGLE: u32 = 3;
pub const SIGHASH_MASK: u32 = 0x1f;
pub const SIGHASH_ANYONECANPAY: u32 = 0x80;

pub struct TransparentInput<'a> {
index: usize,
script_code: &'a Script,
value: Amount,
}

impl<'a> TransparentInput<'a> {
pub fn new(index: usize, script_code: &'a Script, value: Amount) -> Self {
TransparentInput {
index,
script_code,
value,
}
}

pub fn index(&self) -> usize {
self.index
}

pub fn script_code(&self) -> &'a Script {
self.script_code
}

pub fn value(&self) -> Amount {
self.value
}
}

#[cfg(feature = "zfuture")]
pub struct TzeInput<'a> {
index: usize,
precondition: &'a Precondition,
value: Amount,
}

#[cfg(feature = "zfuture")]
impl<'a> TzeInput<'a> {
pub fn new(index: usize, precondition: &'a Precondition, value: Amount) -> Self {
TzeInput {
index,
precondition,
value,
}
}

pub fn index(&self) -> usize {
self.index
}

pub fn precondition(&self) -> &'a Precondition {
self.precondition
}

pub fn value(&self) -> Amount {
self.value
}
}
pub const SIGHASH_ALL: u8 = 0x01;
pub const SIGHASH_NONE: u8 = 0x02;
pub const SIGHASH_SINGLE: u8 = 0x03;
pub const SIGHASH_MASK: u8 = 0x1f;
pub const SIGHASH_ANYONECANPAY: u8 = 0x80;

pub enum SignableInput<'a> {
Shielded,
Transparent(TransparentInput<'a>),
Transparent {
hash_type: u8,
index: usize,
script_code: &'a Script,
script_pubkey: &'a Script,
value: Amount,
},
#[cfg(feature = "zfuture")]
Tze(TzeInput<'a>),
Tze {
index: usize,
precondition: &'a Precondition,
value: Amount,
},
}

impl<'a> SignableInput<'a> {
pub fn transparent(index: usize, script_code: &'a Script, value: Amount) -> Self {
SignableInput::Transparent(TransparentInput {
index,
script_code,
value,
})
}

#[cfg(feature = "zfuture")]
pub fn tze(index: usize, precondition: &'a Precondition, value: Amount) -> Self {
SignableInput::Tze(TzeInput {
index,
precondition,
value,
})
pub fn hash_type(&self) -> u8 {
match self {
SignableInput::Shielded => SIGHASH_ALL,
SignableInput::Transparent { hash_type, .. } => *hash_type,
#[cfg(feature = "zfuture")]
SignableInput::Tze { .. } => SIGHASH_ALL,
}
}
}

Expand All @@ -113,24 +57,43 @@ impl AsRef<[u8; 32]> for SignatureHash {
}
}

/// Addtional context that is needed to compute signature hashes
/// for transactions that include transparent inputs or outputs.
pub trait TransparentAuthorizingContext: transparent::Authorization {
/// Returns the list of all transparent input amounts, provided
/// so that wallets can commit to the transparent input breakdown
/// without requiring the full data of the previous transactions
/// providing these inputs.
fn input_amounts(&self) -> Vec<Amount>;
/// Returns the list of all transparent input scriptPubKeys, provided
/// so that wallets can commit to the transparent input breakdown
/// without requiring the full data of the previous transactions
/// providing these inputs.
fn input_scriptpubkeys(&self) -> Vec<Script>;
}

/// Computes the signature hash for an input to a transaction, given
/// the full data of the transaction, the input being signed, and the
/// set of precomputed hashes produced in the construction of the
/// transaction ID.
pub fn signature_hash<
'a,
TA: TransparentAuthorizingContext,
SA: sapling::Authorization<Proof = GrothProofBytes>,
A: Authorization<SaplingAuth = SA>,
A: Authorization<SaplingAuth = SA, TransparentAuth = TA>,
>(
tx: &TransactionData<A>,
hash_type: u32,
signable_input: &SignableInput<'a>,
txid_parts: &TxDigests<Blake2bHash>,
) -> SignatureHash {
SignatureHash(match tx.version {
TxVersion::Sprout(_) | TxVersion::Overwinter | TxVersion::Sapling => {
v4_signature_hash(tx, hash_type, signable_input)
v4_signature_hash(tx, signable_input)
}

TxVersion::Zip225 => v5_signature_hash(tx, hash_type, signable_input, txid_parts),
TxVersion::Zip225 => v5_signature_hash(tx, signable_input, txid_parts),

#[cfg(feature = "zfuture")]
TxVersion::ZFuture => v5_signature_hash(tx, hash_type, signable_input, txid_parts),
TxVersion::ZFuture => v5_signature_hash(tx, signable_input, txid_parts),
})
}
25 changes: 15 additions & 10 deletions zcash_primitives/src/transaction/sighash_v4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ pub fn v4_signature_hash<
A: Authorization<SaplingAuth = SA>,
>(
tx: &TransactionData<A>,
hash_type: u32,
signable_input: &SignableInput<'_>,
) -> Blake2bHash {
let hash_type = signable_input.hash_type();
if tx.version.has_overwinter() {
let mut personal = [0; 16];
(&mut personal[..12]).copy_from_slice(ZCASH_SIGHASH_PERSONALIZATION_PREFIX);
Expand Down Expand Up @@ -192,8 +192,8 @@ pub fn v4_signature_hash<
);
} else if (hash_type & SIGHASH_MASK) == SIGHASH_SINGLE {
match (tx.transparent_bundle.as_ref(), signable_input) {
(Some(b), SignableInput::Transparent(input)) if input.index() < b.vout.len() => {
h.update(single_output_hash(&b.vout[input.index()]).as_bytes())
(Some(b), SignableInput::Transparent { index, .. }) if index < &b.vout.len() => {
h.update(single_output_hash(&b.vout[*index]).as_bytes())
}
_ => h.update(&[0; 32]),
};
Expand Down Expand Up @@ -237,18 +237,23 @@ pub fn v4_signature_hash<
if tx.version.has_sapling() {
h.update(&tx.sapling_value_balance().to_i64_le_bytes());
}
update_u32!(h, hash_type, tmp);
update_u32!(h, hash_type.into(), tmp);

match signable_input {
SignableInput::Shielded => (),
SignableInput::Transparent(input) => {
SignableInput::Transparent {
index,
script_code,
value,
..
} => {
if let Some(bundle) = tx.transparent_bundle.as_ref() {
let mut data = vec![];
bundle.vin[input.index()].prevout.write(&mut data).unwrap();
input.script_code().write(&mut data).unwrap();
data.extend_from_slice(&input.value().to_i64_le_bytes());
bundle.vin[*index].prevout.write(&mut data).unwrap();
script_code.write(&mut data).unwrap();
data.extend_from_slice(&value.to_i64_le_bytes());
(&mut data)
.write_u32::<LittleEndian>(bundle.vin[input.index()].sequence)
.write_u32::<LittleEndian>(bundle.vin[*index].sequence)
.unwrap();
h.update(&data);
} else {
Expand All @@ -259,7 +264,7 @@ pub fn v4_signature_hash<
}

#[cfg(feature = "zfuture")]
SignableInput::Tze(_) => {
SignableInput::Tze { .. } => {
panic!("A request has been made to sign a TZE input, but the transaction version is not ZFuture");
}
}
Expand Down
Loading

0 comments on commit 81c69dd

Please sign in to comment.