2020-07-09 23:51:01 -07:00
//! Tests for block verification
2022-07-21 16:15:22 -07:00
use std ::sync ::Arc ;
2020-09-08 16:39:03 -07:00
2020-07-23 20:17:39 -07:00
use chrono ::Utc ;
use color_eyre ::eyre ::{ eyre , Report } ;
2020-08-05 14:41:41 -07:00
use once_cell ::sync ::Lazy ;
2021-08-25 08:07:26 -07:00
use tower ::{ buffer ::Buffer , util ::BoxService } ;
2020-07-09 23:51:01 -07:00
2020-10-12 20:38:49 -07:00
use zebra_chain ::{
2021-11-23 19:36:17 -08:00
amount ::{ Amount , MAX_MONEY } ,
2021-11-15 12:55:32 -08:00
block ::{
self ,
2022-03-09 15:34:50 -08:00
tests ::generate ::{
large_multi_transaction_block , large_single_transaction_block_many_inputs ,
} ,
2021-11-15 12:55:32 -08:00
Block , Height ,
} ,
2021-05-09 18:31:45 -07:00
parameters ::{ Network , NetworkUpgrade } ,
2020-09-01 12:39:04 -07:00
serialization ::{ ZcashDeserialize , ZcashDeserializeInto } ,
2021-11-22 21:53:53 -08:00
transaction ::{ arbitrary ::transaction_to_fake_v5 , LockTime , Transaction } ,
2024-03-12 14:41:44 -07:00
work ::difficulty ::{ ParameterDifficulty as _ , INVALID_COMPACT_DIFFICULTY } ,
2020-09-01 12:39:04 -07:00
} ;
2021-11-15 12:55:32 -08:00
use zebra_script ::CachedFfiTransaction ;
2021-06-03 15:48:40 -07:00
use zebra_test ::transcript ::{ ExpectedTranscriptError , Transcript } ;
2022-01-31 22:24:08 -08:00
use crate ::{ parameters ::SLOW_START_SHIFT , transaction } ;
2021-11-15 12:55:32 -08:00
use super ::* ;
2021-08-25 08:07:26 -07:00
2023-01-11 15:39:51 -08:00
static VALID_BLOCK_TRANSCRIPT : Lazy < Vec < ( Request , Result < block ::Hash , ExpectedTranscriptError > ) > > =
Lazy ::new ( | | {
let block : Arc < _ > =
Block ::zcash_deserialize ( & zebra_test ::vectors ::BLOCK_MAINNET_GENESIS_BYTES [ .. ] )
. unwrap ( )
. into ( ) ;
let hash = Ok ( block . as_ref ( ) . into ( ) ) ;
vec! [ ( Request ::Commit ( block ) , hash ) ]
} ) ;
2021-06-03 15:48:40 -07:00
static INVALID_TIME_BLOCK_TRANSCRIPT : Lazy <
2023-01-11 15:39:51 -08:00
Vec < ( Request , Result < block ::Hash , ExpectedTranscriptError > ) > ,
2021-06-03 15:48:40 -07:00
> = Lazy ::new ( | | {
let mut block : Block =
Block ::zcash_deserialize ( & zebra_test ::vectors ::BLOCK_MAINNET_GENESIS_BYTES [ .. ] ) . unwrap ( ) ;
// Modify the block's time
// Changing the block header also invalidates the header hashes, but
// those checks should be performed later in validation, because they
// are more expensive.
let three_hours_in_the_future = Utc ::now ( )
. checked_add_signed ( chrono ::Duration ::hours ( 3 ) )
. ok_or_else ( | | eyre! ( " overflow when calculating 3 hours in the future " ) )
. unwrap ( ) ;
2022-07-21 16:15:22 -07:00
Arc ::make_mut ( & mut block . header ) . time = three_hours_in_the_future ;
2021-06-03 15:48:40 -07:00
2023-01-11 15:39:51 -08:00
vec! [ (
Request ::Commit ( Arc ::new ( block ) ) ,
Err ( ExpectedTranscriptError ::Any ) ,
) ]
2021-06-03 15:48:40 -07:00
} ) ;
2020-08-05 14:41:41 -07:00
static INVALID_HEADER_SOLUTION_TRANSCRIPT : Lazy <
2023-01-11 15:39:51 -08:00
Vec < ( Request , Result < block ::Hash , ExpectedTranscriptError > ) > ,
2020-08-05 14:41:41 -07:00
> = Lazy ::new ( | | {
let mut block : Block =
Block ::zcash_deserialize ( & zebra_test ::vectors ::BLOCK_MAINNET_GENESIS_BYTES [ .. ] ) . unwrap ( ) ;
2020-07-09 23:51:01 -07:00
// Change nonce to something invalid
2023-01-17 05:57:22 -08:00
Arc ::make_mut ( & mut block . header ) . nonce = [ 0 ; 32 ] . into ( ) ;
2020-07-09 23:51:01 -07:00
2023-01-11 15:39:51 -08:00
vec! [ (
Request ::Commit ( Arc ::new ( block ) ) ,
Err ( ExpectedTranscriptError ::Any ) ,
) ]
2020-08-05 14:41:41 -07:00
} ) ;
2021-06-03 15:48:40 -07:00
static INVALID_COINBASE_TRANSCRIPT : Lazy <
2023-01-11 15:39:51 -08:00
Vec < ( Request , Result < block ::Hash , ExpectedTranscriptError > ) > ,
2021-06-03 15:48:40 -07:00
> = Lazy ::new ( | | {
let header = block ::Header ::zcash_deserialize ( & zebra_test ::vectors ::DUMMY_HEADER [ .. ] ) . unwrap ( ) ;
// Test 1: Empty transaction
let block1 = Block {
2022-07-21 16:15:22 -07:00
header : header . into ( ) ,
2021-06-03 15:48:40 -07:00
transactions : Vec ::new ( ) ,
} ;
// Test 2: Transaction at first position is not coinbase
let mut transactions = Vec ::new ( ) ;
let tx = zebra_test ::vectors ::DUMMY_TX1
. zcash_deserialize_into ( )
. unwrap ( ) ;
transactions . push ( tx ) ;
let block2 = Block {
2022-07-21 16:15:22 -07:00
header : header . into ( ) ,
2021-06-03 15:48:40 -07:00
transactions ,
} ;
// Test 3: Invalid coinbase position
let mut block3 =
Block ::zcash_deserialize ( & zebra_test ::vectors ::BLOCK_MAINNET_GENESIS_BYTES [ .. ] ) . unwrap ( ) ;
assert_eq! ( block3 . transactions . len ( ) , 1 ) ;
// Extract the coinbase transaction from the block
2023-10-30 05:21:05 -07:00
let coinbase_transaction = block3 . transactions . first ( ) . unwrap ( ) . clone ( ) ;
2021-06-03 15:48:40 -07:00
// Add another coinbase transaction to block
block3 . transactions . push ( coinbase_transaction ) ;
assert_eq! ( block3 . transactions . len ( ) , 2 ) ;
vec! [
2023-01-11 15:39:51 -08:00
(
Request ::Commit ( Arc ::new ( block1 ) ) ,
Err ( ExpectedTranscriptError ::Any ) ,
) ,
(
Request ::Commit ( Arc ::new ( block2 ) ) ,
Err ( ExpectedTranscriptError ::Any ) ,
) ,
(
Request ::Commit ( Arc ::new ( block3 ) ) ,
Err ( ExpectedTranscriptError ::Any ) ,
) ,
2021-06-03 15:48:40 -07:00
]
} ) ;
2020-07-09 23:51:01 -07:00
2020-09-10 10:17:14 -07:00
// TODO: enable this test after implementing contextual verification
2020-11-20 22:37:43 -08:00
// #[tokio::test]
// #[ignore]
#[ allow(dead_code) ]
2020-08-05 14:41:41 -07:00
async fn check_transcripts_test ( ) -> Result < ( ) , Report > {
check_transcripts ( ) . await
2020-07-09 23:51:01 -07:00
}
2020-11-20 22:37:43 -08:00
#[ allow(dead_code) ]
2020-07-09 23:51:01 -07:00
#[ spandoc::spandoc ]
2020-08-05 14:41:41 -07:00
async fn check_transcripts ( ) -> Result < ( ) , Report > {
2022-08-04 08:44:44 -07:00
let _init_guard = zebra_test ::init ( ) ;
2020-09-01 12:39:04 -07:00
let network = Network ::Mainnet ;
2021-07-28 16:55:01 -07:00
let state_service = zebra_state ::init_test ( network ) ;
2020-09-01 12:39:04 -07:00
2022-01-31 22:24:08 -08:00
let transaction = transaction ::Verifier ::new ( network , state_service . clone ( ) ) ;
2021-08-25 08:07:26 -07:00
let transaction = Buffer ::new ( BoxService ::new ( transaction ) , 1 ) ;
let block_verifier = Buffer ::new (
2023-06-01 05:29:03 -07:00
SemanticBlockVerifier ::new ( network , state_service . clone ( ) , transaction ) ,
2021-08-25 08:07:26 -07:00
1 ,
) ;
2020-08-05 14:41:41 -07:00
for transcript_data in & [
& VALID_BLOCK_TRANSCRIPT ,
& INVALID_TIME_BLOCK_TRANSCRIPT ,
& INVALID_HEADER_SOLUTION_TRANSCRIPT ,
& INVALID_COINBASE_TRANSCRIPT ,
] {
let transcript = Transcript ::from ( transcript_data . iter ( ) . cloned ( ) ) ;
transcript . check ( block_verifier . clone ( ) ) . await . unwrap ( ) ;
}
2020-07-09 23:51:01 -07:00
Ok ( ( ) )
}
2020-09-08 16:39:03 -07:00
2020-10-12 15:35:39 -07:00
#[ test ]
fn coinbase_is_first_for_historical_blocks ( ) -> Result < ( ) , Report > {
2022-08-04 08:44:44 -07:00
let _init_guard = zebra_test ::init ( ) ;
2020-11-09 16:29:25 -08:00
2020-10-12 15:35:39 -07:00
let block_iter = zebra_test ::vectors ::BLOCKS . iter ( ) ;
for block in block_iter {
let block = block
. zcash_deserialize_into ::< Block > ( )
. expect ( " block is structurally valid " ) ;
check ::coinbase_is_first ( & block )
. expect ( " the coinbase in a historical block should be valid " ) ;
}
Ok ( ( ) )
}
2020-10-12 15:52:03 -07:00
#[ test ]
fn difficulty_is_valid_for_historical_blocks ( ) -> Result < ( ) , Report > {
2022-08-04 08:44:44 -07:00
let _init_guard = zebra_test ::init ( ) ;
2020-11-09 16:29:25 -08:00
2020-10-12 15:52:03 -07:00
difficulty_is_valid_for_network ( Network ::Mainnet ) ? ;
difficulty_is_valid_for_network ( Network ::Testnet ) ? ;
Ok ( ( ) )
}
fn difficulty_is_valid_for_network ( network : Network ) -> Result < ( ) , Report > {
2024-03-05 06:12:25 -08:00
let block_iter = network . block_iter ( ) ;
2020-10-12 15:52:03 -07:00
for ( & height , block ) in block_iter {
let block = block
. zcash_deserialize_into ::< Block > ( )
. expect ( " block is structurally valid " ) ;
check ::difficulty_is_valid ( & block . header , network , & Height ( height ) , & block . hash ( ) )
. expect ( " the difficulty from a historical block should be valid " ) ;
}
Ok ( ( ) )
}
2020-10-12 20:38:49 -07:00
#[ test ]
fn difficulty_validation_failure ( ) -> Result < ( ) , Report > {
2022-08-04 08:44:44 -07:00
let _init_guard = zebra_test ::init ( ) ;
2020-10-12 20:38:49 -07:00
// Get a block in the mainnet, and mangle its difficulty field
let block =
Arc ::< Block > ::zcash_deserialize ( & zebra_test ::vectors ::BLOCK_MAINNET_415000_BYTES [ .. ] )
. expect ( " block should deserialize " ) ;
let mut block = Arc ::try_unwrap ( block ) . expect ( " block should unwrap " ) ;
let height = block . coinbase_height ( ) . unwrap ( ) ;
let hash = block . hash ( ) ;
// Set the difficulty field to an invalid value
2022-07-21 16:15:22 -07:00
Arc ::make_mut ( & mut block . header ) . difficulty_threshold = INVALID_COMPACT_DIFFICULTY ;
2020-10-12 20:38:49 -07:00
// Validate the block
let result =
check ::difficulty_is_valid ( & block . header , Network ::Mainnet , & height , & hash ) . unwrap_err ( ) ;
let expected = BlockError ::InvalidDifficulty ( height , hash ) ;
assert_eq! ( expected , result ) ;
// Get a block in the testnet, but tell the validator it's from the mainnet
let block =
Arc ::< Block > ::zcash_deserialize ( & zebra_test ::vectors ::BLOCK_TESTNET_925483_BYTES [ .. ] )
. expect ( " block should deserialize " ) ;
let block = Arc ::try_unwrap ( block ) . expect ( " block should unwrap " ) ;
let height = block . coinbase_height ( ) . unwrap ( ) ;
let hash = block . hash ( ) ;
let difficulty_threshold = block . header . difficulty_threshold . to_expanded ( ) . unwrap ( ) ;
// Validate the block as if it is a mainnet block
let result =
check ::difficulty_is_valid ( & block . header , Network ::Mainnet , & height , & hash ) . unwrap_err ( ) ;
let expected = BlockError ::TargetDifficultyLimit (
height ,
hash ,
difficulty_threshold ,
Network ::Mainnet ,
2024-03-12 14:41:44 -07:00
Network ::Mainnet . target_difficulty_limit ( ) ,
2020-10-12 20:38:49 -07:00
) ;
assert_eq! ( expected , result ) ;
// Get a block in the mainnet, but pass an easy hash to the validator
let block =
Arc ::< Block > ::zcash_deserialize ( & zebra_test ::vectors ::BLOCK_MAINNET_415000_BYTES [ .. ] )
. expect ( " block should deserialize " ) ;
let block = Arc ::try_unwrap ( block ) . expect ( " block should unwrap " ) ;
let height = block . coinbase_height ( ) . unwrap ( ) ;
let bad_hash = block ::Hash ( [ 0xff ; 32 ] ) ;
let difficulty_threshold = block . header . difficulty_threshold . to_expanded ( ) . unwrap ( ) ;
// Validate the block
let result = check ::difficulty_is_valid ( & block . header , Network ::Mainnet , & height , & bad_hash )
. unwrap_err ( ) ;
2020-11-04 18:57:31 -08:00
let expected =
BlockError ::DifficultyFilter ( height , bad_hash , difficulty_threshold , Network ::Mainnet ) ;
2020-10-12 20:38:49 -07:00
assert_eq! ( expected , result ) ;
Ok ( ( ) )
}
2020-10-12 15:33:44 -07:00
#[ test ]
fn equihash_is_valid_for_historical_blocks ( ) -> Result < ( ) , Report > {
2022-08-04 08:44:44 -07:00
let _init_guard = zebra_test ::init ( ) ;
2020-11-09 16:29:25 -08:00
2020-10-12 15:33:44 -07:00
let block_iter = zebra_test ::vectors ::BLOCKS . iter ( ) ;
for block in block_iter {
let block = block
. zcash_deserialize_into ::< Block > ( )
. expect ( " block is structurally valid " ) ;
check ::equihash_solution_is_valid ( & block . header )
. expect ( " the equihash solution from a historical block should be valid " ) ;
}
Ok ( ( ) )
}
2020-10-12 13:54:48 -07:00
#[ test ]
2020-10-12 15:28:14 -07:00
fn subsidy_is_valid_for_historical_blocks ( ) -> Result < ( ) , Report > {
2022-08-04 08:44:44 -07:00
let _init_guard = zebra_test ::init ( ) ;
2020-11-09 16:29:25 -08:00
2020-10-12 14:38:07 -07:00
subsidy_is_valid_for_network ( Network ::Mainnet ) ? ;
subsidy_is_valid_for_network ( Network ::Testnet ) ? ;
2020-10-12 13:54:48 -07:00
Ok ( ( ) )
}
2020-10-12 14:38:07 -07:00
fn subsidy_is_valid_for_network ( network : Network ) -> Result < ( ) , Report > {
2024-03-05 06:12:25 -08:00
let block_iter = network . block_iter ( ) ;
2020-10-12 15:52:23 -07:00
2020-10-12 13:54:48 -07:00
for ( & height , block ) in block_iter {
let block = block
. zcash_deserialize_into ::< Block > ( )
. expect ( " block is structurally valid " ) ;
2022-01-28 14:14:46 -08:00
let canopy_activation_height = NetworkUpgrade ::Canopy
. activation_height ( network )
. expect ( " Canopy activation height is known " ) ;
2020-10-12 13:54:48 -07:00
// TODO: first halving, second halving, third halving, and very large halvings
2022-01-28 14:14:46 -08:00
if block ::Height ( height ) > = canopy_activation_height {
2020-10-12 14:41:24 -07:00
check ::subsidy_is_valid ( & block , network ) . expect ( " subsidies should pass for this block " ) ;
2020-10-12 13:54:48 -07:00
}
}
2020-10-12 15:28:14 -07:00
2020-10-12 13:54:48 -07:00
Ok ( ( ) )
}
#[ test ]
2020-10-12 15:52:23 -07:00
fn coinbase_validation_failure ( ) -> Result < ( ) , Report > {
2022-08-04 08:44:44 -07:00
let _init_guard = zebra_test ::init ( ) ;
2020-10-12 13:54:48 -07:00
let network = Network ::Mainnet ;
2022-01-28 14:14:46 -08:00
// Get a block in the mainnet that is inside the funding stream period,
2020-10-12 15:52:23 -07:00
// and delete the coinbase transaction
2020-10-12 13:54:48 -07:00
let block =
2022-01-28 14:14:46 -08:00
Arc ::< Block > ::zcash_deserialize ( & zebra_test ::vectors ::BLOCK_MAINNET_1046400_BYTES [ .. ] )
2020-10-12 13:54:48 -07:00
. expect ( " block should deserialize " ) ;
let mut block = Arc ::try_unwrap ( block ) . expect ( " block should unwrap " ) ;
// Remove coinbase transaction
block . transactions . remove ( 0 ) ;
2020-10-12 15:52:23 -07:00
// Validate the block using coinbase_is_first
let result = check ::coinbase_is_first ( & block ) . unwrap_err ( ) ;
let expected = BlockError ::NoTransactions ;
assert_eq! ( expected , result ) ;
// Validate the block using subsidy_is_valid
let result = check ::subsidy_is_valid ( & block , network ) . unwrap_err ( ) ;
let expected = BlockError ::Transaction ( TransactionError ::Subsidy ( SubsidyError ::NoCoinbase ) ) ;
assert_eq! ( expected , result ) ;
2022-01-28 14:14:46 -08:00
// Get another funding stream block, and delete the coinbase transaction
2020-10-12 15:52:23 -07:00
let block =
2022-01-28 14:14:46 -08:00
Arc ::< Block > ::zcash_deserialize ( & zebra_test ::vectors ::BLOCK_MAINNET_1046401_BYTES [ .. ] )
2020-10-12 15:52:23 -07:00
. expect ( " block should deserialize " ) ;
let mut block = Arc ::try_unwrap ( block ) . expect ( " block should unwrap " ) ;
// Remove coinbase transaction
block . transactions . remove ( 0 ) ;
// Validate the block using coinbase_is_first
let result = check ::coinbase_is_first ( & block ) . unwrap_err ( ) ;
let expected = BlockError ::Transaction ( TransactionError ::CoinbasePosition ) ;
assert_eq! ( expected , result ) ;
// Validate the block using subsidy_is_valid
2020-10-12 14:41:24 -07:00
let result = check ::subsidy_is_valid ( & block , network ) . unwrap_err ( ) ;
2020-10-12 13:54:48 -07:00
let expected = BlockError ::Transaction ( TransactionError ::Subsidy ( SubsidyError ::NoCoinbase ) ) ;
assert_eq! ( expected , result ) ;
2022-01-28 14:14:46 -08:00
// Get another funding stream, and duplicate the coinbase transaction
2020-10-12 15:52:23 -07:00
let block =
2022-01-28 14:14:46 -08:00
Arc ::< Block > ::zcash_deserialize ( & zebra_test ::vectors ::BLOCK_MAINNET_1180900_BYTES [ .. ] )
2020-10-12 15:52:23 -07:00
. expect ( " block should deserialize " ) ;
let mut block = Arc ::try_unwrap ( block ) . expect ( " block should unwrap " ) ;
// Remove coinbase transaction
block . transactions . push (
block
. transactions
2023-10-30 05:21:05 -07:00
. first ( )
2020-10-12 15:52:23 -07:00
. expect ( " block has coinbase " )
. clone ( ) ,
) ;
// Validate the block using coinbase_is_first
let result = check ::coinbase_is_first ( & block ) . unwrap_err ( ) ;
2021-04-27 17:43:00 -07:00
let expected = BlockError ::Transaction ( TransactionError ::CoinbaseAfterFirst ) ;
2020-10-12 15:52:23 -07:00
assert_eq! ( expected , result ) ;
// Validate the block using subsidy_is_valid, which does not detect this error
check ::subsidy_is_valid ( & block , network )
. expect ( " subsidy does not check for extra coinbase transactions " ) ;
2020-10-12 13:54:48 -07:00
Ok ( ( ) )
}
2021-11-08 14:33:12 -08:00
#[ test ]
fn funding_stream_validation ( ) -> Result < ( ) , Report > {
2022-08-04 08:44:44 -07:00
let _init_guard = zebra_test ::init ( ) ;
2021-11-08 14:33:12 -08:00
funding_stream_validation_for_network ( Network ::Mainnet ) ? ;
funding_stream_validation_for_network ( Network ::Testnet ) ? ;
Ok ( ( ) )
}
fn funding_stream_validation_for_network ( network : Network ) -> Result < ( ) , Report > {
2024-03-05 06:12:25 -08:00
let block_iter = network . block_iter ( ) ;
2021-11-08 14:33:12 -08:00
2022-01-28 14:14:46 -08:00
let canopy_activation_height = NetworkUpgrade ::Canopy
. activation_height ( network )
. expect ( " Canopy activation height is known " ) ;
2021-11-08 14:33:12 -08:00
for ( & height , block ) in block_iter {
2022-01-28 14:14:46 -08:00
if Height ( height ) > = canopy_activation_height {
2021-11-08 14:33:12 -08:00
let block = Block ::zcash_deserialize ( & block [ .. ] ) . expect ( " block should deserialize " ) ;
// Validate
let result = check ::subsidy_is_valid ( & block , network ) ;
assert! ( result . is_ok ( ) ) ;
}
}
Ok ( ( ) )
}
#[ test ]
fn funding_stream_validation_failure ( ) -> Result < ( ) , Report > {
2022-08-04 08:44:44 -07:00
let _init_guard = zebra_test ::init ( ) ;
2021-11-08 14:33:12 -08:00
let network = Network ::Mainnet ;
// Get a block in the mainnet that is inside the funding stream period.
let block =
Arc ::< Block > ::zcash_deserialize ( & zebra_test ::vectors ::BLOCK_MAINNET_1046400_BYTES [ .. ] )
. expect ( " block should deserialize " ) ;
// Build the new transaction with modified coinbase outputs
let tx = block
. transactions
2023-10-30 05:21:05 -07:00
. first ( )
2021-11-23 19:36:17 -08:00
. map ( | transaction | {
let mut output = transaction . outputs ( ) [ 0 ] . clone ( ) ;
output . value = Amount ::try_from ( i32 ::MAX ) . unwrap ( ) ;
Transaction ::V4 {
inputs : transaction . inputs ( ) . to_vec ( ) ,
outputs : vec ! [ output ] ,
lock_time : transaction . lock_time ( ) . unwrap_or_else ( LockTime ::unlocked ) ,
expiry_height : Height ( 0 ) ,
joinsplit_data : None ,
sapling_shielded_data : None ,
}
2021-11-08 14:33:12 -08:00
} )
. unwrap ( ) ;
// Build new block
let transactions : Vec < Arc < zebra_chain ::transaction ::Transaction > > = vec! [ Arc ::new ( tx ) ] ;
let block = Block {
2022-07-21 16:15:22 -07:00
header : block . header . clone ( ) ,
2021-11-08 14:33:12 -08:00
transactions ,
} ;
// Validate it
2021-11-23 19:36:17 -08:00
let result = check ::subsidy_is_valid ( & block , network ) ;
let expected = Err ( BlockError ::Transaction ( TransactionError ::Subsidy (
2021-11-08 14:33:12 -08:00
SubsidyError ::FundingStreamNotFound ,
2021-11-23 19:36:17 -08:00
) ) ) ;
assert_eq! ( expected , result ) ;
Ok ( ( ) )
}
#[ test ]
fn miner_fees_validation_success ( ) -> Result < ( ) , Report > {
2022-08-04 08:44:44 -07:00
let _init_guard = zebra_test ::init ( ) ;
2021-11-23 19:36:17 -08:00
miner_fees_validation_for_network ( Network ::Mainnet ) ? ;
miner_fees_validation_for_network ( Network ::Testnet ) ? ;
Ok ( ( ) )
}
fn miner_fees_validation_for_network ( network : Network ) -> Result < ( ) , Report > {
2024-03-05 06:12:25 -08:00
let block_iter = network . block_iter ( ) ;
2021-11-23 19:36:17 -08:00
for ( & height , block ) in block_iter {
if Height ( height ) > SLOW_START_SHIFT {
let block = Block ::zcash_deserialize ( & block [ .. ] ) . expect ( " block should deserialize " ) ;
// fake the miner fee to a big amount
let miner_fees = Amount ::try_from ( MAX_MONEY / 2 ) . unwrap ( ) ;
// Validate
let result = check ::miner_fees_are_valid ( & block , network , miner_fees ) ;
assert! ( result . is_ok ( ) ) ;
}
}
Ok ( ( ) )
}
#[ test ]
fn miner_fees_validation_failure ( ) -> Result < ( ) , Report > {
2022-08-04 08:44:44 -07:00
let _init_guard = zebra_test ::init ( ) ;
2021-11-23 19:36:17 -08:00
let network = Network ::Mainnet ;
let block =
Arc ::< Block > ::zcash_deserialize ( & zebra_test ::vectors ::BLOCK_MAINNET_347499_BYTES [ .. ] )
. expect ( " block should deserialize " ) ;
// fake the miner fee to a small amount
let miner_fees = Amount ::zero ( ) ;
// Validate
let result = check ::miner_fees_are_valid ( & block , network , miner_fees ) ;
let expected = Err ( BlockError ::Transaction ( TransactionError ::Subsidy (
SubsidyError ::InvalidMinerFees ,
) ) ) ;
2021-11-08 14:33:12 -08:00
assert_eq! ( expected , result ) ;
Ok ( ( ) )
}
2020-10-12 15:21:15 -07:00
#[ test ]
2020-10-12 15:28:34 -07:00
fn time_is_valid_for_historical_blocks ( ) -> Result < ( ) , Report > {
2022-08-04 08:44:44 -07:00
let _init_guard = zebra_test ::init ( ) ;
2020-11-09 16:29:25 -08:00
2020-10-12 15:28:34 -07:00
let block_iter = zebra_test ::vectors ::BLOCKS . iter ( ) ;
2020-10-12 15:21:15 -07:00
let now = Utc ::now ( ) ;
2020-10-12 15:28:34 -07:00
for block in block_iter {
let block = block
. zcash_deserialize_into ::< Block > ( )
. expect ( " block is structurally valid " ) ;
// This check is non-deterministic, but the block test vectors are
// all in the past. So it's unlikely that the test machine will have
// a clock that's far enough in the past for the test to fail.
check ::time_is_valid_at (
& block . header ,
now ,
& block
. coinbase_height ( )
. expect ( " block test vector height should be valid " ) ,
& block . hash ( ) ,
)
. expect ( " the header time from a historical block should be valid, based on the test machine's local clock. Hint: check the test machine's time, date, and timezone. " ) ;
}
Ok ( ( ) )
2020-10-12 15:21:15 -07:00
}
2021-05-09 18:31:45 -07:00
#[ test ]
fn merkle_root_is_valid ( ) -> Result < ( ) , Report > {
2022-08-04 08:44:44 -07:00
let _init_guard = zebra_test ::init ( ) ;
2021-05-09 18:31:45 -07:00
// test all original blocks available, all blocks validate
merkle_root_is_valid_for_network ( Network ::Mainnet ) ? ;
merkle_root_is_valid_for_network ( Network ::Testnet ) ? ;
// create and test fake blocks with v5 transactions, all blocks fail validation
merkle_root_fake_v5_for_network ( Network ::Mainnet ) ? ;
merkle_root_fake_v5_for_network ( Network ::Testnet ) ? ;
Ok ( ( ) )
}
fn merkle_root_is_valid_for_network ( network : Network ) -> Result < ( ) , Report > {
2024-03-05 06:12:25 -08:00
let block_iter = network . block_iter ( ) ;
2021-05-09 18:31:45 -07:00
for ( _height , block ) in block_iter {
let block = block
. zcash_deserialize_into ::< Block > ( )
. expect ( " block is structurally valid " ) ;
let transaction_hashes = block
. transactions
. iter ( )
. map ( | tx | tx . hash ( ) )
. collect ::< Vec < _ > > ( ) ;
check ::merkle_root_validity ( network , & block , & transaction_hashes )
. expect ( " merkle root should be valid for this block " ) ;
}
Ok ( ( ) )
}
fn merkle_root_fake_v5_for_network ( network : Network ) -> Result < ( ) , Report > {
2024-03-05 06:12:25 -08:00
let block_iter = network . block_iter ( ) ;
2021-05-09 18:31:45 -07:00
for ( height , block ) in block_iter {
let mut block = block
. zcash_deserialize_into ::< Block > ( )
. expect ( " block is structurally valid " ) ;
// skip blocks that are before overwinter as they will not have a valid consensus branch id
if * height
< NetworkUpgrade ::Overwinter
. activation_height ( network )
. expect ( " a valid overwinter activation height " )
. 0
{
continue ;
}
// convert all transactions from the block to V5
let transactions : Vec < Arc < Transaction > > = block
. transactions
. iter ( )
. map ( AsRef ::as_ref )
. map ( | t | transaction_to_fake_v5 ( t , network , Height ( * height ) ) )
. map ( Into ::into )
. collect ( ) ;
block . transactions = transactions ;
let transaction_hashes = block
. transactions
. iter ( )
. map ( | tx | tx . hash ( ) )
. collect ::< Vec < _ > > ( ) ;
// Replace the merkle root so that it matches the modified transactions.
// This test provides some transaction id and merkle root coverage,
// but we also need to test against zcashd test vectors.
2022-07-21 16:15:22 -07:00
Arc ::make_mut ( & mut block . header ) . merkle_root = transaction_hashes . iter ( ) . cloned ( ) . collect ( ) ;
2021-05-09 18:31:45 -07:00
check ::merkle_root_validity ( network , & block , & transaction_hashes )
. expect ( " merkle root should be valid for this block " ) ;
}
Ok ( ( ) )
}
2021-11-15 12:55:32 -08:00
#[ test ]
fn legacy_sigops_count_for_large_generated_blocks ( ) {
2022-08-04 08:44:44 -07:00
let _init_guard = zebra_test ::init ( ) ;
2021-11-15 12:55:32 -08:00
// We can't test sigops using the transaction verifier, because it looks up UTXOs.
2022-03-09 15:34:50 -08:00
let block = large_single_transaction_block_many_inputs ( ) ;
2021-11-15 12:55:32 -08:00
let mut legacy_sigop_count = 0 ;
for transaction in block . transactions {
2022-01-31 07:28:42 -08:00
let cached_ffi_transaction =
Arc ::new ( CachedFfiTransaction ::new ( transaction . clone ( ) , Vec ::new ( ) ) ) ;
2021-11-15 12:55:32 -08:00
let tx_sigop_count = cached_ffi_transaction . legacy_sigop_count ( ) ;
assert_eq! ( tx_sigop_count , Ok ( 0 ) ) ;
legacy_sigop_count + = tx_sigop_count . expect ( " unexpected invalid sigop count " ) ;
}
// We know this block has no sigops.
assert_eq! ( legacy_sigop_count , 0 ) ;
let block = large_multi_transaction_block ( ) ;
let mut legacy_sigop_count = 0 ;
for transaction in block . transactions {
2022-01-31 07:28:42 -08:00
let cached_ffi_transaction =
Arc ::new ( CachedFfiTransaction ::new ( transaction . clone ( ) , Vec ::new ( ) ) ) ;
2021-11-15 12:55:32 -08:00
let tx_sigop_count = cached_ffi_transaction . legacy_sigop_count ( ) ;
assert_eq! ( tx_sigop_count , Ok ( 1 ) ) ;
legacy_sigop_count + = tx_sigop_count . expect ( " unexpected invalid sigop count " ) ;
}
// Test that large blocks can actually fail the sigops check.
assert! ( legacy_sigop_count > MAX_BLOCK_SIGOPS ) ;
}
#[ test ]
fn legacy_sigops_count_for_historic_blocks ( ) {
2022-08-04 08:44:44 -07:00
let _init_guard = zebra_test ::init ( ) ;
2021-11-15 12:55:32 -08:00
// We can't test sigops using the transaction verifier, because it looks up UTXOs.
for block in zebra_test ::vectors ::BLOCKS . iter ( ) {
let mut legacy_sigop_count = 0 ;
let block : Block = block
. zcash_deserialize_into ( )
. expect ( " block test vector is valid " ) ;
for transaction in block . transactions {
2022-01-31 07:28:42 -08:00
let cached_ffi_transaction =
Arc ::new ( CachedFfiTransaction ::new ( transaction . clone ( ) , Vec ::new ( ) ) ) ;
2021-11-15 12:55:32 -08:00
legacy_sigop_count + = cached_ffi_transaction
. legacy_sigop_count ( )
. expect ( " unexpected invalid sigop count " ) ;
}
// Test that historic blocks pass the sigops check.
assert! ( legacy_sigop_count < = MAX_BLOCK_SIGOPS ) ;
}
}
2021-11-22 21:17:05 -08:00
#[ test ]
2021-11-25 16:37:24 -08:00
fn transaction_expiration_height_validation ( ) -> Result < ( ) , Report > {
2022-08-04 08:44:44 -07:00
let _init_guard = zebra_test ::init ( ) ;
2021-11-22 21:17:05 -08:00
2021-11-25 16:37:24 -08:00
transaction_expiration_height_for_network ( Network ::Mainnet ) ? ;
transaction_expiration_height_for_network ( Network ::Testnet ) ? ;
2021-11-22 21:17:05 -08:00
Ok ( ( ) )
}
2021-11-25 16:37:24 -08:00
fn transaction_expiration_height_for_network ( network : Network ) -> Result < ( ) , Report > {
2024-03-05 06:12:25 -08:00
let block_iter = network . block_iter ( ) ;
2021-11-22 21:17:05 -08:00
for ( & height , block ) in block_iter {
let block = Block ::zcash_deserialize ( & block [ .. ] ) . expect ( " block should deserialize " ) ;
2021-11-25 16:37:24 -08:00
for ( n , transaction ) in block . transactions . iter ( ) . enumerate ( ) {
if n = = 0 {
// coinbase
let result = transaction ::check ::coinbase_expiry_height (
& Height ( height ) ,
transaction ,
network ,
) ;
assert! ( result . is_ok ( ) ) ;
} else {
// non coinbase
let result =
transaction ::check ::non_coinbase_expiry_height ( & Height ( height ) , transaction ) ;
assert! ( result . is_ok ( ) ) ;
}
}
2021-11-22 21:17:05 -08:00
}
Ok ( ( ) )
}