mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-11-01 20:07:02 +00:00
ZIP 143
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -704,6 +704,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
name = "zcash_primitives"
|
name = "zcash_primitives"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)",
|
||||||
"byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pairing 0.14.2",
|
"pairing 0.14.2",
|
||||||
|
|||||||
@@ -10,3 +10,7 @@ byteorder = "1"
|
|||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
pairing = { path = "../pairing" }
|
pairing = { path = "../pairing" }
|
||||||
sapling-crypto = { path = "../sapling-crypto" }
|
sapling-crypto = { path = "../sapling-crypto" }
|
||||||
|
|
||||||
|
[dependencies.blake2-rfc]
|
||||||
|
git = "https://github.com/gtank/blake2-rfc"
|
||||||
|
rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
|
||||||
|
extern crate blake2_rfc;
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
extern crate pairing;
|
extern crate pairing;
|
||||||
extern crate sapling_crypto;
|
extern crate sapling_crypto;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const ZC_NUM_JS_OUTPUTS: usize = 2;
|
|||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct Amount(pub i64);
|
pub struct Amount(pub i64);
|
||||||
|
|
||||||
struct Script(Vec<u8>);
|
pub struct Script(pub Vec<u8>);
|
||||||
|
|
||||||
impl Script {
|
impl Script {
|
||||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||||
@@ -36,7 +36,7 @@ impl Script {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OutPoint {
|
pub struct OutPoint {
|
||||||
hash: [u8; 32],
|
hash: [u8; 32],
|
||||||
n: u32,
|
n: u32,
|
||||||
}
|
}
|
||||||
@@ -56,9 +56,9 @@ impl OutPoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct TxIn {
|
pub struct TxIn {
|
||||||
prevout: OutPoint,
|
pub prevout: OutPoint,
|
||||||
script_sig: Script,
|
script_sig: Script,
|
||||||
sequence: u32,
|
pub sequence: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TxIn {
|
impl TxIn {
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ use std::io::{self, Read, Write};
|
|||||||
|
|
||||||
use serialize::Vector;
|
use serialize::Vector;
|
||||||
|
|
||||||
mod components;
|
pub mod components;
|
||||||
|
mod sighash;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|||||||
174
zcash_primitives/src/transaction/sighash.rs
Normal file
174
zcash_primitives/src/transaction/sighash.rs
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
use blake2_rfc::blake2b::Blake2b;
|
||||||
|
use byteorder::{LittleEndian, WriteBytesExt};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
components::{Amount, Script},
|
||||||
|
Transaction, OVERWINTER_VERSION_GROUP_ID, SAPLING_TX_VERSION,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ZCASH_SIGHASH_PERSONALIZATION_PREFIX: &'static [u8; 12] = b"ZcashSigHash";
|
||||||
|
const ZCASH_PREVOUTS_HASH_PERSONALIZATION: &'static [u8; 16] = b"ZcashPrevoutHash";
|
||||||
|
const ZCASH_SEQUENCE_HASH_PERSONALIZATION: &'static [u8; 16] = b"ZcashSequencHash";
|
||||||
|
const ZCASH_OUTPUTS_HASH_PERSONALIZATION: &'static [u8; 16] = b"ZcashOutputsHash";
|
||||||
|
const ZCASH_JOINSPLITS_HASH_PERSONALIZATION: &'static [u8; 16] = b"ZcashJSplitsHash";
|
||||||
|
|
||||||
|
pub const SIGHASH_ALL: u32 = 1;
|
||||||
|
const SIGHASH_NONE: u32 = 2;
|
||||||
|
const SIGHASH_SINGLE: u32 = 3;
|
||||||
|
const SIGHASH_MASK: u32 = 0x1f;
|
||||||
|
const SIGHASH_ANYONECANPAY: u32 = 0x80;
|
||||||
|
|
||||||
|
macro_rules! update_u32 {
|
||||||
|
($h:expr, $value:expr, $tmp:expr) => {
|
||||||
|
(&mut $tmp[..4]).write_u32::<LittleEndian>($value).unwrap();
|
||||||
|
$h.update(&$tmp[..4]);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! update_hash {
|
||||||
|
($h:expr, $cond:expr, $value:expr) => {
|
||||||
|
if $cond {
|
||||||
|
$h.update(&$value);
|
||||||
|
} else {
|
||||||
|
$h.update(&[0; 32]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
enum SigHashVersion {
|
||||||
|
Sprout,
|
||||||
|
Overwinter,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SigHashVersion {
|
||||||
|
fn from_tx(tx: &Transaction) -> Self {
|
||||||
|
if tx.overwintered {
|
||||||
|
match tx.version_group_id {
|
||||||
|
OVERWINTER_VERSION_GROUP_ID => SigHashVersion::Overwinter,
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SigHashVersion::Sprout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prevout_hash(tx: &Transaction) -> Vec<u8> {
|
||||||
|
let mut data = Vec::with_capacity(tx.vin.len() * 36);
|
||||||
|
for t_in in &tx.vin {
|
||||||
|
t_in.prevout.write(&mut data).unwrap();
|
||||||
|
}
|
||||||
|
let mut h = Blake2b::with_params(32, &[], &[], ZCASH_PREVOUTS_HASH_PERSONALIZATION);
|
||||||
|
h.update(&data);
|
||||||
|
h.finalize().as_ref().to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sequence_hash(tx: &Transaction) -> Vec<u8> {
|
||||||
|
let mut data = Vec::with_capacity(tx.vin.len() * 4);
|
||||||
|
for t_in in &tx.vin {
|
||||||
|
(&mut data)
|
||||||
|
.write_u32::<LittleEndian>(t_in.sequence)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
let mut h = Blake2b::with_params(32, &[], &[], ZCASH_SEQUENCE_HASH_PERSONALIZATION);
|
||||||
|
h.update(&data);
|
||||||
|
h.finalize().as_ref().to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn outputs_hash(tx: &Transaction) -> Vec<u8> {
|
||||||
|
let mut data = Vec::with_capacity(tx.vout.len() * (4 + 1));
|
||||||
|
for t_out in &tx.vout {
|
||||||
|
t_out.write(&mut data).unwrap();
|
||||||
|
}
|
||||||
|
let mut h = Blake2b::with_params(32, &[], &[], ZCASH_OUTPUTS_HASH_PERSONALIZATION);
|
||||||
|
h.update(&data);
|
||||||
|
h.finalize().as_ref().to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn joinsplits_hash(tx: &Transaction) -> Vec<u8> {
|
||||||
|
let mut data = Vec::with_capacity(
|
||||||
|
tx.joinsplits.len() * if tx.version < SAPLING_TX_VERSION {
|
||||||
|
1802 // JSDescription with PHGR13 proof
|
||||||
|
} else {
|
||||||
|
1698 // JSDescription with Groth16 proof
|
||||||
|
},
|
||||||
|
);
|
||||||
|
for js in &tx.joinsplits {
|
||||||
|
js.write(&mut data).unwrap();
|
||||||
|
}
|
||||||
|
data.extend_from_slice(&tx.joinsplit_pubkey);
|
||||||
|
let mut h = Blake2b::with_params(32, &[], &[], ZCASH_JOINSPLITS_HASH_PERSONALIZATION);
|
||||||
|
h.update(&data);
|
||||||
|
h.finalize().as_ref().to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signature_hash(
|
||||||
|
tx: &Transaction,
|
||||||
|
consensus_branch_id: u32,
|
||||||
|
hash_type: u32,
|
||||||
|
transparent_input: Option<(usize, Script, Amount)>,
|
||||||
|
) -> Vec<u8> {
|
||||||
|
let sigversion = SigHashVersion::from_tx(tx);
|
||||||
|
match sigversion {
|
||||||
|
SigHashVersion::Overwinter => {
|
||||||
|
let hash_outputs = if (hash_type & SIGHASH_MASK) != SIGHASH_SINGLE
|
||||||
|
&& (hash_type & SIGHASH_MASK) != SIGHASH_NONE
|
||||||
|
{
|
||||||
|
outputs_hash(tx)
|
||||||
|
} else if (hash_type & SIGHASH_MASK) == SIGHASH_SINGLE
|
||||||
|
&& transparent_input.is_some()
|
||||||
|
&& transparent_input.as_ref().unwrap().0 < tx.vout.len()
|
||||||
|
{
|
||||||
|
let mut data = vec![];
|
||||||
|
tx.vout[transparent_input.as_ref().unwrap().0]
|
||||||
|
.write(&mut data)
|
||||||
|
.unwrap();
|
||||||
|
let mut h = Blake2b::with_params(32, &[], &[], ZCASH_OUTPUTS_HASH_PERSONALIZATION);
|
||||||
|
h.update(&data);
|
||||||
|
h.finalize().as_ref().to_vec()
|
||||||
|
} else {
|
||||||
|
vec![0; 32]
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut personal = [0; 16];
|
||||||
|
(&mut personal[..12]).copy_from_slice(ZCASH_SIGHASH_PERSONALIZATION_PREFIX);
|
||||||
|
(&mut personal[12..])
|
||||||
|
.write_u32::<LittleEndian>(consensus_branch_id)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut h = Blake2b::with_params(32, &[], &[], &personal);
|
||||||
|
let mut tmp = [0; 8];
|
||||||
|
|
||||||
|
update_u32!(h, tx.header(), tmp);
|
||||||
|
update_u32!(h, tx.version_group_id, tmp);
|
||||||
|
update_hash!(h, hash_type & SIGHASH_ANYONECANPAY == 0, prevout_hash(tx));
|
||||||
|
update_hash!(
|
||||||
|
h,
|
||||||
|
hash_type & SIGHASH_ANYONECANPAY == 0
|
||||||
|
&& (hash_type & SIGHASH_MASK) != SIGHASH_SINGLE
|
||||||
|
&& (hash_type & SIGHASH_MASK) != SIGHASH_NONE,
|
||||||
|
sequence_hash(tx)
|
||||||
|
);
|
||||||
|
h.update(&hash_outputs);
|
||||||
|
update_hash!(h, !tx.joinsplits.is_empty(), joinsplits_hash(tx));
|
||||||
|
update_u32!(h, tx.lock_time, tmp);
|
||||||
|
update_u32!(h, tx.expiry_height, tmp);
|
||||||
|
update_u32!(h, hash_type, tmp);
|
||||||
|
|
||||||
|
if let Some((n, script_code, amount)) = transparent_input {
|
||||||
|
let mut data = vec![];
|
||||||
|
tx.vin[n].prevout.write(&mut data).unwrap();
|
||||||
|
script_code.write(&mut data).unwrap();
|
||||||
|
(&mut data).write_i64::<LittleEndian>(amount.0).unwrap();
|
||||||
|
(&mut data)
|
||||||
|
.write_u32::<LittleEndian>(tx.vin[n].sequence)
|
||||||
|
.unwrap();
|
||||||
|
h.update(&data);
|
||||||
|
}
|
||||||
|
|
||||||
|
h.finalize().as_ref().to_vec()
|
||||||
|
}
|
||||||
|
SigHashVersion::Sprout => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user