Validate coinbase expiration height (#3082)

* add testnet test blocks around nu5

* validate coinbase expiration height

* change const name and doc

Co-authored-by: teor <teor@riseup.net>

* change commit location

Co-authored-by: teor <teor@riseup.net>

* use pre Nu5 rules when there is no activation height

* add sapling final root to nu5 test vectors

* fix tests

Co-authored-by: teor <teor@riseup.net>
This commit is contained in:
Alfredo Garcia 2021-11-23 02:17:05 -03:00 committed by GitHub
parent 29d5da320f
commit dbd49a3f00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 139 additions and 10 deletions

View File

@ -48,6 +48,14 @@ impl Height {
/// `Height::MAX.0` can't be used in match range patterns, use this
/// alias instead.
pub const MAX_AS_U32: u32 = Self::MAX.0;
/// The maximum expiration Height that is allowed in all transactions
/// previous to Nu5 and in non-coinbase transactions from Nu5 activation height
/// and above.
///
/// TODO: This is currently the same as `Height::MAX` but that change in #1113.
/// Remove this TODO when that happens.
pub const MAX_EXPIRY_HEIGHT: Height = Height(499_999_999);
}
impl Add<Height> for Height {

View File

@ -801,7 +801,7 @@ pub fn transaction_to_fake_v5(
inputs: inputs.to_vec(),
outputs: outputs.to_vec(),
lock_time: *lock_time,
expiry_height: block::Height(0),
expiry_height: height,
sapling_shielded_data: None,
orchard_shielded_data: None,
},
@ -815,7 +815,7 @@ pub fn transaction_to_fake_v5(
inputs: inputs.to_vec(),
outputs: outputs.to_vec(),
lock_time: *lock_time,
expiry_height: block::Height(0),
expiry_height: height,
sapling_shielded_data: None,
orchard_shielded_data: None,
},
@ -823,14 +823,13 @@ pub fn transaction_to_fake_v5(
inputs,
outputs,
lock_time,
expiry_height,
..
} => V5 {
network_upgrade: block_nu,
inputs: inputs.to_vec(),
outputs: outputs.to_vec(),
lock_time: *lock_time,
expiry_height: *expiry_height,
expiry_height: height,
sapling_shielded_data: None,
orchard_shielded_data: None,
},
@ -838,7 +837,6 @@ pub fn transaction_to_fake_v5(
inputs,
outputs,
lock_time,
expiry_height,
sapling_shielded_data,
..
} => V5 {
@ -846,7 +844,7 @@ pub fn transaction_to_fake_v5(
inputs: inputs.to_vec(),
outputs: outputs.to_vec(),
lock_time: *lock_time,
expiry_height: *expiry_height,
expiry_height: height,
sapling_shielded_data: sapling_shielded_data
.clone()
.map(sapling_shielded_v4_to_fake_v5)

View File

@ -499,11 +499,18 @@ fn fake_v5_librustzcash_round_trip_for_network(network: Network) {
.expect("a valid height")
.0;
// skip blocks that are before overwinter as they will not have a valid consensus branch id
let blocks_after_overwinter =
block_iter.skip_while(|(height, _)| **height < overwinter_activation_height);
let nu5_activation_height = NetworkUpgrade::Nu5
.activation_height(network)
.unwrap_or(Height::MAX_EXPIRY_HEIGHT)
.0;
for (height, original_bytes) in blocks_after_overwinter {
// skip blocks that are before overwinter as they will not have a valid consensus branch id
// skip blocks equal or greater Nu5 activation as they are already v5 transactions
let blocks_after_overwinter_and_before_nu5 = block_iter
.skip_while(|(height, _)| **height < overwinter_activation_height)
.take_while(|(height, _)| **height < nu5_activation_height);
for (height, original_bytes) in blocks_after_overwinter_and_before_nu5 {
let original_block = original_bytes
.zcash_deserialize_into::<Block>()
.expect("block is structurally valid");

View File

@ -188,6 +188,9 @@ where
tx::check::coinbase_outputs_are_decryptable(coinbase_tx, network, height)?;
check::subsidy_is_valid(&block, network)?;
// Validate `nExpiryHeight` consensus rules
check::coinbase_expiry_height(&height, coinbase_tx, network)?;
let mut async_checks = FuturesUnordered::new();
let known_utxos = Arc::new(transparent::new_ordered_outputs(

View File

@ -248,3 +248,48 @@ pub fn merkle_root_validity(
Ok(())
}
/// Returns `Ok(())` if the expiry height for the coinbase transaction is valid
/// according to specifications [7.1] and [ZIP-203].
///
/// [7.1]: https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus
/// [ZIP-203]: https://zips.z.cash/zip-0203
pub fn coinbase_expiry_height(
height: &Height,
coinbase: &transaction::Transaction,
network: Network,
) -> Result<(), BlockError> {
match NetworkUpgrade::Nu5.activation_height(network) {
// If Nu5 does not have a height, apply the pre-Nu5 rule.
None => validate_expiry_height_max(coinbase.expiry_height()),
Some(activation_height) => {
// Conesnsus rule: from NU5 activation, the nExpiryHeight field of a
// coinbase transaction MUST be set equal to the block height.
if *height >= activation_height {
match coinbase.expiry_height() {
None => Err(TransactionError::CoinbaseExpiration)?,
Some(expiry) => {
if expiry != *height {
return Err(TransactionError::CoinbaseExpiration)?;
}
}
}
return Ok(());
}
// Consensus rule: [Overwinter to Canopy inclusive, pre-NU5] nExpiryHeight
// MUST be less than or equal to 499999999.
validate_expiry_height_max(coinbase.expiry_height())
}
}
}
/// Validate the consensus rule: nExpiryHeight MUST be less than or equal to 499999999.
fn validate_expiry_height_max(expiry_height: Option<Height>) -> Result<(), BlockError> {
if let Some(expiry) = expiry_height {
if expiry > Height::MAX_EXPIRY_HEIGHT {
return Err(TransactionError::CoinbaseExpiration)?;
}
}
Ok(())
}

View File

@ -661,3 +661,34 @@ fn legacy_sigops_count_for_historic_blocks() {
assert!(legacy_sigop_count <= MAX_BLOCK_SIGOPS);
}
}
#[test]
fn coinbase_height_validation() -> Result<(), Report> {
zebra_test::init();
coinbase_height_for_network(Network::Mainnet)?;
coinbase_height_for_network(Network::Testnet)?;
Ok(())
}
fn coinbase_height_for_network(network: Network) -> Result<(), Report> {
let block_iter = match network {
Network::Mainnet => zebra_test::vectors::MAINNET_BLOCKS.iter(),
Network::Testnet => zebra_test::vectors::TESTNET_BLOCKS.iter(),
};
for (&height, block) in block_iter {
let block = Block::zcash_deserialize(&block[..]).expect("block should deserialize");
// Validate
let result = check::coinbase_expiry_height(
&Height(height),
block.transactions.get(0).unwrap(),
network,
);
assert!(result.is_ok());
}
Ok(())
}

View File

@ -56,6 +56,9 @@ pub enum TransactionError {
#[error("coinbase inputs MUST NOT exist in mempool")]
CoinbaseInMempool,
#[error("coinbase expiration height is invalid")]
CoinbaseExpiration,
#[error("coinbase transaction failed subsidy validation")]
#[cfg_attr(any(test, feature = "proptest-impl"), proptest(skip))]
Subsidy(#[from] SubsidyError),

View File

@ -0,0 +1 @@
04000000b26e0c796bca49d9f3728e922af97a3e256f4ec5e742f3ac04c68284fd344900b21c06e5efbab781a9e24cd6e05fae07d5fc7ba2afa9fb041e251c4cfffcf9d437333ddce980f8998f03dcb9dabd6e604f9316db5928918fe7630b3bc944b02764365d617e046f1f22000d646c8255fc55033c901d155342d73ad69d7651df2eb9b8988392b50000fd4005014a93d51a057de5f208135b6de23b09892f4ee19724bcb23afb5c6c3721487509d52fc42a4a67b3d8bb0ea1b14adc87ee60ec7426b9fbdd605735af9aafd423fe9473f9554c835e0e14d1acef041239f676c8a301e5b3afc812c8b7943dc6637b6eb8761bf9d68add1131b5fc8d8b4a5a6ec574fa43d19442af5fd62df20a250cf824ca91fcaadc00e9f0188cea505fd6a7665fa51587afbb1125f71016963fdae7ca3c827775a70c7d9080905607ef4ddbd664dac41e420b8abb8bf81e1d03af4c9fc21944629800cacf375705d3f9a94312f9dd29f6ddb56fc0aa65f2a4b35b264af57c67dd5157b68651d715c3fea987f96070d1b63e9ad2f0170d729772064e4a1f446c431136d5d47edc8a592636156c1a3e4dca9d070a0764c3534bd35f47b21dd61112213d82dded6453a0bfc301b3a735da4c5977457e22451c9c2c1312ebbd9fe3b067d926515420528a1e0199d8c0c80e2e48d02da0258fe70c008b7fb3ee7435635b145d11301ba85cd541d95029da7f1f7611e60e21e6ef49c5c6c717a67137dd24b248aa35a82ac612c27be3c278cea5dff6a39783557e7581bcd0db5c0cfdfdbaa2cebc4bd84894b742f9504263d87593d31cbad0e9d20c3527919df7cbf1de3e2e414f9e66c30d8d8ceefc8a1851345e829263c0ed8ed279ffaeb65f002b4af7a83aa9f49e25f590f22d71ed00997e43049d0c85b1e1eee12672f4fcdd6025e943da133f1227f8e6a8f531c801dfc5a7a85f75bee5f1f37a8e0c12c9ae80d617a3d6f4ed742d6e3c5965d7d81e21f73911c7d41d558745cd021673bc53da121a6dff82510688453d908674dcf52e54e8c7c25c5d8fd33c796a1788a2a86724743db33b321c13c3ac1103c35c47b21c596c946d8c974e997083d28a676e211f23145afb272fee8037b35fc1de9b22d2b599c84e5f52df43d30217585e790b609d0f3b90397237bba93a8c9ac0fa0ef6ceabe9a9329b6982793492f7bb6eda5ad96e92041682b5b1918ae531b061709136740cd656b3862705203f89e2d41caea4408136f1dd544920a811bc95022ff575af24e3c7ec7a262b7144c8263d40f84db42c27e379ba0b417d77e694b46bdc71fb7791bdb5d80403b2141d5be888e9214118eaad58718c891532f1146bacc28fdff5afef82414a1d9e0054976eb407e6037ad7cace999a6743eb32e9e8b8d71da5c92e5c001c1edd03ed70e137b0da02c6723b8d83142e1ce09603db9b8132891975876f35f2ab347eaa1f7534279a06f25f2e30f78b91d048817c1cc1cc058328edc268145baaf3f6cb42f17f9732bbe142d435d45c9c8cd777fce3e58a6f057f9e40f7ffe66f58da209b5d69da1fbaa3bb3f11d27f9f8b83ab6ea74809c6b877932a1fcd99abe1a43469ceaf155aa0aee7496223b3260502430a072816567bf563a24eeef9a4bd7082d53cf6103c6d25ac65a9a9e55ba827cbda3f1a8692feace204bb95a74814544b2cc0f15eeaca87ec74c1f1d4ef3c86f6538cb394dfa3df7569ba48f79e32bf5246dd092f3518b01a1f45699a64ea51c01ddb47121b5d7c35699ef9d590bab9f216d6bdbc65a085e19a8f44d30ec9a92dc00c47d69f4d741ec1ee3deee7e0b9021134191cdecc1215e366e4bb031dfea632d7f75a588e05c586298173dbe9a19a11e254a50ad12aa82bcf345263b306de295cff73ce17ff914cdcc6655e1dc3820870ef26d749e6ab7c7071bbc8e98821ab025220f908a36e11ca4f29bac17324188acfcbe11acbd03c9a0a338b5ed0d63f86c0f23608d5b8552d92f799ab0c3e6995c2981c79ad5d9195d04fe0e713108added942a1adde96ca14d9bb525f697cefac2f23e62720e254da847763f1efd15f10b857ed9c2eb1ae3d23d1d81010400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff0603df66180101ffffffff0480b2e60e000000001976a91403e105ea5dbb243cddada1ec31cebd92266cd22588ac286bee000000000017a9140c0bcca02f3cba01a5d7423ac3903d40586399eb8738c94d010000000017a914221c95f83bf073cfb76724ed23af737c1ee3bb498740787d010000000017a91471e1df05024288a00802de81e08c437859586c878700000000000000000000000000000000000000

View File

@ -0,0 +1 @@
04000000ed34eead710be6b5da373edd2e5515407a0d9f5f2b511f09f1ddabd3764e27008039c40bd3ab96700f2242b1200608d4a1c74264bc71b3f956a2339b4606957ba7d9248f4d9300eca388220a40b4e98ab8f0459af40d74dff3bb07a9691df4487e365d61a94e6d1f2a00ab6acaf882cba8e6f322ab507bf2c349664286203c6f194b1708250c0000fd40050008feecb65909d17f1120a372873dacc148305f700e82b5b34e8898135857f4a4f3cc568ad4d0bed15913e4461ed0d39e56a038737e664414b53b5db2d763666c1d99096ce943ebc9c9fb7fd665be8c049b6218178d33323a94502f1afa91eb9552f254b9796afa3f2a80dff1dc1dfae350eb733ee840512630fef7658525ad7740256720d1a469725d0b2fe8f6a8161807843933a43b83245073b4fd161c38cdce0ac57bd9629e0ce5961d33848464ee2fb1cc64529f1588820e06e62d52a476721bdf95add623872aef7cc13622b58103182b969f0d66b38f8351f20d0919e3a111e3f74f843746877d349a75eed5df74983fc3726e15fbf1bcce1054e38a2096ef4fc087f10c6fae6fb1a0fd78c4cd343bb3f52d939d36d204e3b55c3e26599ed2dcb2ee2b541195d06757a58908a536c04d83b67581bed02e3f72c584122ff27dadd6657e61b5375f092cfb4cf700785920fd8e3b92f58ba3d98be05a799257dd02945c8c9b0637a5d39bc3ac16d491f7355af88a9be77212d248bace52cab1dab316e83dfa7ae208a532d7ab16157abd0b299b377927d3db496e832db82739b9a91abeef2ea7942c2f1893b310969ff051786352f5884e8a572c8317d659efe1089f7a4e68bef3135ea9501e9b15c4ec91cfa1c875435cbbfda4421eab78f0d42439bde9230965617ef5c5b099fff24a52d139d07e07f1388f8caf6e25a7be438a487ad9b2f46e3d029d3831cb9337b6fcb3bb53c4ee9cc79d4b894ebd02d60e125f220c543ed1f5adb320cc3da5026bf41ecc9b15497a39662a66315de4f488f2cbe4f57d4b0d14e80be341a3678ccfb9aa1011c86934d8619324cea3a71768a37dd2ce37d59730484e5dccde868ee3fc2aef1befe9c117504c01a6325a5de16824c70e73f9267844666bc043d848fd72d7289a83ec15a2c1eefb08cd0085657436e2fca122681375d86109d986a6b6a1d110cc656138cd90b89422aae8a27da27b6e7abf9dcf0b21c3370cf453a9eafe12331a72a85338dddf45af34234cd3f50db21b6c5a67eea8e2e4965618bf9f3e0abeec58e91d4d55b138416743b3f179496addcb7633b9dfcacf128ff17e471368c5e7469246145e690c0b54f492d4c50cf7d40f80d36e7b25607c8d2a4bfe19073da2b82a1ce3ca6ac4fc12f6b4c9ea185ca61401cbfd34f09f1cff1ae9119c9fc09d00f07c56e01e0ce223c7b1c7066accf135917ac60e79f2a117c72e108012c5342f79c9938f32f1ca77445cca50ef81dc2a95ad681f4c6b44be22e4afc8bc7b11718b58ea3e02b4a48b4a8f90cefb4b804443b6b5e83b0af55a7a1ebb7b4409cfa2134c5e291dd2750eaac6131c0d4710bbda6d9b4ead5ce43681cd7ac8dc27c3081e76775f959f2692a1c5d3e8bd0629c54cadae408bd5149a0129e966b8091e2eba31b294ff5d47c0f91655691917ef791dc912f5c78bc373285d36d57ea68f9971ce0bb0fb4e4d1970637a8fa24dc858e914bfbfa9953320dfd474a36bcc39ffcff67becfb7122c2885a0aa605b09470e5456d349b5eb32685fdb460ce7bcaf75f13b927536de5a29530b6e27da228736cae7abe4a490b358c38424ce53ac123e419acec6aa1c28936b38d10edcb2c2790a282af0d8302fe734d58f507fec0470410fd20bb615f65f516a0921bd8ca8cc494ade81313ac0b2411e24d57d3f5ca742dd91e12ae27dfe74e07f9fc1c1b26ba7574d544095d30495efb0efe3eaf438c2e1c6b1a635f8ea4d4fd362948e20d85de15f80d22a3e8815f995f58bce5afe3cd79918dcdb25a09160a1a59d1d0d33b6535a1fe58920f06cca21d562716b2ee3a91df66639bb4921b93ff4b58bab61668ab295b49f77367d513e0c9088e55dada66fe65dc7d5601050000800a27a7262196513700000000e0661800010000000000000000000000000000000000000000000000000000000000000000ffffffff0603e066180102ffffffff0480b2e60e000000001976a9146e82b7c719334e3aac3916872a088b548c43c9fb88ac286bee000000000017a9140c0bcca02f3cba01a5d7423ac3903d40586399eb8738c94d010000000017a914221c95f83bf073cfb76724ed23af737c1ee3bb498740787d010000000017a91471e1df05024288a00802de81e08c437859586c8787000000

View File

@ -0,0 +1 @@
04000000da438160fc9c57ca00c5251e689f9c8b21e1f84fcd645b95c0a85e9948e43100f6c3f2ef2b6f542ad77967e75314f45c509033eb887b09828ff9137c2b73ef4752da86ea6380c2353ec868cba9a16edb3b1a483a2d084ae76ab4404b2d929b048f365d61149c6c1f1c007c736075333a175f237e246890a5906e0c065d4adfedcb16fecaca250000fd4005002699b2262a65cdf85c128de3aaa471314b5c2cbb0e307fbdc2046603836141116974b80890ed989b7b19890642cc641f17fd31b7a92c7d74af40cbdefda224896b818b9baad78d22e2fa0ba66f2dd7251fe241095ced9f9249c4d7955392c4a228fa6e6bfb973c6e0d632ec3bcc389eaa15bb3c3193536862f4071a3b92bb0f227c10e3120f9b42331204428d6585b1abf396dda3c47b31ef2a5887979ae9a7efc534c7c3c3cc407355da6c927245f6ea0e15f0d1d7ed13b73d48f8021b415f63d93e04fa3a4644e3c529926a4d976d81e13a7c965cfb7e1b9d812b7c01af0bff6a4aa9ad00819e92754c1d9c5c12427e4434c2670c33df93d6eb41fbc7684220ec890a62a93ec3e4c7eb5329c5cece735eebdfa670d97c50b6689b9e0fb713f8e39fdd8e4223e3aa8abe23de3e4fc94b6fcf2708930abf88aac55ca4dd5f2d80998c678285e7ad52186ffef7f229800a24f98f9d8b0995beff6e980d17f4e73f9da5b3603da84b20f876d57eb53b3eedbcf17b6cf561e9ccf2f41570f009e4131c3f983d81ef3fb65a66d70e6114255c5b46559b0fdca9c4a51fee1c146d117fb235c1150d22b2b45848bec53a5fc7bb1a98757003e0f2c49d98e949cdd69b7340be5df7ddb553668425efb6a123bd981e425cb29e96d31898f250815c644b9365d1835316d520e234ccd22c8c459488fbee8e6fcb66202c83ed96dd298933a32f251ddc96f69b788759c7d1319baada1924e137eede59400512bea082a71ad020c120aa559c72bc7690ad163b7af65a1dc7d1d635c3a78154f57a5fec9ed1327f9d2f6749a0a08d72a4e05e1e840e82b3d61e15cc3684a4847f79cfdbd1b2506a998fc018c636c6dcc74a4fc61690abd06da5be806c1a9e44184dcc0f602b340aa72716a03f435671a2230a4699d568a44cbe043261a46eff0de687852470138a4b0bd20b685bd011a1e89fc9c731ba35c2ba90f8d17a86d3069f38ef3827076a8a4553b844d5dc60289e4638707f5aecfdaf25255d62b54b4032c8d5f086306f1b34466407508012dd59d14ea80025dd0270372bce36d49e8c9db0c0224e118e2455c42cf038b0cd955cda1b15ac9964ce1132f24daf05f43d0e8c226f9fe7080187c997798136b013dbb876b4d1c314b566af314b11f5e779614b5b8a878439d8727bb45a601eeebaf58d3f6c93f77a17b5370eb199e33d698010b345f0e0649677e985e923d12b6ab7d0c3aaf48720741d65e611979edd512368ab8e6b5a5bb9777fd245079733d5e57324b2089c61f5bbf921ecebd970eb6036f56c937cba3fba4dca34c293e01aa7e7fdaf1dd127df1c17d8d7b19dad363cbfe69c79d24107840aa0bfc447158535d772b3453fa2ce7a99e87bbd8f6a5160c7538a993e9b1251c8396e8bc678a56353662b401b72494df058c6e4941f13ad05655ca34d571f93d2e175a2cb8937259668dd3dcf9e4c81edc3fd77464167affc3c39e34bff852c3c45cbde28f9b9a5f8a7e54763dc2fd970271cade79ae49d40a8706853eb116185a6eeef82745d18957e2ce01169c38bbc31c68684a47dbe3de542463771e9711de6c3476291034f5032ba18e8e924c4f30c4df96dbe15f2a5a55503a9cdd40a8f62fc5a70107df34a68cc6756c33a137be63540d2551218a248fe92dae21d922bef56a42d7bda739383ad9fd27d63c74d1d2253c752fc1f57e852e8bc912a8cb8f12d5b50d97569560d8661afdb00b7c711e23a31d1d025f13a9779343268b5233f9af589a9b2a17b7e9915dcc65c39c4a5612b3f74b6a30c8762280352b122afad7caafd1ae1883e7c5de831d539c605c21376469eb113e88abbcf5578c6b2e56445bdc6f6321b8eff24e97d43ecfcc62d1f4ebd4acfd63d4c69701050000800a27a7262196513700000000e1661800010000000000000000000000000000000000000000000000000000000000000000ffffffff0603e166180101ffffffff0480b2e60e000000001976a9146e82b7c719334e3aac3916872a088b548c43c9fb88ac286bee000000000017a9140c0bcca02f3cba01a5d7423ac3903d40586399eb8738c94d010000000017a914221c95f83bf073cfb76724ed23af737c1ee3bb498740787d010000000017a91471e1df05024288a00802de81e08c437859586c8787000000

View File

@ -220,6 +220,10 @@ lazy_static! {
(1_116_000, BLOCK_TESTNET_1116000_BYTES.as_ref()),
(1_116_001, BLOCK_TESTNET_1116001_BYTES.as_ref()),
(1_326_100, BLOCK_TESTNET_1326100_BYTES.as_ref()),
(1_599_199, BLOCK_TESTNET_1599199_BYTES.as_ref()),
// Nu5
(1_599_200, BLOCK_TESTNET_1599200_BYTES.as_ref()),
(1_599_201, BLOCK_TESTNET_1599201_BYTES.as_ref()),
].iter().cloned().collect();
/// Testnet final Sprout roots, indexed by height.
@ -272,6 +276,10 @@ lazy_static! {
(1_116_000, SAPLING_FINAL_ROOT_TESTNET_1116000_BYTES.as_ref().try_into().unwrap()),
(1_116_001, SAPLING_FINAL_ROOT_TESTNET_1116001_BYTES.as_ref().try_into().unwrap()),
(1_326_100, SAPLING_FINAL_ROOT_TESTNET_1326100_BYTES.as_ref().try_into().unwrap()),
(1_599_199, SAPLING_FINAL_ROOT_TESTNET_1599199_BYTES.as_ref().try_into().unwrap()),
// Nu5
(1_599_200, SAPLING_FINAL_ROOT_TESTNET_1599200_BYTES.as_ref().try_into().unwrap()),
(1_599_201, SAPLING_FINAL_ROOT_TESTNET_1599201_BYTES.as_ref().try_into().unwrap()),
].iter().cloned().collect();
// Mainnet
@ -835,6 +843,29 @@ lazy_static! {
pub static ref SAPLING_FINAL_ROOT_TESTNET_1326100_BYTES: [u8; 32] =
<[u8; 32]>::from_hex("2b30b19f4254709fe365bd0b381b2e3d9d0c933eb4dba4dd1d07f0f6e196a183")
.expect("final root bytes are in valid hex representation").rev();
// Nu5 transition
// for i in 1599199 1599200 1599201; do
// zcash-cli -testnet getblock $i 0 > block-test-$[i/1000000]-$[i/1000%1000]-$[i%1000].txt
// done
pub static ref BLOCK_TESTNET_1599199_BYTES: Vec<u8> =
<Vec<u8>>::from_hex(include_str!("block-test-1-599-199.txt").trim())
.expect("Block bytes are in valid hex representation");
pub static ref BLOCK_TESTNET_1599200_BYTES: Vec<u8> =
<Vec<u8>>::from_hex(include_str!("block-test-1-599-200.txt").trim())
.expect("Block bytes are in valid hex representation");
pub static ref BLOCK_TESTNET_1599201_BYTES: Vec<u8> =
<Vec<u8>>::from_hex(include_str!("block-test-1-599-201.txt").trim())
.expect("Block bytes are in valid hex representation");
pub static ref SAPLING_FINAL_ROOT_TESTNET_1599199_BYTES: [u8; 32] =
<[u8; 32]>::from_hex("4de75d10def701ad22ecc17517a3adc8789ea8c214ac5bfc917b8924377e6c89")
.expect("final root bytes are in valid hex representation").rev();
pub static ref SAPLING_FINAL_ROOT_TESTNET_1599200_BYTES: [u8; 32] =
<[u8; 32]>::from_hex("4de75d10def701ad22ecc17517a3adc8789ea8c214ac5bfc917b8924377e6c89")
.expect("final root bytes are in valid hex representation").rev();
pub static ref SAPLING_FINAL_ROOT_TESTNET_1599201_BYTES: [u8; 32] =
<[u8; 32]>::from_hex("4de75d10def701ad22ecc17517a3adc8789ea8c214ac5bfc917b8924377e6c89")
.expect("final root bytes are in valid hex representation").rev();
}
#[cfg(test)]