use chrono::DateTime;
use std::sync::Arc;
use crate::{
block::{serialize::MAX_BLOCK_BYTES, Block, Header},
serialization::{ZcashDeserialize, ZcashSerialize},
transaction::{LockTime, Transaction},
transparent,
};
pub const MIN_LARGE_BLOCK_BYTES: u64 = MAX_BLOCK_BYTES - 100;
pub const MAX_COMPACT_SIZE_BYTES: usize = 4;
pub const TX_V1_HEADER_BYTES: usize = 4;
pub fn block_header() -> (Header, Vec<u8>) {
let block_header = Header::zcash_deserialize(&zebra_test::vectors::DUMMY_HEADER[..]).unwrap();
let block_header_bytes = block_header.zcash_serialize_to_vec().unwrap();
(block_header, block_header_bytes)
}
pub fn transaction() -> (Transaction, Vec<u8>) {
let transaction = Transaction::zcash_deserialize(&zebra_test::vectors::DUMMY_TX1[..]).unwrap();
let transaction_bytes = transaction.zcash_serialize_to_vec().unwrap();
(transaction, transaction_bytes)
}
pub fn input() -> (transparent::Input, Vec<u8>) {
let input =
transparent::Input::zcash_deserialize(&zebra_test::vectors::DUMMY_INPUT1[..]).unwrap();
let input_bytes = input.zcash_serialize_to_vec().unwrap();
(input, input_bytes)
}
pub fn output() -> (transparent::Output, Vec<u8>) {
let output =
transparent::Output::zcash_deserialize(&zebra_test::vectors::DUMMY_OUTPUT1[..]).unwrap();
let output_bytes = output.zcash_serialize_to_vec().unwrap();
(output, output_bytes)
}
pub fn large_multi_transaction_block() -> Block {
multi_transaction_block(false)
}
pub fn large_single_transaction_block_many_inputs() -> Block {
single_transaction_block_many_inputs(false)
}
pub fn large_single_transaction_block_many_outputs() -> Block {
single_transaction_block_many_outputs(false)
}
pub fn oversized_multi_transaction_block() -> Block {
multi_transaction_block(true)
}
pub fn oversized_single_transaction_block_many_inputs() -> Block {
single_transaction_block_many_inputs(true)
}
pub fn oversized_single_transaction_block_many_outputs() -> Block {
single_transaction_block_many_outputs(true)
}
fn multi_transaction_block(oversized: bool) -> Block {
let (transaction, transaction_bytes) = transaction();
let (block_header, block_header_bytes) = block_header();
let mut max_transactions_in_block = (usize::try_from(MAX_BLOCK_BYTES).unwrap()
- block_header_bytes.len()
- MAX_COMPACT_SIZE_BYTES)
/ transaction_bytes.len();
if oversized {
max_transactions_in_block += 1;
}
let transactions = std::iter::repeat(Arc::new(transaction))
.take(max_transactions_in_block)
.collect::<Vec<_>>();
let block = Block {
header: block_header.into(),
transactions,
};
let serialized_len = block.zcash_serialize_to_vec().unwrap().len();
assert_eq!(
oversized,
serialized_len > MAX_BLOCK_BYTES.try_into().unwrap(),
"block is over-sized if requested:\n\
oversized: {oversized},\n\
serialized_len: {serialized_len},\n\
MAX_BLOCK_BYTES: {MAX_BLOCK_BYTES},",
);
assert!(
serialized_len > MIN_LARGE_BLOCK_BYTES.try_into().unwrap(),
"block is large\n\
oversized: {oversized},\n\
serialized_len: {serialized_len},\n\
MIN_LARGE_BLOCK_BYTES: {MIN_LARGE_BLOCK_BYTES},",
);
block
}
fn single_transaction_block_many_inputs(oversized: bool) -> Block {
let (input, input_bytes) = input();
let (output, output_bytes) = output();
let (block_header, block_header_bytes) = block_header();
let lock_time = LockTime::Time(DateTime::from_timestamp(61, 0).unwrap());
let lock_time_bytes = lock_time.zcash_serialize_to_vec().unwrap();
let mut max_inputs_in_tx = (usize::try_from(MAX_BLOCK_BYTES).unwrap()
- block_header_bytes.len()
- 1
- TX_V1_HEADER_BYTES
- lock_time_bytes.len()
- MAX_COMPACT_SIZE_BYTES
- 1
- output_bytes.len())
/ input_bytes.len();
if oversized {
max_inputs_in_tx += 1;
}
let mut outputs = Vec::new();
let inputs = std::iter::repeat(input)
.take(max_inputs_in_tx)
.collect::<Vec<_>>();
outputs.push(output);
let big_transaction = Transaction::V1 {
inputs,
outputs,
lock_time,
};
let transactions = vec![Arc::new(big_transaction)];
let block = Block {
header: block_header.into(),
transactions,
};
let serialized_len = block.zcash_serialize_to_vec().unwrap().len();
assert_eq!(
oversized,
serialized_len > MAX_BLOCK_BYTES.try_into().unwrap(),
"block is over-sized if requested:\n\
oversized: {oversized},\n\
serialized_len: {serialized_len},\n\
MAX_BLOCK_BYTES: {MAX_BLOCK_BYTES},",
);
assert!(
serialized_len > MIN_LARGE_BLOCK_BYTES.try_into().unwrap(),
"block is large\n\
oversized: {oversized},\n\
serialized_len: {serialized_len},\n\
MIN_LARGE_BLOCK_BYTES: {MIN_LARGE_BLOCK_BYTES},",
);
block
}
fn single_transaction_block_many_outputs(oversized: bool) -> Block {
let (input, input_bytes) = input();
let (output, output_bytes) = output();
let (block_header, block_header_bytes) = block_header();
let lock_time = LockTime::Time(DateTime::from_timestamp(61, 0).unwrap());
let lock_time_bytes = lock_time.zcash_serialize_to_vec().unwrap();
let mut max_outputs_in_tx = (usize::try_from(MAX_BLOCK_BYTES).unwrap()
- block_header_bytes.len()
- 1
- TX_V1_HEADER_BYTES
- lock_time_bytes.len()
- 1
- input_bytes.len()
- MAX_COMPACT_SIZE_BYTES)
/ output_bytes.len();
if oversized {
max_outputs_in_tx += 1;
}
let inputs = vec![input];
let outputs = std::iter::repeat(output)
.take(max_outputs_in_tx)
.collect::<Vec<_>>();
let big_transaction = Transaction::V1 {
inputs,
outputs,
lock_time,
};
let transactions = vec![Arc::new(big_transaction)];
let block = Block {
header: block_header.into(),
transactions,
};
let serialized_len = block.zcash_serialize_to_vec().unwrap().len();
assert_eq!(
oversized,
serialized_len > MAX_BLOCK_BYTES.try_into().unwrap(),
"block is over-sized if requested:\n\
oversized: {oversized},\n\
serialized_len: {serialized_len},\n\
MAX_BLOCK_BYTES: {MAX_BLOCK_BYTES},",
);
assert!(
serialized_len > MIN_LARGE_BLOCK_BYTES.try_into().unwrap(),
"block is large\n\
oversized: {oversized},\n\
serialized_len: {serialized_len},\n\
MIN_LARGE_BLOCK_BYTES: {MIN_LARGE_BLOCK_BYTES},",
);
block
}