Add TZEs to transaction format

This commit is contained in:
Jack Grigg 2019-11-07 12:12:45 +00:00 committed by Kris Nuttycombe
parent 90a17b3c31
commit b3332db3e9
4 changed files with 149 additions and 17 deletions

View File

@ -36,6 +36,7 @@ ripemd160 = { version = "0.9", optional = true }
secp256k1 = { version = "0.19", optional = true }
sha2 = "0.9"
subtle = "2.2.1"
zcash_extensions_api = { version = "0.0", path = "../zcash_extensions_api" }
[dev-dependencies]
criterion = "0.3"

View File

@ -3,10 +3,10 @@ use std::io::{self, Read, Write};
const MAX_SIZE: usize = 0x02000000;
struct CompactSize;
pub(crate) struct CompactSize;
impl CompactSize {
fn read<R: Read>(mut reader: R) -> io::Result<usize> {
pub(crate) fn read<R: Read>(mut reader: R) -> io::Result<usize> {
let flag = reader.read_u8()?;
match if flag < 253 {
Ok(flag as usize)
@ -43,7 +43,7 @@ impl CompactSize {
}
}
fn write<W: Write>(mut writer: W, size: usize) -> io::Result<()> {
pub(crate) fn write<W: Write>(mut writer: W, size: usize) -> io::Result<()> {
match size {
s if s < 253 => writer.write_u8(s as u8),
s if s <= 0xFFFF => {

View File

@ -4,9 +4,11 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use ff::PrimeField;
use group::GroupEncoding;
use std::io::{self, Read, Write};
use zcash_extensions_api::transparent as tze;
use crate::legacy::Script;
use crate::redjubjub::{PublicKey, Signature};
use crate::serialize::{CompactSize, Vector};
pub mod amount;
pub use self::amount::Amount;
@ -116,6 +118,79 @@ impl TxOut {
}
}
#[derive(Debug)]
pub struct TzeIn {
pub prevout: OutPoint,
pub witness: tze::Witness,
}
impl TzeIn {
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
let prevout = OutPoint::read(&mut reader)?;
let extension_id = CompactSize::read(&mut reader)?;
let mode = CompactSize::read(&mut reader)?;
let payload = Vector::read(&mut reader, |r| r.read_u8())?;
Ok(TzeIn {
prevout,
witness: tze::Witness {
extension_id,
mode,
payload,
},
})
}
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
self.prevout.write(&mut writer)?;
CompactSize::write(&mut writer, self.witness.extension_id)?;
CompactSize::write(&mut writer, self.witness.mode)?;
Vector::write(&mut writer, &self.witness.payload, |w, b| w.write_u8(*b))
}
}
#[derive(Debug)]
pub struct TzeOut {
pub value: Amount,
pub precondition: tze::Precondition,
}
impl TzeOut {
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
let value = {
let mut tmp = [0; 8];
reader.read_exact(&mut tmp)?;
Amount::from_nonnegative_i64_le_bytes(tmp)
}
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "value out of range"))?;
let extension_id = CompactSize::read(&mut reader)?;
let mode = CompactSize::read(&mut reader)?;
let payload = Vector::read(&mut reader, |r| r.read_u8())?;
Ok(TzeOut {
value,
precondition: tze::Precondition {
extension_id,
mode,
payload,
},
})
}
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
writer.write_all(&self.value.to_i64_le_bytes())?;
CompactSize::write(&mut writer, self.precondition.extension_id)?;
CompactSize::write(&mut writer, self.precondition.mode)?;
Vector::write(&mut writer, &self.precondition.payload, |w, b| {
w.write_u8(*b)
})
}
}
pub struct SpendDescription {
pub cv: jubjub::ExtendedPoint,
pub anchor: bls12_381::Scalar,

View File

@ -19,12 +19,16 @@ mod tests;
pub use self::sighash::{signature_hash, signature_hash_data, SIGHASH_ALL};
use self::components::{Amount, JSDescription, OutputDescription, SpendDescription, TxIn, TxOut};
use self::components::{
Amount, JSDescription, OutputDescription, SpendDescription, TxIn, TxOut, TzeIn, TzeOut,
};
const OVERWINTER_VERSION_GROUP_ID: u32 = 0x03C48270;
const OVERWINTER_TX_VERSION: u32 = 3;
const SAPLING_VERSION_GROUP_ID: u32 = 0x892F2085;
const SAPLING_TX_VERSION: u32 = 4;
const FUTURE_VERSION_GROUP_ID: u32 = 0xFFFFFFFF;
const FUTURE_TX_VERSION: u32 = 0x0000FFFF;
#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct TxId(pub [u8; 32]);
@ -64,6 +68,8 @@ pub struct TransactionData {
pub version_group_id: u32,
pub vin: Vec<TxIn>,
pub vout: Vec<TxOut>,
pub tze_inputs: Vec<TzeIn>,
pub tze_outputs: Vec<TzeOut>,
pub lock_time: u32,
pub expiry_height: u32,
pub value_balance: Amount,
@ -85,6 +91,8 @@ impl std::fmt::Debug for TransactionData {
version_group_id = {:?},
vin = {:?},
vout = {:?},
tze_inputs = {:?},
tze_outputs = {:?},
lock_time = {:?},
expiry_height = {:?},
value_balance = {:?},
@ -98,6 +106,8 @@ impl std::fmt::Debug for TransactionData {
self.version_group_id,
self.vin,
self.vout,
self.tze_inputs,
self.tze_outputs,
self.lock_time,
self.expiry_height,
self.value_balance,
@ -118,6 +128,29 @@ impl TransactionData {
version_group_id: SAPLING_VERSION_GROUP_ID,
vin: vec![],
vout: vec![],
tze_inputs: vec![],
tze_outputs: vec![],
lock_time: 0,
expiry_height: 0,
value_balance: Amount::zero(),
shielded_spends: vec![],
shielded_outputs: vec![],
joinsplits: vec![],
joinsplit_pubkey: None,
joinsplit_sig: None,
binding_sig: None,
}
}
pub fn nu4() -> Self {
TransactionData {
overwintered: true,
version: FUTURE_TX_VERSION,
version_group_id: FUTURE_VERSION_GROUP_ID,
vin: vec![],
vout: vec![],
tze_inputs: vec![],
tze_outputs: vec![],
lock_time: 0,
expiry_height: 0,
value_balance: Amount::zero(),
@ -178,7 +211,11 @@ impl Transaction {
let is_sapling_v4 = overwintered
&& version_group_id == SAPLING_VERSION_GROUP_ID
&& version == SAPLING_TX_VERSION;
if overwintered && !(is_overwinter_v3 || is_sapling_v4) {
let has_tze = overwintered
&& version_group_id == FUTURE_VERSION_GROUP_ID
&& version == FUTURE_TX_VERSION;
if overwintered && !(is_overwinter_v3 || is_sapling_v4 || has_tze) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Unknown transaction format",
@ -187,14 +224,21 @@ impl Transaction {
let vin = Vector::read(&mut reader, TxIn::read)?;
let vout = Vector::read(&mut reader, TxOut::read)?;
let (tze_inputs, tze_outputs) = if has_tze {
let wi = Vector::read(&mut reader, TzeIn::read)?;
let wo = Vector::read(&mut reader, TzeOut::read)?;
(wi, wo)
} else {
(vec![], vec![])
};
let lock_time = reader.read_u32::<LittleEndian>()?;
let expiry_height = if is_overwinter_v3 || is_sapling_v4 {
let expiry_height = if is_overwinter_v3 || is_sapling_v4 || has_tze {
reader.read_u32::<LittleEndian>()?
} else {
0
};
let (value_balance, shielded_spends, shielded_outputs) = if is_sapling_v4 {
let (value_balance, shielded_spends, shielded_outputs) = if is_sapling_v4 || has_tze {
let vb = {
let mut tmp = [0; 8];
reader.read_exact(&mut tmp)?;
@ -226,12 +270,13 @@ impl Transaction {
(vec![], None, None)
};
let binding_sig =
if is_sapling_v4 && !(shielded_spends.is_empty() && shielded_outputs.is_empty()) {
Some(Signature::read(&mut reader)?)
} else {
None
};
let binding_sig = if (is_sapling_v4 || has_tze)
&& !(shielded_spends.is_empty() && shielded_outputs.is_empty())
{
Some(Signature::read(&mut reader)?)
} else {
None
};
Transaction::from_data(TransactionData {
overwintered,
@ -239,6 +284,8 @@ impl Transaction {
version_group_id,
vin,
vout,
tze_inputs,
tze_outputs,
lock_time,
expiry_height,
value_balance,
@ -263,7 +310,10 @@ impl Transaction {
let is_sapling_v4 = self.overwintered
&& self.version_group_id == SAPLING_VERSION_GROUP_ID
&& self.version == SAPLING_TX_VERSION;
if self.overwintered && !(is_overwinter_v3 || is_sapling_v4) {
let is_nu4_v5 = self.overwintered
&& self.version_group_id == FUTURE_VERSION_GROUP_ID
&& self.version == FUTURE_TX_VERSION;
if self.overwintered && !(is_overwinter_v3 || is_sapling_v4 || is_nu4_v5) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Unknown transaction format",
@ -272,12 +322,16 @@ impl Transaction {
Vector::write(&mut writer, &self.vin, |w, e| e.write(w))?;
Vector::write(&mut writer, &self.vout, |w, e| e.write(w))?;
if is_nu4_v5 {
Vector::write(&mut writer, &self.tze_inputs, |w, e| e.write(w))?;
Vector::write(&mut writer, &self.tze_outputs, |w, e| e.write(w))?;
}
writer.write_u32::<LittleEndian>(self.lock_time)?;
if is_overwinter_v3 || is_sapling_v4 {
if is_overwinter_v3 || is_sapling_v4 || is_nu4_v5 {
writer.write_u32::<LittleEndian>(self.expiry_height)?;
}
if is_sapling_v4 {
if is_sapling_v4 || is_nu4_v5 {
writer.write_all(&self.value_balance.to_i64_le_bytes())?;
Vector::write(&mut writer, &self.shielded_spends, |w, e| e.write(w))?;
Vector::write(&mut writer, &self.shielded_outputs, |w, e| e.write(w))?;
@ -322,7 +376,9 @@ impl Transaction {
}
}
if is_sapling_v4 && !(self.shielded_spends.is_empty() && self.shielded_outputs.is_empty()) {
if (is_sapling_v4 || is_nu4_v5)
&& !(self.shielded_spends.is_empty() && self.shielded_outputs.is_empty())
{
match self.binding_sig {
Some(sig) => sig.write(&mut writer)?,
None => {