309 lines
10 KiB
Rust
309 lines
10 KiB
Rust
//! Generate large transparent blocks and transactions for testing.
|
|
|
|
use chrono::DateTime;
|
|
use std::sync::Arc;
|
|
|
|
use crate::{
|
|
block::{serialize::MAX_BLOCK_BYTES, Block, Header},
|
|
serialization::{ZcashDeserialize, ZcashSerialize},
|
|
transaction::{LockTime, Transaction},
|
|
transparent,
|
|
};
|
|
|
|
/// The minimum size of the blocks produced by this module.
|
|
pub const MIN_LARGE_BLOCK_BYTES: u64 = MAX_BLOCK_BYTES - 100;
|
|
|
|
/// The maximum number of bytes used to serialize a CompactSize,
|
|
/// for the transaction, input, and output counts generated by this module.
|
|
pub const MAX_COMPACT_SIZE_BYTES: usize = 4;
|
|
|
|
/// The number of bytes used to serialize a version 1 transaction header.
|
|
pub const TX_V1_HEADER_BYTES: usize = 4;
|
|
|
|
/// Returns a generated block header, and its canonical serialized bytes.
|
|
pub fn block_header() -> (Header, Vec<u8>) {
|
|
// Some of the test vectors are in a non-canonical format,
|
|
// so we have to round-trip serialize them.
|
|
|
|
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)
|
|
}
|
|
|
|
/// Returns a generated transparent transaction, and its canonical serialized bytes.
|
|
pub fn transaction() -> (Transaction, Vec<u8>) {
|
|
// Some of the test vectors are in a non-canonical format,
|
|
// so we have to round-trip serialize them.
|
|
|
|
let transaction = Transaction::zcash_deserialize(&zebra_test::vectors::DUMMY_TX1[..]).unwrap();
|
|
let transaction_bytes = transaction.zcash_serialize_to_vec().unwrap();
|
|
|
|
(transaction, transaction_bytes)
|
|
}
|
|
|
|
/// Returns a generated transparent input, and its canonical serialized bytes.
|
|
pub fn input() -> (transparent::Input, Vec<u8>) {
|
|
// Some of the test vectors are in a non-canonical format,
|
|
// so we have to round-trip serialize them.
|
|
|
|
let input =
|
|
transparent::Input::zcash_deserialize(&zebra_test::vectors::DUMMY_INPUT1[..]).unwrap();
|
|
let input_bytes = input.zcash_serialize_to_vec().unwrap();
|
|
|
|
(input, input_bytes)
|
|
}
|
|
|
|
/// Returns a generated transparent output, and its canonical serialized bytes.
|
|
pub fn output() -> (transparent::Output, Vec<u8>) {
|
|
// Some of the test vectors are in a non-canonical format,
|
|
// so we have to round-trip serialize them.
|
|
|
|
let output =
|
|
transparent::Output::zcash_deserialize(&zebra_test::vectors::DUMMY_OUTPUT1[..]).unwrap();
|
|
let output_bytes = output.zcash_serialize_to_vec().unwrap();
|
|
|
|
(output, output_bytes)
|
|
}
|
|
|
|
/// Generate a block with multiple transparent transactions just below limit
|
|
///
|
|
/// TODO: add a coinbase height to the returned block
|
|
pub fn large_multi_transaction_block() -> Block {
|
|
multi_transaction_block(false)
|
|
}
|
|
|
|
/// Generate a block with one transaction and multiple transparent inputs just below limit
|
|
///
|
|
/// TODO: add a coinbase height to the returned block
|
|
/// make the returned block stable under round-trip serialization
|
|
pub fn large_single_transaction_block_many_inputs() -> Block {
|
|
single_transaction_block_many_inputs(false)
|
|
}
|
|
|
|
/// Generate a block with one transaction and multiple transparent outputs just below limit
|
|
///
|
|
/// TODO: add a coinbase height to the returned block
|
|
/// make the returned block stable under round-trip serialization
|
|
pub fn large_single_transaction_block_many_outputs() -> Block {
|
|
single_transaction_block_many_outputs(false)
|
|
}
|
|
|
|
/// Generate a block with multiple transparent transactions just above limit
|
|
///
|
|
/// TODO: add a coinbase height to the returned block
|
|
pub fn oversized_multi_transaction_block() -> Block {
|
|
multi_transaction_block(true)
|
|
}
|
|
|
|
/// Generate a block with one transaction and multiple transparent inputs just above limit
|
|
///
|
|
/// TODO: add a coinbase height to the returned block
|
|
/// make the returned block stable under round-trip serialization
|
|
pub fn oversized_single_transaction_block_many_inputs() -> Block {
|
|
single_transaction_block_many_inputs(true)
|
|
}
|
|
|
|
/// Generate a block with one transaction and multiple transparent outputs just above limit
|
|
///
|
|
/// TODO: add a coinbase height to the returned block
|
|
/// make the returned block stable under round-trip serialization
|
|
pub fn oversized_single_transaction_block_many_outputs() -> Block {
|
|
single_transaction_block_many_outputs(true)
|
|
}
|
|
|
|
/// Implementation of block generation with multiple transparent transactions
|
|
fn multi_transaction_block(oversized: bool) -> Block {
|
|
// A dummy transaction
|
|
let (transaction, transaction_bytes) = transaction();
|
|
|
|
// A block header
|
|
let (block_header, block_header_bytes) = block_header();
|
|
|
|
// Calculate the number of transactions we need,
|
|
// subtracting the bytes used to serialize the expected transaction count.
|
|
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;
|
|
}
|
|
|
|
// Create transactions to be just below or just above the limit
|
|
let transactions = std::iter::repeat(Arc::new(transaction))
|
|
.take(max_transactions_in_block)
|
|
.collect::<Vec<_>>();
|
|
|
|
// Add the transactions into a block
|
|
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
|
|
}
|
|
|
|
/// Implementation of block generation with one transaction and multiple transparent inputs
|
|
fn single_transaction_block_many_inputs(oversized: bool) -> Block {
|
|
// Dummy input and output
|
|
let (input, input_bytes) = input();
|
|
let (output, output_bytes) = output();
|
|
|
|
// A block header
|
|
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();
|
|
|
|
// Calculate the number of inputs we need,
|
|
// subtracting the bytes used to serialize the expected input count,
|
|
// transaction count, and output count.
|
|
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();
|
|
|
|
// Create inputs to be just below the limit
|
|
let inputs = std::iter::repeat(input)
|
|
.take(max_inputs_in_tx)
|
|
.collect::<Vec<_>>();
|
|
|
|
// 1 single output
|
|
outputs.push(output);
|
|
|
|
// Create a big transaction
|
|
let big_transaction = Transaction::V1 {
|
|
inputs,
|
|
outputs,
|
|
lock_time,
|
|
};
|
|
|
|
// Put the big transaction into a block
|
|
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
|
|
}
|
|
|
|
/// Implementation of block generation with one transaction and multiple transparent outputs
|
|
fn single_transaction_block_many_outputs(oversized: bool) -> Block {
|
|
// Dummy input and output
|
|
let (input, input_bytes) = input();
|
|
let (output, output_bytes) = output();
|
|
|
|
// A block header
|
|
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();
|
|
|
|
// Calculate the number of outputs we need,
|
|
// subtracting the bytes used to serialize the expected output count,
|
|
// transaction count, and input count.
|
|
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;
|
|
}
|
|
|
|
// 1 single input
|
|
let inputs = vec![input];
|
|
|
|
// Create outputs to be just below the limit
|
|
let outputs = std::iter::repeat(output)
|
|
.take(max_outputs_in_tx)
|
|
.collect::<Vec<_>>();
|
|
|
|
// Create a big transaction
|
|
let big_transaction = Transaction::V1 {
|
|
inputs,
|
|
outputs,
|
|
lock_time,
|
|
};
|
|
|
|
// Put the big transaction into a block
|
|
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
|
|
}
|