chain: move LockTime into transaction
This commit is contained in:
parent
8e9a239687
commit
5f71bcd0d1
|
|
@ -6,7 +6,7 @@ use crate::merkle_tree::MerkleTreeRootHash;
|
||||||
use crate::serialization::{
|
use crate::serialization::{
|
||||||
sha256d, SerializationError, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize,
|
sha256d, SerializationError, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize,
|
||||||
};
|
};
|
||||||
use crate::types::LockTime;
|
use crate::transaction::LockTime;
|
||||||
use crate::Network;
|
use crate::Network;
|
||||||
|
|
||||||
use crate::test::generate;
|
use crate::test::generate;
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@ use std::sync::Arc;
|
||||||
use crate::{
|
use crate::{
|
||||||
block::{Block, BlockHeader, MAX_BLOCK_BYTES},
|
block::{Block, BlockHeader, MAX_BLOCK_BYTES},
|
||||||
serialization::{ZcashDeserialize, ZcashSerialize},
|
serialization::{ZcashDeserialize, ZcashSerialize},
|
||||||
transaction::{Transaction, TransparentInput, TransparentOutput},
|
transaction::{LockTime, Transaction, TransparentInput, TransparentOutput},
|
||||||
types::LockTime,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Generate a block header
|
/// Generate a block header
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
mod hash;
|
mod hash;
|
||||||
mod joinsplit;
|
mod joinsplit;
|
||||||
|
mod lock_time;
|
||||||
mod serialize;
|
mod serialize;
|
||||||
mod shielded_data;
|
mod shielded_data;
|
||||||
mod transparent;
|
mod transparent;
|
||||||
|
|
@ -13,12 +14,13 @@ mod tests;
|
||||||
|
|
||||||
pub use hash::TransactionHash;
|
pub use hash::TransactionHash;
|
||||||
pub use joinsplit::{JoinSplit, JoinSplitData};
|
pub use joinsplit::{JoinSplit, JoinSplitData};
|
||||||
|
pub use lock_time::LockTime;
|
||||||
pub use shielded_data::{Output, ShieldedData, Spend};
|
pub use shielded_data::{Output, ShieldedData, Spend};
|
||||||
pub use transparent::{CoinbaseData, OutPoint, TransparentInput, TransparentOutput};
|
pub use transparent::{CoinbaseData, OutPoint, TransparentInput, TransparentOutput};
|
||||||
|
|
||||||
use crate::amount::Amount;
|
use crate::amount::Amount;
|
||||||
use crate::proofs::{Bctv14Proof, Groth16Proof};
|
use crate::proofs::{Bctv14Proof, Groth16Proof};
|
||||||
use crate::types::{BlockHeight, LockTime};
|
use crate::types::BlockHeight;
|
||||||
|
|
||||||
/// A Zcash transaction.
|
/// A Zcash transaction.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
|
|
||||||
|
use crate::serialization::{SerializationError, ZcashDeserialize, ZcashSerialize};
|
||||||
|
use crate::types::BlockHeight;
|
||||||
|
|
||||||
|
/// A Bitcoin-style `locktime`, representing either a block height or an epoch
|
||||||
|
/// time.
|
||||||
|
///
|
||||||
|
/// # Invariants
|
||||||
|
///
|
||||||
|
/// Users should not construct a `LockTime` with:
|
||||||
|
/// - a `BlockHeight` greater than MAX_BLOCK_HEIGHT,
|
||||||
|
/// - a timestamp before 6 November 1985
|
||||||
|
/// (Unix timestamp less than MIN_LOCK_TIMESTAMP), or
|
||||||
|
/// - a timestamp after 5 February 2106
|
||||||
|
/// (Unix timestamp greater than MAX_LOCK_TIMESTAMP).
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum LockTime {
|
||||||
|
/// Unlock at a particular block height.
|
||||||
|
Height(BlockHeight),
|
||||||
|
/// Unlock at a particular time.
|
||||||
|
Time(DateTime<Utc>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LockTime {
|
||||||
|
/// The minimum LockTime::Time, as a timestamp in seconds.
|
||||||
|
///
|
||||||
|
/// Users should not construct lock times less than `MIN_TIMESTAMP`.
|
||||||
|
pub const MIN_TIMESTAMP: i64 = 500_000_000;
|
||||||
|
|
||||||
|
/// The maximum LockTime::Time, as a timestamp in seconds.
|
||||||
|
///
|
||||||
|
/// Users should not construct lock times greater than `MAX_TIMESTAMP`.
|
||||||
|
/// LockTime is u32 in the spec, so times are limited to u32::MAX.
|
||||||
|
pub const MAX_TIMESTAMP: i64 = u32::MAX as i64;
|
||||||
|
|
||||||
|
/// Returns the minimum LockTime::Time, as a LockTime.
|
||||||
|
///
|
||||||
|
/// Users should not construct lock times less than `min_lock_timestamp`.
|
||||||
|
//
|
||||||
|
// When `Utc.timestamp` stabilises as a const function, we can make this a
|
||||||
|
// const function.
|
||||||
|
pub fn min_lock_time() -> LockTime {
|
||||||
|
LockTime::Time(Utc.timestamp(Self::MIN_TIMESTAMP, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the maximum LockTime::Time, as a LockTime.
|
||||||
|
///
|
||||||
|
/// Users should not construct lock times greater than `max_lock_timestamp`.
|
||||||
|
//
|
||||||
|
// When `Utc.timestamp` stabilises as a const function, we can make this a
|
||||||
|
// const function.
|
||||||
|
pub fn max_lock_time() -> LockTime {
|
||||||
|
LockTime::Time(Utc.timestamp(Self::MAX_TIMESTAMP, 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ZcashSerialize for LockTime {
|
||||||
|
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
|
||||||
|
// This implementation does not check the invariants on `LockTime` so that the
|
||||||
|
// serialization is fallible only if the underlying writer is. This ensures that
|
||||||
|
// we can always compute a hash of a transaction object.
|
||||||
|
use LockTime::*;
|
||||||
|
match self {
|
||||||
|
Height(BlockHeight(n)) => writer.write_u32::<LittleEndian>(*n)?,
|
||||||
|
Time(t) => writer.write_u32::<LittleEndian>(t.timestamp() as u32)?,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ZcashDeserialize for LockTime {
|
||||||
|
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
|
||||||
|
let n = reader.read_u32::<LittleEndian>()?;
|
||||||
|
if n <= BlockHeight::MAX.0 {
|
||||||
|
Ok(LockTime::Height(BlockHeight(n)))
|
||||||
|
} else {
|
||||||
|
Ok(LockTime::Time(Utc.timestamp(n as i64, 0)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use chrono::{TimeZone, Utc};
|
||||||
use futures::future::Either;
|
use futures::future::Either;
|
||||||
use proptest::{arbitrary::any, array, collection::vec, option, prelude::*};
|
use proptest::{arbitrary::any, array, collection::vec, option, prelude::*};
|
||||||
|
|
||||||
|
|
@ -7,11 +8,11 @@ use crate::{
|
||||||
notes::{sapling, sprout},
|
notes::{sapling, sprout},
|
||||||
proofs::{Bctv14Proof, Groth16Proof, ZkSnarkProof},
|
proofs::{Bctv14Proof, Groth16Proof, ZkSnarkProof},
|
||||||
transaction::{
|
transaction::{
|
||||||
CoinbaseData, JoinSplit, JoinSplitData, OutPoint, Output, ShieldedData, Spend, Transaction,
|
CoinbaseData, JoinSplit, JoinSplitData, LockTime, OutPoint, Output, ShieldedData, Spend,
|
||||||
TransparentInput, TransparentOutput,
|
Transaction, TransparentInput, TransparentOutput,
|
||||||
},
|
},
|
||||||
treestate::{self, note_commitment_tree::SaplingNoteTreeRootHash},
|
treestate::{self, note_commitment_tree::SaplingNoteTreeRootHash},
|
||||||
types::{BlockHeight, LockTime, Script},
|
types::{BlockHeight, Script},
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Transaction {
|
impl Transaction {
|
||||||
|
|
@ -100,6 +101,22 @@ impl Transaction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Arbitrary for LockTime {
|
||||||
|
type Parameters = ();
|
||||||
|
|
||||||
|
fn arbitrary_with(_args: ()) -> Self::Strategy {
|
||||||
|
prop_oneof![
|
||||||
|
(BlockHeight::MIN.0..=BlockHeight::MAX.0)
|
||||||
|
.prop_map(|n| LockTime::Height(BlockHeight(n))),
|
||||||
|
(LockTime::MIN_TIMESTAMP..=LockTime::MAX_TIMESTAMP)
|
||||||
|
.prop_map(|n| { LockTime::Time(Utc.timestamp(n as i64, 0)) })
|
||||||
|
]
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Strategy = BoxedStrategy<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
impl<P: ZkSnarkProof + Arbitrary + 'static> Arbitrary for JoinSplit<P> {
|
impl<P: ZkSnarkProof + Arbitrary + 'static> Arbitrary for JoinSplit<P> {
|
||||||
type Parameters = ();
|
type Parameters = ();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
use super::super::*;
|
use super::super::*;
|
||||||
|
|
||||||
use crate::serialization::{ZcashDeserializeInto, ZcashSerialize};
|
use crate::serialization::{ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize};
|
||||||
|
|
||||||
proptest! {
|
proptest! {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn transaction_roundtrip(tx in any::<Transaction>()) {
|
fn transaction_roundtrip(tx in any::<Transaction>()) {
|
||||||
let data = tx.zcash_serialize_to_vec().expect("tx should serialize");
|
let data = tx.zcash_serialize_to_vec().expect("tx should serialize");
|
||||||
|
|
@ -13,4 +13,15 @@ proptest! {
|
||||||
|
|
||||||
prop_assert_eq![tx, tx2];
|
prop_assert_eq![tx, tx2];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn locktime_roundtrip(locktime in any::<LockTime>()) {
|
||||||
|
let mut bytes = Cursor::new(Vec::new());
|
||||||
|
locktime.zcash_serialize(&mut bytes)?;
|
||||||
|
|
||||||
|
bytes.set_position(0);
|
||||||
|
let other_locktime = LockTime::zcash_deserialize(&mut bytes)?;
|
||||||
|
|
||||||
|
prop_assert_eq![locktime, other_locktime];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
use crate::serialization::{
|
use crate::serialization::{
|
||||||
ReadZcashExt, SerializationError, WriteZcashExt, ZcashDeserialize, ZcashSerialize,
|
ReadZcashExt, SerializationError, WriteZcashExt, ZcashDeserialize, ZcashSerialize,
|
||||||
};
|
};
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
|
||||||
use chrono::{DateTime, TimeZone, Utc};
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt,
|
||||||
io::{self, Read},
|
io::{self, Read},
|
||||||
|
|
@ -46,25 +44,6 @@ impl Arbitrary for BlockHeight {
|
||||||
type Strategy = BoxedStrategy<Self>;
|
type Strategy = BoxedStrategy<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Bitcoin-style `locktime`, representing either a block height or an epoch
|
|
||||||
/// time.
|
|
||||||
///
|
|
||||||
/// # Invariants
|
|
||||||
///
|
|
||||||
/// Users should not construct a `LockTime` with:
|
|
||||||
/// - a `BlockHeight` greater than MAX_BLOCK_HEIGHT,
|
|
||||||
/// - a timestamp before 6 November 1985
|
|
||||||
/// (Unix timestamp less than MIN_LOCK_TIMESTAMP), or
|
|
||||||
/// - a timestamp after 5 February 2106
|
|
||||||
/// (Unix timestamp greater than MAX_LOCK_TIMESTAMP).
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum LockTime {
|
|
||||||
/// Unlock at a particular block height.
|
|
||||||
Height(BlockHeight),
|
|
||||||
/// Unlock at a particular time.
|
|
||||||
Time(DateTime<Utc>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BlockHeight {
|
impl BlockHeight {
|
||||||
/// The minimum BlockHeight.
|
/// The minimum BlockHeight.
|
||||||
///
|
///
|
||||||
|
|
@ -87,81 +66,6 @@ impl BlockHeight {
|
||||||
pub const MAX_AS_U32: u32 = Self::MAX.0;
|
pub const MAX_AS_U32: u32 = Self::MAX.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LockTime {
|
|
||||||
/// The minimum LockTime::Time, as a timestamp in seconds.
|
|
||||||
///
|
|
||||||
/// Users should not construct lock times less than `MIN_TIMESTAMP`.
|
|
||||||
pub const MIN_TIMESTAMP: i64 = 500_000_000;
|
|
||||||
|
|
||||||
/// The maximum LockTime::Time, as a timestamp in seconds.
|
|
||||||
///
|
|
||||||
/// Users should not construct lock times greater than `MAX_TIMESTAMP`.
|
|
||||||
/// LockTime is u32 in the spec, so times are limited to u32::MAX.
|
|
||||||
pub const MAX_TIMESTAMP: i64 = u32::MAX as i64;
|
|
||||||
|
|
||||||
/// Returns the minimum LockTime::Time, as a LockTime.
|
|
||||||
///
|
|
||||||
/// Users should not construct lock times less than `min_lock_timestamp`.
|
|
||||||
//
|
|
||||||
// When `Utc.timestamp` stabilises as a const function, we can make this a
|
|
||||||
// const function.
|
|
||||||
pub fn min_lock_time() -> LockTime {
|
|
||||||
LockTime::Time(Utc.timestamp(Self::MIN_TIMESTAMP, 0))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the maximum LockTime::Time, as a LockTime.
|
|
||||||
///
|
|
||||||
/// Users should not construct lock times greater than `max_lock_timestamp`.
|
|
||||||
//
|
|
||||||
// When `Utc.timestamp` stabilises as a const function, we can make this a
|
|
||||||
// const function.
|
|
||||||
pub fn max_lock_time() -> LockTime {
|
|
||||||
LockTime::Time(Utc.timestamp(Self::MAX_TIMESTAMP, 0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ZcashSerialize for LockTime {
|
|
||||||
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
|
|
||||||
// This implementation does not check the invariants on `LockTime` so that the
|
|
||||||
// serialization is fallible only if the underlying writer is. This ensures that
|
|
||||||
// we can always compute a hash of a transaction object.
|
|
||||||
use LockTime::*;
|
|
||||||
match self {
|
|
||||||
Height(BlockHeight(n)) => writer.write_u32::<LittleEndian>(*n)?,
|
|
||||||
Time(t) => writer.write_u32::<LittleEndian>(t.timestamp() as u32)?,
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ZcashDeserialize for LockTime {
|
|
||||||
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
|
|
||||||
let n = reader.read_u32::<LittleEndian>()?;
|
|
||||||
if n <= BlockHeight::MAX.0 {
|
|
||||||
Ok(LockTime::Height(BlockHeight(n)))
|
|
||||||
} else {
|
|
||||||
Ok(LockTime::Time(Utc.timestamp(n as i64, 0)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl Arbitrary for LockTime {
|
|
||||||
type Parameters = ();
|
|
||||||
|
|
||||||
fn arbitrary_with(_args: ()) -> Self::Strategy {
|
|
||||||
prop_oneof![
|
|
||||||
(BlockHeight::MIN.0..=BlockHeight::MAX.0)
|
|
||||||
.prop_map(|n| LockTime::Height(BlockHeight(n))),
|
|
||||||
(LockTime::MIN_TIMESTAMP..=LockTime::MAX_TIMESTAMP)
|
|
||||||
.prop_map(|n| { LockTime::Time(Utc.timestamp(n as i64, 0)) })
|
|
||||||
]
|
|
||||||
.boxed()
|
|
||||||
}
|
|
||||||
|
|
||||||
type Strategy = BoxedStrategy<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A sequence of message authentication tags ...
|
/// A sequence of message authentication tags ...
|
||||||
///
|
///
|
||||||
/// binding h_sig to each a_sk of the JoinSplit description, computed as
|
/// binding h_sig to each a_sk of the JoinSplit description, computed as
|
||||||
|
|
@ -223,21 +127,10 @@ mod proptests {
|
||||||
|
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
|
||||||
use super::{LockTime, Script};
|
use super::*;
|
||||||
use crate::serialization::{ZcashDeserialize, ZcashSerialize};
|
use crate::serialization::{ZcashDeserialize, ZcashSerialize};
|
||||||
|
|
||||||
proptest! {
|
proptest! {
|
||||||
#[test]
|
|
||||||
fn locktime_roundtrip(locktime in any::<LockTime>()) {
|
|
||||||
let mut bytes = Cursor::new(Vec::new());
|
|
||||||
locktime.zcash_serialize(&mut bytes)?;
|
|
||||||
|
|
||||||
bytes.set_position(0);
|
|
||||||
let other_locktime = LockTime::zcash_deserialize(&mut bytes)?;
|
|
||||||
|
|
||||||
prop_assert_eq![locktime, other_locktime];
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn script_roundtrip(script in any::<Script>()) {
|
fn script_roundtrip(script in any::<Script>()) {
|
||||||
let mut bytes = Cursor::new(Vec::new());
|
let mut bytes = Cursor::new(Vec::new());
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue