use std::{iter, sync::Arc};
use zebra_chain::{
block::{
tests::generate::{
large_multi_transaction_block, large_single_transaction_block_many_inputs,
large_single_transaction_block_many_outputs,
},
Block, Height,
},
parameters::Network::{self, *},
serialization::{ZcashDeserializeInto, ZcashSerialize},
transparent::new_ordered_outputs_with_height,
};
use zebra_test::vectors::{MAINNET_BLOCKS, TESTNET_BLOCKS};
use crate::{
constants::{state_database_format_version_in_code, STATE_DATABASE_KIND},
request::{FinalizedBlock, Treestate},
service::finalized_state::{disk_db::DiskWriteBatch, ZebraDb, STATE_COLUMN_FAMILIES_IN_CODE},
CheckpointVerifiedBlock, Config, SemanticallyVerifiedBlock,
};
#[test]
fn test_block_db_round_trip() {
let mainnet_test_cases = MAINNET_BLOCKS
.values()
.map(|block| block.zcash_deserialize_into().unwrap());
let testnet_test_cases = TESTNET_BLOCKS
.values()
.map(|block| block.zcash_deserialize_into().unwrap());
test_block_db_round_trip_with(&Mainnet, mainnet_test_cases);
test_block_db_round_trip_with(&Network::new_default_testnet(), testnet_test_cases);
test_block_db_round_trip_with(&Mainnet, iter::once(large_multi_transaction_block()));
let block = large_single_transaction_block_many_inputs();
let block_data = block
.zcash_serialize_to_vec()
.expect("serialization to vec never fails");
let block: Block = block_data
.zcash_deserialize_into()
.expect("deserialization of valid serialized block never fails");
test_block_db_round_trip_with(&Mainnet, iter::once(block));
let block = large_single_transaction_block_many_outputs();
let block_data = block
.zcash_serialize_to_vec()
.expect("serialization to vec never fails");
let block: Block = block_data
.zcash_deserialize_into()
.expect("deserialization of valid serialized block never fails");
test_block_db_round_trip_with(&Mainnet, iter::once(block));
}
fn test_block_db_round_trip_with(
network: &Network,
block_test_cases: impl IntoIterator<Item = Block>,
) {
let _init_guard = zebra_test::init();
let state = ZebraDb::new(
&Config::ephemeral(),
STATE_DATABASE_KIND,
&state_database_format_version_in_code(),
network,
true,
STATE_COLUMN_FAMILIES_IN_CODE
.iter()
.map(ToString::to_string),
false,
);
for original_block in block_test_cases.into_iter() {
let block_data = original_block
.zcash_serialize_to_vec()
.expect("serialization to vec never fails");
let round_trip_block: Block = block_data
.zcash_deserialize_into()
.expect("deserialization of valid serialized block never fails");
let round_trip_data = round_trip_block
.zcash_serialize_to_vec()
.expect("serialization to vec never fails");
assert_eq!(
original_block, round_trip_block,
"test block structure must round-trip",
);
assert_eq!(
block_data, round_trip_data,
"test block data must round-trip",
);
let original_block = Arc::new(original_block);
let checkpoint_verified = if original_block.coinbase_height().is_some() {
CheckpointVerifiedBlock::from(original_block.clone())
} else {
let hash = original_block.hash();
let transaction_hashes: Arc<[_]> = original_block
.transactions
.iter()
.map(|tx| tx.hash())
.collect();
let new_outputs =
new_ordered_outputs_with_height(&original_block, Height(0), &transaction_hashes);
CheckpointVerifiedBlock(SemanticallyVerifiedBlock {
block: original_block.clone(),
hash,
height: Height(0),
new_outputs,
transaction_hashes,
deferred_pool_balance_change: None,
})
};
let dummy_treestate = Treestate::default();
let finalized =
FinalizedBlock::from_checkpoint_verified(checkpoint_verified, dummy_treestate);
let mut batch = DiskWriteBatch::new();
batch.prepare_block_header_and_transaction_data_batch(&state.db, &finalized);
state.db.write(batch).expect("block is valid for writing");
let stored_block = state
.block(finalized.height.into())
.expect("block was stored at height");
if stored_block != original_block {
error!(
"
detailed block mismatch report:
original: {:?}\n\
original data: {:?}\n\
stored: {:?}\n\
stored data: {:?}\n\
",
original_block,
hex::encode(original_block.zcash_serialize_to_vec().unwrap()),
stored_block,
hex::encode(stored_block.zcash_serialize_to_vec().unwrap()),
);
}
assert_eq!(stored_block, original_block);
}
}