Add TZEs to transaction format
This commit is contained in:
parent
90a17b3c31
commit
b3332db3e9
|
@ -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"
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,8 +270,9 @@ impl Transaction {
|
|||
(vec![], None, None)
|
||||
};
|
||||
|
||||
let binding_sig =
|
||||
if is_sapling_v4 && !(shielded_spends.is_empty() && shielded_outputs.is_empty()) {
|
||||
let binding_sig = if (is_sapling_v4 || has_tze)
|
||||
&& !(shielded_spends.is_empty() && shielded_outputs.is_empty())
|
||||
{
|
||||
Some(Signature::read(&mut reader)?)
|
||||
} else {
|
||||
None
|
||||
|
@ -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 => {
|
||||
|
|
Loading…
Reference in New Issue