1//! Generate large transparent blocks and transactions for testing.
23use chrono::DateTime;
4use std::sync::Arc;
56use crate::{
7 block::{serialize::MAX_BLOCK_BYTES, Block, Header},
8 serialization::{ZcashDeserialize, ZcashSerialize},
9 transaction::{LockTime, Transaction},
10 transparent,
11};
1213/// The minimum size of the blocks produced by this module.
14pub const MIN_LARGE_BLOCK_BYTES: u64 = MAX_BLOCK_BYTES - 100;
1516/// The maximum number of bytes used to serialize a CompactSize,
17/// for the transaction, input, and output counts generated by this module.
18pub const MAX_COMPACT_SIZE_BYTES: usize = 4;
1920/// The number of bytes used to serialize a version 1 transaction header.
21pub const TX_V1_HEADER_BYTES: usize = 4;
2223/// Returns a generated block header, and its canonical serialized bytes.
24pub fn block_header() -> (Header, Vec<u8>) {
25// Some of the test vectors are in a non-canonical format,
26 // so we have to round-trip serialize them.
2728let block_header = Header::zcash_deserialize(&zebra_test::vectors::DUMMY_HEADER[..]).unwrap();
29let block_header_bytes = block_header.zcash_serialize_to_vec().unwrap();
3031 (block_header, block_header_bytes)
32}
3334/// Returns a generated transparent transaction, and its canonical serialized bytes.
35pub fn transaction() -> (Transaction, Vec<u8>) {
36// Some of the test vectors are in a non-canonical format,
37 // so we have to round-trip serialize them.
3839let transaction = Transaction::zcash_deserialize(&zebra_test::vectors::DUMMY_TX1[..]).unwrap();
40let transaction_bytes = transaction.zcash_serialize_to_vec().unwrap();
4142 (transaction, transaction_bytes)
43}
4445/// Returns a generated transparent input, and its canonical serialized bytes.
46pub fn input() -> (transparent::Input, Vec<u8>) {
47// Some of the test vectors are in a non-canonical format,
48 // so we have to round-trip serialize them.
4950let input =
51 transparent::Input::zcash_deserialize(&zebra_test::vectors::DUMMY_INPUT1[..]).unwrap();
52let input_bytes = input.zcash_serialize_to_vec().unwrap();
5354 (input, input_bytes)
55}
5657/// Returns a generated transparent output, and its canonical serialized bytes.
58pub fn output() -> (transparent::Output, Vec<u8>) {
59// Some of the test vectors are in a non-canonical format,
60 // so we have to round-trip serialize them.
6162let output =
63 transparent::Output::zcash_deserialize(&zebra_test::vectors::DUMMY_OUTPUT1[..]).unwrap();
64let output_bytes = output.zcash_serialize_to_vec().unwrap();
6566 (output, output_bytes)
67}
6869/// Generate a block with multiple transparent transactions just below limit
70///
71/// TODO: add a coinbase height to the returned block
72pub fn large_multi_transaction_block() -> Block {
73 multi_transaction_block(false)
74}
7576/// Generate a block with one transaction and multiple transparent inputs just below limit
77///
78/// TODO: add a coinbase height to the returned block
79/// make the returned block stable under round-trip serialization
80pub fn large_single_transaction_block_many_inputs() -> Block {
81 single_transaction_block_many_inputs(false)
82}
8384/// Generate a block with one transaction and multiple transparent outputs just below limit
85///
86/// TODO: add a coinbase height to the returned block
87/// make the returned block stable under round-trip serialization
88pub fn large_single_transaction_block_many_outputs() -> Block {
89 single_transaction_block_many_outputs(false)
90}
9192/// Generate a block with multiple transparent transactions just above limit
93///
94/// TODO: add a coinbase height to the returned block
95pub fn oversized_multi_transaction_block() -> Block {
96 multi_transaction_block(true)
97}
9899/// Generate a block with one transaction and multiple transparent inputs just above limit
100///
101/// TODO: add a coinbase height to the returned block
102/// make the returned block stable under round-trip serialization
103pub fn oversized_single_transaction_block_many_inputs() -> Block {
104 single_transaction_block_many_inputs(true)
105}
106107/// Generate a block with one transaction and multiple transparent outputs just above limit
108///
109/// TODO: add a coinbase height to the returned block
110/// make the returned block stable under round-trip serialization
111pub fn oversized_single_transaction_block_many_outputs() -> Block {
112 single_transaction_block_many_outputs(true)
113}
114115/// Implementation of block generation with multiple transparent transactions
116fn multi_transaction_block(oversized: bool) -> Block {
117// A dummy transaction
118let (transaction, transaction_bytes) = transaction();
119120// A block header
121let (block_header, block_header_bytes) = block_header();
122123// Calculate the number of transactions we need,
124 // subtracting the bytes used to serialize the expected transaction count.
125let mut max_transactions_in_block = (usize::try_from(MAX_BLOCK_BYTES).unwrap()
126 - block_header_bytes.len()
127 - MAX_COMPACT_SIZE_BYTES)
128 / transaction_bytes.len();
129if oversized {
130 max_transactions_in_block += 1;
131 }
132133// Create transactions to be just below or just above the limit
134let transactions =
135 std::iter::repeat_n(Arc::new(transaction), max_transactions_in_block).collect::<Vec<_>>();
136137// Add the transactions into a block
138let block = Block {
139 header: block_header.into(),
140 transactions,
141 };
142143let serialized_len = block.zcash_serialize_to_vec().unwrap().len();
144assert_eq!(
145 oversized,
146 serialized_len > MAX_BLOCK_BYTES.try_into().unwrap(),
147"block is over-sized if requested:\n\
148 oversized: {oversized},\n\
149 serialized_len: {serialized_len},\n\
150 MAX_BLOCK_BYTES: {MAX_BLOCK_BYTES},",
151 );
152assert!(
153 serialized_len > MIN_LARGE_BLOCK_BYTES.try_into().unwrap(),
154"block is large\n\
155 oversized: {oversized},\n\
156 serialized_len: {serialized_len},\n\
157 MIN_LARGE_BLOCK_BYTES: {MIN_LARGE_BLOCK_BYTES},",
158 );
159160 block
161}
162163/// Implementation of block generation with one transaction and multiple transparent inputs
164fn single_transaction_block_many_inputs(oversized: bool) -> Block {
165// Dummy input and output
166let (input, input_bytes) = input();
167let (output, output_bytes) = output();
168169// A block header
170let (block_header, block_header_bytes) = block_header();
171172let lock_time = LockTime::Time(DateTime::from_timestamp(61, 0).unwrap());
173let lock_time_bytes = lock_time.zcash_serialize_to_vec().unwrap();
174175// Calculate the number of inputs we need,
176 // subtracting the bytes used to serialize the expected input count,
177 // transaction count, and output count.
178let mut max_inputs_in_tx = (usize::try_from(MAX_BLOCK_BYTES).unwrap()
179 - block_header_bytes.len()
180 - 1
181- TX_V1_HEADER_BYTES
182 - lock_time_bytes.len()
183 - MAX_COMPACT_SIZE_BYTES
184 - 1
185- output_bytes.len())
186 / input_bytes.len();
187188if oversized {
189 max_inputs_in_tx += 1;
190 }
191192let mut outputs = Vec::new();
193194// Create inputs to be just below the limit
195let inputs = std::iter::repeat_n(input, max_inputs_in_tx).collect::<Vec<_>>();
196197// 1 single output
198outputs.push(output);
199200// Create a big transaction
201let big_transaction = Transaction::V1 {
202 inputs,
203 outputs,
204 lock_time,
205 };
206207// Put the big transaction into a block
208let transactions = vec![Arc::new(big_transaction)];
209210let block = Block {
211 header: block_header.into(),
212 transactions,
213 };
214215let serialized_len = block.zcash_serialize_to_vec().unwrap().len();
216assert_eq!(
217 oversized,
218 serialized_len > MAX_BLOCK_BYTES.try_into().unwrap(),
219"block is over-sized if requested:\n\
220 oversized: {oversized},\n\
221 serialized_len: {serialized_len},\n\
222 MAX_BLOCK_BYTES: {MAX_BLOCK_BYTES},",
223 );
224assert!(
225 serialized_len > MIN_LARGE_BLOCK_BYTES.try_into().unwrap(),
226"block is large\n\
227 oversized: {oversized},\n\
228 serialized_len: {serialized_len},\n\
229 MIN_LARGE_BLOCK_BYTES: {MIN_LARGE_BLOCK_BYTES},",
230 );
231232 block
233}
234235/// Implementation of block generation with one transaction and multiple transparent outputs
236fn single_transaction_block_many_outputs(oversized: bool) -> Block {
237// Dummy input and output
238let (input, input_bytes) = input();
239let (output, output_bytes) = output();
240241// A block header
242let (block_header, block_header_bytes) = block_header();
243244let lock_time = LockTime::Time(DateTime::from_timestamp(61, 0).unwrap());
245let lock_time_bytes = lock_time.zcash_serialize_to_vec().unwrap();
246247// Calculate the number of outputs we need,
248 // subtracting the bytes used to serialize the expected output count,
249 // transaction count, and input count.
250let mut max_outputs_in_tx = (usize::try_from(MAX_BLOCK_BYTES).unwrap()
251 - block_header_bytes.len()
252 - 1
253- TX_V1_HEADER_BYTES
254 - lock_time_bytes.len()
255 - 1
256- input_bytes.len()
257 - MAX_COMPACT_SIZE_BYTES)
258 / output_bytes.len();
259260if oversized {
261 max_outputs_in_tx += 1;
262 }
263264// 1 single input
265let inputs = vec![input];
266267// Create outputs to be just below the limit
268let outputs = std::iter::repeat_n(output, max_outputs_in_tx).collect::<Vec<_>>();
269270// Create a big transaction
271let big_transaction = Transaction::V1 {
272 inputs,
273 outputs,
274 lock_time,
275 };
276277// Put the big transaction into a block
278let transactions = vec![Arc::new(big_transaction)];
279280let block = Block {
281 header: block_header.into(),
282 transactions,
283 };
284285let serialized_len = block.zcash_serialize_to_vec().unwrap().len();
286assert_eq!(
287 oversized,
288 serialized_len > MAX_BLOCK_BYTES.try_into().unwrap(),
289"block is over-sized if requested:\n\
290 oversized: {oversized},\n\
291 serialized_len: {serialized_len},\n\
292 MAX_BLOCK_BYTES: {MAX_BLOCK_BYTES},",
293 );
294assert!(
295 serialized_len > MIN_LARGE_BLOCK_BYTES.try_into().unwrap(),
296"block is large\n\
297 oversized: {oversized},\n\
298 serialized_len: {serialized_len},\n\
299 MIN_LARGE_BLOCK_BYTES: {MIN_LARGE_BLOCK_BYTES},",
300 );
301302 block
303}