Add transaction version 5 stubs (#1824)
* add transaction V5 stub * add v5_strategy * deduplicate version group ids * Update comment for V5 transactions * Add V5 transactions to non_finalized_state Currently these are all `unimplemented!(...)` * Fix struct matches * Apply trivial panic message changes * add zcash_deserialize for V5 * make all tx versions explicit in sprout and sapling nullifier functions * match exhaustively in sprout and sapling nullifier functions * fix matches in zebra-consensus * fix NU5 strategy * We're still deciding if v5 transactions support Sprout Co-authored-by: teor <teor@riseup.net>
This commit is contained in:
parent
65fa1c6bd9
commit
8883543a85
|
|
@ -15,10 +15,12 @@
|
||||||
mod genesis;
|
mod genesis;
|
||||||
mod network;
|
mod network;
|
||||||
mod network_upgrade;
|
mod network_upgrade;
|
||||||
|
mod transaction;
|
||||||
|
|
||||||
pub use genesis::*;
|
pub use genesis::*;
|
||||||
pub use network::Network;
|
pub use network::Network;
|
||||||
pub use network_upgrade::*;
|
pub use network_upgrade::*;
|
||||||
|
pub use transaction::*;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
//! Transaction consensus and utility parameters.
|
||||||
|
|
||||||
|
/// The version group ID for Overwinter transactions.
|
||||||
|
pub const OVERWINTER_VERSION_GROUP_ID: u32 = 0x03C4_8270;
|
||||||
|
|
||||||
|
/// The version group ID for Sapling transactions.
|
||||||
|
pub const SAPLING_VERSION_GROUP_ID: u32 = 0x892F_2085;
|
||||||
|
|
||||||
|
/// The version group ID for version 5 transactions.
|
||||||
|
///
|
||||||
|
/// Orchard transactions must use transaction version 5 and this version
|
||||||
|
/// group ID. Sapling transactions can use v4 or v5 transactions.
|
||||||
|
pub const TX_V5_VERSION_GROUP_ID: u32 = 0x26A7_270A;
|
||||||
|
|
@ -42,7 +42,7 @@ use crate::{
|
||||||
/// internally by different enum variants. Because we checkpoint on Sapling
|
/// internally by different enum variants. Because we checkpoint on Sapling
|
||||||
/// activation, we do not validate any pre-Sapling transaction types.
|
/// activation, we do not validate any pre-Sapling transaction types.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
// XXX consider boxing the Optional fields of V4 txs
|
// XXX consider boxing the Optional fields of V4 and V5 txs
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum Transaction {
|
pub enum Transaction {
|
||||||
/// A fully transparent transaction (`version = 1`).
|
/// A fully transparent transaction (`version = 1`).
|
||||||
|
|
@ -99,6 +99,22 @@ pub enum Transaction {
|
||||||
/// The shielded data for this transaction, if any.
|
/// The shielded data for this transaction, if any.
|
||||||
shielded_data: Option<ShieldedData>,
|
shielded_data: Option<ShieldedData>,
|
||||||
},
|
},
|
||||||
|
/// A `version = 5` transaction, which supports `Sapling` and `Orchard`.
|
||||||
|
// TODO: does this transaction type support `Sprout`?
|
||||||
|
// Check for ZIP-225 updates after the decision on 2021-03-05.
|
||||||
|
V5 {
|
||||||
|
/// The earliest time or block height that this transaction can be added to the
|
||||||
|
/// chain.
|
||||||
|
lock_time: LockTime,
|
||||||
|
/// The latest block height that this transaction can be added to the chain.
|
||||||
|
expiry_height: block::Height,
|
||||||
|
/// The transparent inputs to the transaction.
|
||||||
|
inputs: Vec<transparent::Input>,
|
||||||
|
/// The transparent outputs from the transaction.
|
||||||
|
outputs: Vec<transparent::Output>,
|
||||||
|
/// The rest of the transaction as bytes
|
||||||
|
rest: Vec<u8>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transaction {
|
impl Transaction {
|
||||||
|
|
@ -114,6 +130,7 @@ impl Transaction {
|
||||||
Transaction::V2 { ref inputs, .. } => inputs,
|
Transaction::V2 { ref inputs, .. } => inputs,
|
||||||
Transaction::V3 { ref inputs, .. } => inputs,
|
Transaction::V3 { ref inputs, .. } => inputs,
|
||||||
Transaction::V4 { ref inputs, .. } => inputs,
|
Transaction::V4 { ref inputs, .. } => inputs,
|
||||||
|
Transaction::V5 { ref inputs, .. } => inputs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -124,6 +141,7 @@ impl Transaction {
|
||||||
Transaction::V2 { ref outputs, .. } => outputs,
|
Transaction::V2 { ref outputs, .. } => outputs,
|
||||||
Transaction::V3 { ref outputs, .. } => outputs,
|
Transaction::V3 { ref outputs, .. } => outputs,
|
||||||
Transaction::V4 { ref outputs, .. } => outputs,
|
Transaction::V4 { ref outputs, .. } => outputs,
|
||||||
|
Transaction::V5 { ref outputs, .. } => outputs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,6 +152,7 @@ impl Transaction {
|
||||||
Transaction::V2 { lock_time, .. } => *lock_time,
|
Transaction::V2 { lock_time, .. } => *lock_time,
|
||||||
Transaction::V3 { lock_time, .. } => *lock_time,
|
Transaction::V3 { lock_time, .. } => *lock_time,
|
||||||
Transaction::V4 { lock_time, .. } => *lock_time,
|
Transaction::V4 { lock_time, .. } => *lock_time,
|
||||||
|
Transaction::V5 { lock_time, .. } => *lock_time,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,6 +163,7 @@ impl Transaction {
|
||||||
Transaction::V2 { .. } => None,
|
Transaction::V2 { .. } => None,
|
||||||
Transaction::V3 { expiry_height, .. } => Some(*expiry_height),
|
Transaction::V3 { expiry_height, .. } => Some(*expiry_height),
|
||||||
Transaction::V4 { expiry_height, .. } => Some(*expiry_height),
|
Transaction::V4 { expiry_height, .. } => Some(*expiry_height),
|
||||||
|
Transaction::V5 { expiry_height, .. } => Some(*expiry_height),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -174,8 +194,26 @@ impl Transaction {
|
||||||
.joinsplits()
|
.joinsplits()
|
||||||
.flat_map(|joinsplit| joinsplit.nullifiers.iter()),
|
.flat_map(|joinsplit| joinsplit.nullifiers.iter()),
|
||||||
),
|
),
|
||||||
|
// Maybe JoinSplits, maybe not, we're still deciding
|
||||||
|
Transaction::V5 { .. } => {
|
||||||
|
unimplemented!(
|
||||||
|
"v5 transaction format as specified in ZIP-225 after decision on 2021-03-12"
|
||||||
|
)
|
||||||
|
}
|
||||||
// No JoinSplits
|
// No JoinSplits
|
||||||
_ => Box::new(std::iter::empty()),
|
Transaction::V1 { .. }
|
||||||
|
| Transaction::V2 {
|
||||||
|
joinsplit_data: None,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| Transaction::V3 {
|
||||||
|
joinsplit_data: None,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| Transaction::V4 {
|
||||||
|
joinsplit_data: None,
|
||||||
|
..
|
||||||
|
} => Box::new(std::iter::empty()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -189,8 +227,17 @@ impl Transaction {
|
||||||
shielded_data: Some(shielded_data),
|
shielded_data: Some(shielded_data),
|
||||||
..
|
..
|
||||||
} => Box::new(shielded_data.nullifiers()),
|
} => Box::new(shielded_data.nullifiers()),
|
||||||
|
Transaction::V5 { .. } => {
|
||||||
|
unimplemented!("v5 transaction format as specified in ZIP-225")
|
||||||
|
}
|
||||||
// No JoinSplits
|
// No JoinSplits
|
||||||
_ => Box::new(std::iter::empty()),
|
Transaction::V1 { .. }
|
||||||
|
| Transaction::V2 { .. }
|
||||||
|
| Transaction::V3 { .. }
|
||||||
|
| Transaction::V4 {
|
||||||
|
shielded_data: None,
|
||||||
|
..
|
||||||
|
} => Box::new(std::iter::empty()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,26 @@ impl Transaction {
|
||||||
)
|
)
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
/// Generate a proptest strategy for V5 Transactions
|
||||||
|
pub fn v5_strategy(ledger_state: LedgerState) -> BoxedStrategy<Self> {
|
||||||
|
(
|
||||||
|
any::<LockTime>(),
|
||||||
|
any::<block::Height>(),
|
||||||
|
transparent::Input::vec_strategy(ledger_state, 10),
|
||||||
|
vec(any::<transparent::Output>(), 0..10),
|
||||||
|
any::<Vec<u8>>(),
|
||||||
|
)
|
||||||
|
.prop_map(
|
||||||
|
|(lock_time, expiry_height, inputs, outputs, rest)| Transaction::V5 {
|
||||||
|
lock_time,
|
||||||
|
expiry_height,
|
||||||
|
inputs,
|
||||||
|
outputs,
|
||||||
|
rest,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
/// Proptest Strategy for creating a Vector of transactions where the first
|
/// Proptest Strategy for creating a Vector of transactions where the first
|
||||||
/// transaction is always the only coinbase transaction
|
/// transaction is always the only coinbase transaction
|
||||||
|
|
@ -236,9 +256,11 @@ impl Arbitrary for Transaction {
|
||||||
NetworkUpgrade::Blossom | NetworkUpgrade::Heartwood | NetworkUpgrade::Canopy => {
|
NetworkUpgrade::Blossom | NetworkUpgrade::Heartwood | NetworkUpgrade::Canopy => {
|
||||||
Self::v4_strategy(ledger_state)
|
Self::v4_strategy(ledger_state)
|
||||||
}
|
}
|
||||||
NetworkUpgrade::NU5 => {
|
NetworkUpgrade::NU5 => prop_oneof![
|
||||||
unimplemented!("NU5 upgrade can use v4 or v5 transactions, as specified in ZIP-225")
|
Self::v4_strategy(ledger_state),
|
||||||
}
|
Self::v5_strategy(ledger_state)
|
||||||
|
]
|
||||||
|
.boxed(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use std::{io, sync::Arc};
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
parameters::{OVERWINTER_VERSION_GROUP_ID, SAPLING_VERSION_GROUP_ID, TX_V5_VERSION_GROUP_ID},
|
||||||
primitives::ZkSnarkProof,
|
primitives::ZkSnarkProof,
|
||||||
serialization::{
|
serialization::{
|
||||||
ReadZcashExt, SerializationError, WriteZcashExt, ZcashDeserialize, ZcashDeserializeInto,
|
ReadZcashExt, SerializationError, WriteZcashExt, ZcashDeserialize, ZcashDeserializeInto,
|
||||||
|
|
@ -16,9 +17,6 @@ use crate::{
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const OVERWINTER_VERSION_GROUP_ID: u32 = 0x03C4_8270;
|
|
||||||
const SAPLING_VERSION_GROUP_ID: u32 = 0x892F_2085;
|
|
||||||
|
|
||||||
impl ZcashDeserialize for jubjub::Fq {
|
impl ZcashDeserialize for jubjub::Fq {
|
||||||
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
|
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
|
||||||
let possible_scalar = jubjub::Fq::from_bytes(&reader.read_32_bytes()?);
|
let possible_scalar = jubjub::Fq::from_bytes(&reader.read_32_bytes()?);
|
||||||
|
|
@ -182,6 +180,23 @@ impl ZcashSerialize for Transaction {
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Transaction::V5 {
|
||||||
|
lock_time,
|
||||||
|
expiry_height,
|
||||||
|
inputs,
|
||||||
|
outputs,
|
||||||
|
rest,
|
||||||
|
} => {
|
||||||
|
// Write version 5 and set the fOverwintered bit.
|
||||||
|
writer.write_u32::<LittleEndian>(5 | (1 << 31))?;
|
||||||
|
writer.write_u32::<LittleEndian>(TX_V5_VERSION_GROUP_ID)?;
|
||||||
|
lock_time.zcash_serialize(&mut writer)?;
|
||||||
|
writer.write_u32::<LittleEndian>(expiry_height.0)?;
|
||||||
|
inputs.zcash_serialize(&mut writer)?;
|
||||||
|
outputs.zcash_serialize(&mut writer)?;
|
||||||
|
// write the rest
|
||||||
|
writer.write_all(rest)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -285,6 +300,26 @@ impl ZcashDeserialize for Transaction {
|
||||||
joinsplit_data,
|
joinsplit_data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
(5, false) => {
|
||||||
|
let id = reader.read_u32::<LittleEndian>()?;
|
||||||
|
if id != TX_V5_VERSION_GROUP_ID {
|
||||||
|
return Err(SerializationError::Parse("expected TX_V5_VERSION_GROUP_ID"));
|
||||||
|
}
|
||||||
|
let lock_time = LockTime::zcash_deserialize(&mut reader)?;
|
||||||
|
let expiry_height = block::Height(reader.read_u32::<LittleEndian>()?);
|
||||||
|
let inputs = Vec::zcash_deserialize(&mut reader)?;
|
||||||
|
let outputs = Vec::zcash_deserialize(&mut reader)?;
|
||||||
|
let mut rest = Vec::new();
|
||||||
|
reader.read_to_end(&mut rest)?;
|
||||||
|
|
||||||
|
Ok(Transaction::V5 {
|
||||||
|
lock_time,
|
||||||
|
expiry_height,
|
||||||
|
inputs,
|
||||||
|
outputs,
|
||||||
|
rest,
|
||||||
|
})
|
||||||
|
}
|
||||||
(_, _) => Err(SerializationError::Parse("bad tx header")),
|
(_, _) => Err(SerializationError::Parse("bad tx header")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
use super::Transaction;
|
use super::Transaction;
|
||||||
use crate::{
|
use crate::{
|
||||||
parameters::{ConsensusBranchId, NetworkUpgrade},
|
parameters::{
|
||||||
|
ConsensusBranchId, NetworkUpgrade, OVERWINTER_VERSION_GROUP_ID, SAPLING_VERSION_GROUP_ID,
|
||||||
|
TX_V5_VERSION_GROUP_ID,
|
||||||
|
},
|
||||||
serialization::{WriteZcashExt, ZcashSerialize},
|
serialization::{WriteZcashExt, ZcashSerialize},
|
||||||
transparent,
|
transparent,
|
||||||
};
|
};
|
||||||
|
|
@ -12,9 +15,6 @@ use std::io;
|
||||||
static ZIP143_EXPLANATION: &str = "Invalid transaction version: after Overwinter activation transaction versions 1 and 2 are rejected";
|
static ZIP143_EXPLANATION: &str = "Invalid transaction version: after Overwinter activation transaction versions 1 and 2 are rejected";
|
||||||
static ZIP243_EXPLANATION: &str = "Invalid transaction version: after Sapling activation transaction versions 1, 2, and 3 are rejected";
|
static ZIP243_EXPLANATION: &str = "Invalid transaction version: after Sapling activation transaction versions 1, 2, and 3 are rejected";
|
||||||
|
|
||||||
const OVERWINTER_VERSION_GROUP_ID: u32 = 0x03C4_8270;
|
|
||||||
const SAPLING_VERSION_GROUP_ID: u32 = 0x892F_2085;
|
|
||||||
|
|
||||||
const ZCASH_SIGHASH_PERSONALIZATION_PREFIX: &[u8; 12] = b"ZcashSigHash";
|
const ZCASH_SIGHASH_PERSONALIZATION_PREFIX: &[u8; 12] = b"ZcashSigHash";
|
||||||
const ZCASH_PREVOUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashPrevoutHash";
|
const ZCASH_PREVOUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashPrevoutHash";
|
||||||
const ZCASH_SEQUENCE_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashSequencHash";
|
const ZCASH_SEQUENCE_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashSequencHash";
|
||||||
|
|
@ -129,6 +129,7 @@ impl<'a> SigHasher<'a> {
|
||||||
Transaction::V1 { .. } | Transaction::V2 { .. } => unreachable!(ZIP143_EXPLANATION),
|
Transaction::V1 { .. } | Transaction::V2 { .. } => unreachable!(ZIP143_EXPLANATION),
|
||||||
Transaction::V3 { .. } => 3 | overwintered_flag,
|
Transaction::V3 { .. } => 3 | overwintered_flag,
|
||||||
Transaction::V4 { .. } => 4 | overwintered_flag,
|
Transaction::V4 { .. } => 4 | overwintered_flag,
|
||||||
|
Transaction::V5 { .. } => 5 | overwintered_flag,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,6 +138,7 @@ impl<'a> SigHasher<'a> {
|
||||||
Transaction::V1 { .. } | Transaction::V2 { .. } => unreachable!(ZIP143_EXPLANATION),
|
Transaction::V1 { .. } | Transaction::V2 { .. } => unreachable!(ZIP143_EXPLANATION),
|
||||||
Transaction::V3 { .. } => OVERWINTER_VERSION_GROUP_ID,
|
Transaction::V3 { .. } => OVERWINTER_VERSION_GROUP_ID,
|
||||||
Transaction::V4 { .. } => SAPLING_VERSION_GROUP_ID,
|
Transaction::V4 { .. } => SAPLING_VERSION_GROUP_ID,
|
||||||
|
Transaction::V5 { .. } => TX_V5_VERSION_GROUP_ID,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -243,6 +245,9 @@ impl<'a> SigHasher<'a> {
|
||||||
Transaction::V1 { .. } | Transaction::V2 { .. } => unreachable!(ZIP143_EXPLANATION),
|
Transaction::V1 { .. } | Transaction::V2 { .. } => unreachable!(ZIP143_EXPLANATION),
|
||||||
Transaction::V3 { joinsplit_data, .. } => joinsplit_data.is_some(),
|
Transaction::V3 { joinsplit_data, .. } => joinsplit_data.is_some(),
|
||||||
Transaction::V4 { joinsplit_data, .. } => joinsplit_data.is_some(),
|
Transaction::V4 { joinsplit_data, .. } => joinsplit_data.is_some(),
|
||||||
|
Transaction::V5 { .. } => {
|
||||||
|
unimplemented!("v5 transaction hash as specified in ZIP-225 and ZIP-244")
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if !has_joinsplits {
|
if !has_joinsplits {
|
||||||
|
|
@ -256,7 +261,7 @@ impl<'a> SigHasher<'a> {
|
||||||
|
|
||||||
// This code, and the check above for has_joinsplits cannot be combined
|
// This code, and the check above for has_joinsplits cannot be combined
|
||||||
// into a single branch because the `joinsplit_data` type of each
|
// into a single branch because the `joinsplit_data` type of each
|
||||||
// tranaction kind has a different type.
|
// transaction kind has a different type.
|
||||||
//
|
//
|
||||||
// For v3 joinsplit_data is a JoinSplitData<Bctv14Proof>
|
// For v3 joinsplit_data is a JoinSplitData<Bctv14Proof>
|
||||||
// For v4 joinsplit_data is a JoinSplitData<Groth16Proof>
|
// For v4 joinsplit_data is a JoinSplitData<Groth16Proof>
|
||||||
|
|
@ -264,6 +269,8 @@ impl<'a> SigHasher<'a> {
|
||||||
// The type parameter on these types prevents them from being unified,
|
// The type parameter on these types prevents them from being unified,
|
||||||
// which forces us to duplicate the logic in each branch even though the
|
// which forces us to duplicate the logic in each branch even though the
|
||||||
// code within each branch is identical.
|
// code within each branch is identical.
|
||||||
|
//
|
||||||
|
// TODO: use a generic function to remove the duplicate code
|
||||||
match self.trans {
|
match self.trans {
|
||||||
Transaction::V3 {
|
Transaction::V3 {
|
||||||
joinsplit_data: Some(jsd),
|
joinsplit_data: Some(jsd),
|
||||||
|
|
@ -283,7 +290,20 @@ impl<'a> SigHasher<'a> {
|
||||||
}
|
}
|
||||||
(&mut hash).write_all(&<[u8; 32]>::from(jsd.pub_key)[..])?;
|
(&mut hash).write_all(&<[u8; 32]>::from(jsd.pub_key)[..])?;
|
||||||
}
|
}
|
||||||
_ => unreachable!("already checked transaction kind above"),
|
Transaction::V5 { .. } => {
|
||||||
|
unimplemented!("v5 transaction hash as specified in ZIP-225 and ZIP-244")
|
||||||
|
}
|
||||||
|
|
||||||
|
Transaction::V1 { .. }
|
||||||
|
| Transaction::V2 { .. }
|
||||||
|
| Transaction::V3 {
|
||||||
|
joinsplit_data: None,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| Transaction::V4 {
|
||||||
|
joinsplit_data: None,
|
||||||
|
..
|
||||||
|
} => unreachable!("already checked transaction kind above"),
|
||||||
};
|
};
|
||||||
|
|
||||||
writer.write_all(hash.finalize().as_ref())
|
writer.write_all(hash.finalize().as_ref())
|
||||||
|
|
@ -398,14 +418,15 @@ impl<'a> SigHasher<'a> {
|
||||||
use Transaction::*;
|
use Transaction::*;
|
||||||
|
|
||||||
let shielded_data = match self.trans {
|
let shielded_data = match self.trans {
|
||||||
Transaction::V4 {
|
V4 {
|
||||||
shielded_data: Some(shielded_data),
|
shielded_data: Some(shielded_data),
|
||||||
..
|
..
|
||||||
} => shielded_data,
|
} => shielded_data,
|
||||||
Transaction::V4 {
|
V4 {
|
||||||
shielded_data: None,
|
shielded_data: None,
|
||||||
..
|
..
|
||||||
} => return writer.write_all(&[0; 32]),
|
} => return writer.write_all(&[0; 32]),
|
||||||
|
V5 { .. } => unimplemented!("v5 transaction hash as specified in ZIP-225 and ZIP-244"),
|
||||||
V1 { .. } | V2 { .. } | V3 { .. } => unreachable!(ZIP243_EXPLANATION),
|
V1 { .. } | V2 { .. } | V3 { .. } => unreachable!(ZIP243_EXPLANATION),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -434,14 +455,15 @@ impl<'a> SigHasher<'a> {
|
||||||
use Transaction::*;
|
use Transaction::*;
|
||||||
|
|
||||||
let shielded_data = match self.trans {
|
let shielded_data = match self.trans {
|
||||||
Transaction::V4 {
|
V4 {
|
||||||
shielded_data: Some(shielded_data),
|
shielded_data: Some(shielded_data),
|
||||||
..
|
..
|
||||||
} => shielded_data,
|
} => shielded_data,
|
||||||
Transaction::V4 {
|
V4 {
|
||||||
shielded_data: None,
|
shielded_data: None,
|
||||||
..
|
..
|
||||||
} => return writer.write_all(&[0; 32]),
|
} => return writer.write_all(&[0; 32]),
|
||||||
|
V5 { .. } => unimplemented!("v5 transaction hash as specified in ZIP-225 and ZIP-244"),
|
||||||
V1 { .. } | V2 { .. } | V3 { .. } => unreachable!(ZIP243_EXPLANATION),
|
V1 { .. } | V2 { .. } | V3 { .. } => unreachable!(ZIP243_EXPLANATION),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -465,7 +487,8 @@ impl<'a> SigHasher<'a> {
|
||||||
use Transaction::*;
|
use Transaction::*;
|
||||||
|
|
||||||
let value_balance = match self.trans {
|
let value_balance = match self.trans {
|
||||||
Transaction::V4 { value_balance, .. } => value_balance,
|
V4 { value_balance, .. } => value_balance,
|
||||||
|
V5 { .. } => unimplemented!("v5 transaction hash as specified in ZIP-225 and ZIP-244"),
|
||||||
V1 { .. } | V2 { .. } | V3 { .. } => unreachable!(ZIP243_EXPLANATION),
|
V1 { .. } | V2 { .. } | V3 { .. } => unreachable!(ZIP243_EXPLANATION),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -258,6 +258,9 @@ where
|
||||||
|
|
||||||
Ok(tx.hash())
|
Ok(tx.hash())
|
||||||
}
|
}
|
||||||
|
Transaction::V5 { .. } => {
|
||||||
|
unimplemented!("v5 transaction validation as specified in ZIP-216, ZIP-224, ZIP-225, and ZIP-244")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.instrument(span)
|
.instrument(span)
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,9 @@ pub fn has_inputs_and_outputs(tx: &Transaction) -> Result<(), TransactionError>
|
||||||
Transaction::V1 { .. } | Transaction::V2 { .. } | Transaction::V3 { .. } => {
|
Transaction::V1 { .. } | Transaction::V2 { .. } | Transaction::V3 { .. } => {
|
||||||
unreachable!("tx version is checked first")
|
unreachable!("tx version is checked first")
|
||||||
}
|
}
|
||||||
|
Transaction::V5 { .. } => {
|
||||||
|
unimplemented!("v5 transaction format as specified in ZIP-225")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,6 +117,10 @@ pub fn coinbase_tx_no_joinsplit_or_spend(tx: &Transaction) -> Result<(), Transac
|
||||||
Transaction::V1 { .. } | Transaction::V2 { .. } | Transaction::V3 { .. } => {
|
Transaction::V1 { .. } | Transaction::V2 { .. } | Transaction::V3 { .. } => {
|
||||||
unreachable!("tx version is checked first")
|
unreachable!("tx version is checked first")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Transaction::V5 { .. } => {
|
||||||
|
unimplemented!("v5 coinbase validation as specified in ZIP-225 and the draft spec")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -165,14 +165,16 @@ impl UpdateWith<PreparedBlock> for Chain {
|
||||||
.zip(transaction_hashes.iter().cloned())
|
.zip(transaction_hashes.iter().cloned())
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
|
use transaction::Transaction::*;
|
||||||
let (inputs, shielded_data, joinsplit_data) = match transaction.deref() {
|
let (inputs, shielded_data, joinsplit_data) = match transaction.deref() {
|
||||||
transaction::Transaction::V4 {
|
V4 {
|
||||||
inputs,
|
inputs,
|
||||||
shielded_data,
|
shielded_data,
|
||||||
joinsplit_data,
|
joinsplit_data,
|
||||||
..
|
..
|
||||||
} => (inputs, shielded_data, joinsplit_data),
|
} => (inputs, shielded_data, joinsplit_data),
|
||||||
_ => unreachable!(
|
V5 { .. } => unimplemented!("v5 transaction format as specified in ZIP-225"),
|
||||||
|
V1 { .. } | V2 { .. } | V3 { .. } => unreachable!(
|
||||||
"older transaction versions only exist in finalized blocks pre sapling",
|
"older transaction versions only exist in finalized blocks pre sapling",
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
@ -223,14 +225,16 @@ impl UpdateWith<PreparedBlock> for Chain {
|
||||||
for (transaction, transaction_hash) in
|
for (transaction, transaction_hash) in
|
||||||
block.transactions.iter().zip(transaction_hashes.iter())
|
block.transactions.iter().zip(transaction_hashes.iter())
|
||||||
{
|
{
|
||||||
|
use transaction::Transaction::*;
|
||||||
let (inputs, shielded_data, joinsplit_data) = match transaction.deref() {
|
let (inputs, shielded_data, joinsplit_data) = match transaction.deref() {
|
||||||
transaction::Transaction::V4 {
|
V4 {
|
||||||
inputs,
|
inputs,
|
||||||
shielded_data,
|
shielded_data,
|
||||||
joinsplit_data,
|
joinsplit_data,
|
||||||
..
|
..
|
||||||
} => (inputs, shielded_data, joinsplit_data),
|
} => (inputs, shielded_data, joinsplit_data),
|
||||||
_ => unreachable!(
|
V5 { .. } => unimplemented!("v5 transaction format as specified in ZIP-225"),
|
||||||
|
V1 { .. } | V2 { .. } | V3 { .. } => unreachable!(
|
||||||
"older transaction versions only exist in finalized blocks pre sapling",
|
"older transaction versions only exist in finalized blocks pre sapling",
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ impl FakeChainHelper for Arc<Block> {
|
||||||
Transaction::V2 { inputs, .. } => &mut inputs[0],
|
Transaction::V2 { inputs, .. } => &mut inputs[0],
|
||||||
Transaction::V3 { inputs, .. } => &mut inputs[0],
|
Transaction::V3 { inputs, .. } => &mut inputs[0],
|
||||||
Transaction::V4 { inputs, .. } => &mut inputs[0],
|
Transaction::V4 { inputs, .. } => &mut inputs[0],
|
||||||
|
Transaction::V5 { inputs, .. } => &mut inputs[0],
|
||||||
};
|
};
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue