diff --git a/zebra-chain/src/primitives/zcash_primitives.rs b/zebra-chain/src/primitives/zcash_primitives.rs index 2260412cdc4..90ea70324d7 100644 --- a/zebra-chain/src/primitives/zcash_primitives.rs +++ b/zebra-chain/src/primitives/zcash_primitives.rs @@ -180,12 +180,16 @@ impl TryFrom<&Transaction> for zp_tx::Transaction { Transaction::V1 { .. } | Transaction::V2 { .. } | Transaction::V3 { .. } - | Transaction::V4 { .. } => panic!("Zebra only uses librustzcash for V5 transactions"), + | Transaction::V4 { .. } => { + panic!("Zebra only uses librustzcash for V5/V6 transactions"); + } }; convert_tx_to_librustzcash( trans, - network_upgrade.branch_id().expect("V5 txs have branch IDs"), + network_upgrade + .branch_id() + .expect("V5/V6 txs have branch IDs"), ) } } diff --git a/zebra-chain/src/transaction/tests/vectors.rs b/zebra-chain/src/transaction/tests/vectors.rs index 9e0af8adf63..00bb81a783a 100644 --- a/zebra-chain/src/transaction/tests/vectors.rs +++ b/zebra-chain/src/transaction/tests/vectors.rs @@ -30,6 +30,18 @@ lazy_static! { sapling_shielded_data: None, orchard_shielded_data: None, }; + + #[cfg(feature = "tx-v6")] + pub static ref EMPTY_V6_TX: Transaction = Transaction::V6 { + network_upgrade: NetworkUpgrade::Nu7, + lock_time: LockTime::min_lock_time_timestamp(), + expiry_height: block::Height(0), + inputs: Vec::new(), + outputs: Vec::new(), + sapling_shielded_data: None, + orchard_shielded_data: None, + orchard_zsa_issue_data: None + }; } /// Build a mock output list for pre-V5 transactions, with (index+1) @@ -257,18 +269,9 @@ fn deserialize_large_transaction() { .expect_err("transaction should not deserialize due to its size"); } -// Transaction V5 test vectors - -/// An empty transaction v5, with no Orchard, Sapling, or Transparent data -/// -/// empty transaction are invalid, but Zebra only checks this rule in -/// zebra_consensus::transaction::Verifier -#[test] -fn empty_v5_round_trip() { +fn tx_round_trip(tx: &Transaction) { let _init_guard = zebra_test::init(); - let tx: &Transaction = &EMPTY_V5_TX; - let data = tx.zcash_serialize_to_vec().expect("tx should serialize"); let tx2: &Transaction = &data .zcash_deserialize_into() @@ -314,18 +317,47 @@ fn empty_v4_round_trip() { assert_eq!(data, data2, "data must be equal if structs are equal"); } -/// Check if an empty V5 transaction can be deserialized by librustzcash too. +/// An empty transaction v5, with no Orchard, Sapling, or Transparent data +/// +/// empty transaction are invalid, but Zebra only checks this rule in +/// zebra_consensus::transaction::Verifier #[test] -fn empty_v5_librustzcash_round_trip() { +fn empty_v5_round_trip() { + tx_round_trip(&EMPTY_V5_TX) +} + +#[cfg(feature = "tx-v6")] +/// An empty transaction v6, with no Orchard/OrchardZSA, Sapling, or Transparent data +/// +/// empty transaction are invalid, but Zebra only checks this rule in +/// zebra_consensus::transaction::Verifier +#[test] +fn empty_v6_round_trip() { + tx_round_trip(&EMPTY_V6_TX) +} + +fn tx_librustzcash_round_trip(tx: &Transaction) { let _init_guard = zebra_test::init(); - let tx: &Transaction = &EMPTY_V5_TX; let _alt_tx: zcash_primitives::transaction::Transaction = tx.try_into().expect( "librustzcash deserialization might work for empty zebra serialized transactions. \ Hint: if empty transactions fail, but other transactions work, delete this test", ); } +/// Check if an empty V5 transaction can be deserialized by librustzcash too. +#[test] +fn empty_v5_librustzcash_round_trip() { + tx_librustzcash_round_trip(&EMPTY_V5_TX); +} + +#[cfg(feature = "tx-v6")] +/// Check if an empty V6 transaction can be deserialized by librustzcash too. +#[test] +fn empty_v6_librustzcash_round_trip() { + tx_librustzcash_round_trip(&EMPTY_V6_TX); +} + /// Do a round-trip test on fake v5 transactions created from v4 transactions /// in the block test vectors. /// @@ -450,6 +482,54 @@ fn fake_v5_round_trip_for_network(network: Network) { } } +#[cfg(feature = "tx-v6")] +/// Do a serialization round-trip on OrchardZSA workflow blocks and their V6 +/// transactions. +#[test] +fn v6_round_trip() { + use zebra_test::vectors::ORCHARD_ZSA_WORKFLOW_BLOCKS; + + let _init_guard = zebra_test::init(); + + for block_bytes in ORCHARD_ZSA_WORKFLOW_BLOCKS.iter() { + let block = block_bytes + .zcash_deserialize_into::() + .expect("block is structurally valid"); + + // test full blocks + let block_bytes2 = block + .zcash_serialize_to_vec() + .expect("vec serialization is infallible"); + + assert_eq!( + block_bytes, &block_bytes2, + "data must be equal if structs are equal" + ); + + // test each transaction + for tx in &block.transactions { + let tx_bytes = tx + .zcash_serialize_to_vec() + .expect("vec serialization is infallible"); + + let tx2 = tx_bytes + .zcash_deserialize_into::() + .expect("tx is structurally valid"); + + assert_eq!(tx.as_ref(), &tx2); + + let tx_bytes2 = tx2 + .zcash_serialize_to_vec() + .expect("vec serialization is infallible"); + + assert_eq!( + tx_bytes, tx_bytes2, + "data must be equal if structs are equal" + ); + } + } +} + #[test] fn invalid_orchard_nullifier() { let _init_guard = zebra_test::init(); @@ -549,6 +629,34 @@ fn fake_v5_librustzcash_round_trip_for_network(network: Network) { } } +#[cfg(feature = "tx-v6")] +/// Confirms each V6 transaction in the OrchardZSA test blocks converts to librustzcash’s +/// transaction type without error. +#[test] +fn v6_librustzcash_tx_conversion() { + use zebra_test::vectors::ORCHARD_ZSA_WORKFLOW_BLOCKS; + + let _init_guard = zebra_test::init(); + + for block_bytes in ORCHARD_ZSA_WORKFLOW_BLOCKS.iter() { + let block = block_bytes + .zcash_deserialize_into::() + .expect("block is structurally valid"); + + // Test each V6 transaction + for tx in block + .transactions + .iter() + .filter(|tx| matches!(tx.as_ref(), &Transaction::V6 { .. })) + { + let _alt_tx: zcash_primitives::transaction::Transaction = tx + .as_ref() + .try_into() + .expect("librustzcash conversion must work for zebra transactions"); + } + } +} + #[test] fn zip244_round_trip() -> Result<()> { let _init_guard = zebra_test::init(); diff --git a/zebra-consensus/src/orchard_zsa/tests.rs b/zebra-consensus/src/orchard_zsa/tests.rs index 7783c454d74..237e73b2983 100644 --- a/zebra-consensus/src/orchard_zsa/tests.rs +++ b/zebra-consensus/src/orchard_zsa/tests.rs @@ -24,14 +24,14 @@ use zebra_chain::{ use zebra_test::{ transcript::{ExpectedTranscriptError, Transcript}, - vectors::ORCHARD_WORKFLOW_BLOCKS_ZSA, + vectors::ORCHARD_ZSA_WORKFLOW_BLOCKS, }; use crate::{block::Request, Config}; fn create_transcript_data() -> impl Iterator)> { - let workflow_blocks = ORCHARD_WORKFLOW_BLOCKS_ZSA.iter().map(|block_bytes| { + let workflow_blocks = ORCHARD_ZSA_WORKFLOW_BLOCKS.iter().map(|block_bytes| { Arc::new(Block::zcash_deserialize(&block_bytes[..]).expect("block should deserialize")) }); diff --git a/zebra-consensus/src/primitives/halo2/tests.rs b/zebra-consensus/src/primitives/halo2/tests.rs index e0fe52527b1..beb644a0c1f 100644 --- a/zebra-consensus/src/primitives/halo2/tests.rs +++ b/zebra-consensus/src/primitives/halo2/tests.rs @@ -24,7 +24,6 @@ use zebra_chain::{ use crate::primitives::halo2::*; -// FIXME: Where is this function called from? #[allow(dead_code, clippy::print_stdout)] fn generate_test_vectors() where @@ -199,7 +198,7 @@ async fn verify_generated_halo2_proofs_vanilla() { #[cfg(feature = "tx-v6")] #[tokio::test(flavor = "multi_thread")] async fn verify_generated_halo2_proofs_zsa() { - verify_generated_halo2_proofs::(&zebra_test::vectors::ORCHARD_SHIELDED_DATA_ZSA) + verify_generated_halo2_proofs::(&zebra_test::vectors::ORCHARD_ZSA_SHIELDED_DATA) .await } @@ -291,7 +290,7 @@ async fn correctly_err_on_invalid_halo2_proofs_vanilla() { #[tokio::test(flavor = "multi_thread")] async fn correctly_err_on_invalid_halo2_proofs_zsa() { correctly_err_on_invalid_halo2_proofs::( - &zebra_test::vectors::ORCHARD_SHIELDED_DATA_ZSA, + &zebra_test::vectors::ORCHARD_ZSA_SHIELDED_DATA, ) .await } diff --git a/zebra-test/src/vectors.rs b/zebra-test/src/vectors.rs index f6983c95661..ce0db04e8e9 100644 --- a/zebra-test/src/vectors.rs +++ b/zebra-test/src/vectors.rs @@ -8,20 +8,20 @@ mod orchard_note_encryption; mod orchard_shielded_data; #[cfg(feature = "tx-v6")] -mod orchard_shielded_data_zsa; +mod orchard_zsa_shielded_data; #[cfg(feature = "tx-v6")] -mod orchard_workflow_blocks_zsa; +mod orchard_zsa_workflow_blocks; pub use block::*; pub use orchard_note_encryption::*; pub use orchard_shielded_data::*; #[cfg(feature = "tx-v6")] -pub use orchard_shielded_data_zsa::*; +pub use orchard_zsa_shielded_data::*; #[cfg(feature = "tx-v6")] -pub use orchard_workflow_blocks_zsa::*; +pub use orchard_zsa_workflow_blocks::*; /// A testnet transaction test vector /// diff --git a/zebra-test/src/vectors/orchard-shielded-data-zsa-1.txt b/zebra-test/src/vectors/orchard-zsa-shielded-data-1.txt similarity index 100% rename from zebra-test/src/vectors/orchard-shielded-data-zsa-1.txt rename to zebra-test/src/vectors/orchard-zsa-shielded-data-1.txt diff --git a/zebra-test/src/vectors/orchard-shielded-data-zsa-2.txt b/zebra-test/src/vectors/orchard-zsa-shielded-data-2.txt similarity index 100% rename from zebra-test/src/vectors/orchard-shielded-data-zsa-2.txt rename to zebra-test/src/vectors/orchard-zsa-shielded-data-2.txt diff --git a/zebra-test/src/vectors/orchard-shielded-data-zsa-3.txt b/zebra-test/src/vectors/orchard-zsa-shielded-data-3.txt similarity index 100% rename from zebra-test/src/vectors/orchard-shielded-data-zsa-3.txt rename to zebra-test/src/vectors/orchard-zsa-shielded-data-3.txt diff --git a/zebra-test/src/vectors/orchard-shielded-data-zsa-4.txt b/zebra-test/src/vectors/orchard-zsa-shielded-data-4.txt similarity index 100% rename from zebra-test/src/vectors/orchard-shielded-data-zsa-4.txt rename to zebra-test/src/vectors/orchard-zsa-shielded-data-4.txt diff --git a/zebra-test/src/vectors/orchard_shielded_data_zsa.rs b/zebra-test/src/vectors/orchard_shielded_data_zsa.rs deleted file mode 100644 index 3e0c20fa8f2..00000000000 --- a/zebra-test/src/vectors/orchard_shielded_data_zsa.rs +++ /dev/null @@ -1,34 +0,0 @@ -//! OrchardZSA shielded data (with Actions) test vectors -//! -//! Generated by `zebra_chain::primitives::halo2::tests::generate_test_vectors()` -//! -//! These are artificial/incomplete `zebra_chain::orchard::ShieldedData` -//! instances, care should be used when using them to test functionality beyond -//! verifying a standalone Orchard Acton Halo2 proof. - -#![allow(missing_docs)] - -use hex::FromHex; -use lazy_static::lazy_static; - -lazy_static! { - pub static ref ORCHARD_SHIELDED_DATA_ZSA: Vec<&'static [u8]> = [ - ORCHARD_SHIELDED_DATA_ZSA_1_BYTES.as_ref(), - ORCHARD_SHIELDED_DATA_ZSA_2_BYTES.as_ref(), - ORCHARD_SHIELDED_DATA_ZSA_3_BYTES.as_ref(), - ORCHARD_SHIELDED_DATA_ZSA_4_BYTES.as_ref(), - ] - .to_vec(); - pub static ref ORCHARD_SHIELDED_DATA_ZSA_1_BYTES: Vec = - >::from_hex(include_str!("orchard-shielded-data-zsa-1.txt").trim()) - .expect("OrchardZSA shielded data bytes are in valid hex representation"); - pub static ref ORCHARD_SHIELDED_DATA_ZSA_2_BYTES: Vec = - >::from_hex(include_str!("orchard-shielded-data-zsa-2.txt").trim()) - .expect("OrchardZSA shielded data bytes are in valid hex representation"); - pub static ref ORCHARD_SHIELDED_DATA_ZSA_3_BYTES: Vec = - >::from_hex(include_str!("orchard-shielded-data-zsa-3.txt").trim()) - .expect("OrchardZSA shielded data bytes are in valid hex representation"); - pub static ref ORCHARD_SHIELDED_DATA_ZSA_4_BYTES: Vec = - >::from_hex(include_str!("orchard-shielded-data-zsa-4.txt").trim()) - .expect("OrchardZSA shielded data bytes are in valid hex representation"); -} diff --git a/zebra-test/src/vectors/orchard_zsa_shielded_data.rs b/zebra-test/src/vectors/orchard_zsa_shielded_data.rs new file mode 100644 index 00000000000..0ff29de1117 --- /dev/null +++ b/zebra-test/src/vectors/orchard_zsa_shielded_data.rs @@ -0,0 +1,34 @@ +//! OrchardZSA shielded data (with Actions) test vectors +//! +//! Generated by `zebra_chain::primitives::halo2::tests::generate_test_vectors()` +//! +//! These are artificial/incomplete `zebra_chain::orchard::ShieldedData` +//! instances, care should be used when using them to test functionality beyond +//! verifying a standalone Orchard Acton Halo2 proof. + +#![allow(missing_docs)] + +use hex::FromHex; +use lazy_static::lazy_static; + +lazy_static! { + pub static ref ORCHARD_ZSA_SHIELDED_DATA: Vec<&'static [u8]> = [ + ORCHARD_ZSA_SHIELDED_DATA_1_BYTES.as_ref(), + ORCHARD_ZSA_SHIELDED_DATA_2_BYTES.as_ref(), + ORCHARD_ZSA_SHIELDED_DATA_3_BYTES.as_ref(), + ORCHARD_ZSA_SHIELDED_DATA_4_BYTES.as_ref(), + ] + .to_vec(); + pub static ref ORCHARD_ZSA_SHIELDED_DATA_1_BYTES: Vec = + >::from_hex(include_str!("orchard-zsa-shielded-data-1.txt").trim()) + .expect("OrchardZSA shielded data bytes are in valid hex representation"); + pub static ref ORCHARD_ZSA_SHIELDED_DATA_2_BYTES: Vec = + >::from_hex(include_str!("orchard-zsa-shielded-data-2.txt").trim()) + .expect("OrchardZSA shielded data bytes are in valid hex representation"); + pub static ref ORCHARD_ZSA_SHIELDED_DATA_3_BYTES: Vec = + >::from_hex(include_str!("orchard-zsa-shielded-data-3.txt").trim()) + .expect("OrchardZSA shielded data bytes are in valid hex representation"); + pub static ref ORCHARD_ZSA_SHIELDED_DATA_4_BYTES: Vec = + >::from_hex(include_str!("orchard-zsa-shielded-data-4.txt").trim()) + .expect("OrchardZSA shielded data bytes are in valid hex representation"); +} diff --git a/zebra-test/src/vectors/orchard_workflow_blocks_zsa.rs b/zebra-test/src/vectors/orchard_zsa_workflow_blocks.rs similarity index 99% rename from zebra-test/src/vectors/orchard_workflow_blocks_zsa.rs rename to zebra-test/src/vectors/orchard_zsa_workflow_blocks.rs index 87153e35be3..733052cf920 100644 --- a/zebra-test/src/vectors/orchard_workflow_blocks_zsa.rs +++ b/zebra-test/src/vectors/orchard_zsa_workflow_blocks.rs @@ -7,7 +7,7 @@ lazy_static! { /// Test blocks for a Zcash Shielded Assets (ZSA) workflow. /// The sequence demonstrates issuing, transferring and burning a custom /// asset, then finalising the issuance and attempting an extra issue. -pub static ref ORCHARD_WORKFLOW_BLOCKS_ZSA: [Vec; 5] = [ +pub static ref ORCHARD_ZSA_WORKFLOW_BLOCKS: [Vec; 5] = [ // Issue: 1000 "0400000027e30134d620e9fe61f719938320bab63e7e72c91b5e23025676f90ed8119f027f6043d927d72f8b5df9984fdd36d2e2e1fd1ff8f7ee04a2b7da9306c14551c40000000000000000000000000000000000000000000000000000000000000000f2fa494d3fa60c200202020202020202020202020202020202020202020202020202020202020202fd4005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff025100ffffffff0140be4025000000001976a91475dd6d7f4bef95aa1ff1a711e5bfd853b4c6aaf888ac000000000100000000000000000000000000000600008077777777d80a1977000000001c1d1c000000000001029063000c87d7145492f9ded4d37b4ffdee769a1c41b0e17d622cce77f122d70ddb74fb50c5c36666482476bf5e8e190dcd8f5ed280af209b3f679d4dc06a213508774e2f7fa82dff9a985866919085523b13b0af4f534975228468feb62cb12575681e6101284f0fba5628e2ea531e9dad53d864c854e419e4c5b91fb7b35d00597572f98db1bb9f3049dbb9a08d403efd824d9d118a68493191e059ca00b2982252a2ffe5c3918a79171c294481fa267e83272858592d5890884feb90752347f33cfc9443e70a9f30d6150652eb2bb04327ee72b9c5e42462d4d2bd92725df50ce267c1588d29b08b25a719738e836f9c26ee47ce3945f9b627c4b9d3bc8ae755d8b78b840f1fcd055cd179af2ae0637f49fcc44cc975abb478fbd9922c15e946e681ff6aa64ac7275d58c7811c3d87c4e48dc97e35ca68780218e256f8bd7d9c1677bff6d75f663d24802a7b433f4461d686e1a0fd3d214b81b1398f8f79d062c4e92381741c3f96f3e81f455c96d05a623985e39c1d16361928424286483b40cc9b1249032dad9bf92a563bcd978c329ede5eb5c7933f937b6f2b73507c8ed0a2d4ca972281ed79bfe367b474b6fc89a29f20c913a7e42287074a185ea83fca9d0db796cce2cca07f3cd379eba7efdabf86a594e6743b0f30d3315daedd2afe289422cc0a5b73c3e837dc2efb5975e4fa8183fbe68b5688bd827472c41248bacde976d8f16700b4f6c9d6c83afc134e3766b7afdd85be2e373f98a7ef0d2ae19e98bfab76f3362888f3e81917b22236c6eae7c79ed9489410903bfbacf77bc1f0de11692cae0289c786ea3eb08f7fc652146d2529d0217801e2dab9d67c13cdbadd189fa302fbd402c4befe5823e70a802dd9c712396c20028f4f7c94a49409b169fa46a7569fe289d7189adb3e5e9d9dc63903aed828ecc3ec0144b59592a6a88c589577b976b7c781b3b43eba304130bf38971784c7caf8e5994d2ae59eede5ba220d7c43378b492e69c0d7b06445a49174b6aa27d08dc186b7bb5ec6b6b6e3b94185d5d10a07887b5f66f9991aadc239b578426ebb61b85ad40bd80aef5c4707963c2d2d9b79dd9cc416a597aa83c4e74cdebda03d6b7a1cd0238e88161d8ba579987335998fe39a909488455b11937e11d751f425ce7cdee73e8a99042f03eec4b4c00329da7dd90b75ac8918924205cb98346c5ab54096e7a91c9f44c4b21a885d36813221546da0609be857260bd691dff247d867f224ab98015aae153ec30248e15b5c0b2a0731496cf0518d9c63202f93d9f2023022d3fd3c83ec465ad3695d0e0d1ea0fb4eaf9dd8f6f92919ba1461e2d6e80f5d89e6b9b6d5241bffe1d91604c02e13592ef10a4b87612f82ce32b50550f0c46eb4cd6d081152b2123b0ae617e74a6f31f8721e8fcddee49e4c9269517fe55d7e364407b9fec4fb22711585c535bd6a3a656634cf034e30d4bed6e14c56ae98646a3fc42bc4906eb02cc80afdc9c5cd824ca22772567d8aec88c3b4fdc91d34133e8bb2a2787c4fddb3e5065fab306caf686f2684635aab39232c71d9211358eb2491ae39d0c5464efc0ae97b166821956d3c3e70acc7871b3d3c7a00e54e0974236fc1243caa57e04d1ddc3c42d67e23607830aff5540a806c6abc2621035f7e4280c7cd0eaf70db3e88d84da095e0c1a4d0d62728c2f8a939ac274fcddc1442b9993bd8b7f1a965b31af20637c789d93aa5e09fa6eeb4b55393f68cd9bc1a8c67f6d484b9c2134a25478e1fd28e0960ffcf8e36492e4b12f0c787fb16e80d7d0e92ab94a34e53c1b1c0b63db557e54c8e0c919073ff2366c83a4ca9b07b639172dc6df0b6602b3e8977ec3becf6b716c55fcdbaea993494e50b49a9dc8e7c09118942432ea3c5a036d4267928f2393072dc3734dd841e0c37cb2d50fe2f75c5dc77ff9e1540a52b136967862312de74af7071d4f17de67775adf87f1e540161a4eaef191a93aef5daa5ff7d42e36fcb31dd1edd73ba829b32a6d0ee48878bd6ff3ef472f48e9e8bc1f479c34f3d5509288f3181a49a8f3d7771c5cd076533924dc67b96da721ec8a19a85f903c2a2eba21c0146526dff8a8be77831f558be214c43efa29ed6e9be6a2d8e712a745bdcd0f9bce70f997da5928cee6775164168bb343d2613821b4814a1198df32cdab2da48c0188dfeeacef916472529503d8a63c4b2092133c31770d79ac922976417ba6a2d92b4108ca7ae496e039a7ef38deb19d22e1116e92e9cdd0a371b27226e6dcfb14ef5855adaf2949d1e764c6f83bd7e4259dfcf4d1831e2d9b80145128ebbcb0259e3cae8ac973204fb2bbdc5e9d967c6f7e5e4c6f0c139b4a07aa6b63430ff511c223c64933f5fd8ea367d4829c63938a2ee54b3469383824bab4807ae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f0000000000fde01c1ce7832fe7add3bc1fd885958fc5fa1697a0336a4504ebac5d683237a8510183f29c22defb607c48816c49704d3ce388ea27bbc7765a43961fba354af095dc9984dd6b892f223256347a3a59083aed6de70dd327a95e0f3dd1531d01f874829d8095242883e06045c1186ab08124b5dd0b5ec86c6bdd12f5713fb6ce125c0203d9191bc63a1de897698f59060b124cd81b8cea5e2a026577ebae2edf3e238203b670331c0cb32a229e263305484d7f3ef896c4e03bad52bee2250296698b47bea4f60342a23e0ab908a3c094543fbeff20748c3e75b7cf9755813388d5d8871f862bd444e3469e9e72302321bf35114dc6c8341c838f962debbeebf9727a132ea1a03a0141d6965bf152fcaa6d18ef7c24e32103cbebd9c1c87f0601da6e4f07af42615a0b2d41aebfe02e1c2ffaaced5c3d996c8fea947adf7975c4d6419b20aa0c804b867530bc1d1d6103ee6a6674530fed4b4a1289d4376902fc5ed33392111c323f6e73a07ba04c69f8e4214be8074e76124e84990d53091a4b95a9d482a0b2447d911255bb3f312c706151d8a87d284aaa0e4e24c059c07f4952d3fb0308acbbe1513842cf7881159080f10bd0f169169d0c0c126770a9b7985f0aed262ba2749b2c9a237fafefdaac68b8756c2a628f5bf2b7bdd804d23e2a8b9eb70dd38586c842d7a0e3c71dcbe5e651343375adde02e5501107339538b0e2dc45a9cb2eb8831ad77bb61d0359ad4c1a2dc31b29a850a31d7e72d00b978de4b570a9a4e4a403156cdf351154975975d424bd9933415081cdca5eeb411c4a723b6a2d19ab96d3a9ff273d5e923d158425319cce5c63c6ee3adbc5b36e05597472669d4bb48a292271a10a85ff7274a74e5a96e223d0705c08da720425e98ef270f907a20085babb3f642bf67dd8eb3fda67592b6dee4360895e22713783899ec9fe37f861e73cd5261a0be04af440b5f35fcefd345bba49a02f7e754bd5276e343a8f1f081f7e904295a12f57d8b0927be322b35368c463525415e5fc01e43c7064331258ef895a5f0f23bdc7b2095c2d27011bf17dbe37eca66d44ef565ab7cf9280a64651a39635b042ac1b74bbbfcf792e92cadaba08677a836f10bb0d1acbf1318c7b39dfed8b7ca0d64a24ca09d717dc618e036818ea11c743aa6e6a2fbbdd0c42f7c59122392bb90515b425b62ccc85b311d880cf24e621f100cdb8552c4e02360583676ae33cd314bf49a5b6979e6a7fc379759bf1dc9ac51b62c8b1851b87a58ac9fd2a9f30a61e7d96546ce53f8476b575777a533484777fa4ad9d921aa589f4d880de9c28c93c26e6d4284a3ce64ddd454490f73c9db8f4f1f49e9cc939405d635f6ba3be2511c2c1462d65905d8f2f40fb82d112141fd9591bf88ec98f82aee3e7d0a8c0156bbad06fd3eeab3da041ba47c572b3be65bae532893ae1b69d3e37a055c02e994e8429aba5dd5b455335144c63d6ebc6171423f2dd8ac600e648d34512929d7fb66b5fdb19f004c7e75e5e1d5e7af29a5acc9b87c8563c97b1c4cfc848676b1a38ac76ef4ab441f9235325dc1416911bf07ed7c598f6fc1c16b7d4a92489b5821f7151a11ce2dfe04d95d661a5cf284b4bbf83baee5165a3ceba103d36d15fc1a9739229e21789210581f9206323cf03526e2aa38f614bb59853128dd688b711afaf15986e89cac8b4b93b1ee24d55bc40743a4783746caf4f5bcad200363785c754d6af2dd5d519a4151223148e4f5c89703dfd209d8a38b5bc55c5f1f644e6e071bdd8f6597141c37530b7ef9e513c49f9b7b0e0c743830931ae2958c73b14ab1f35e2618298db2c437c95d6d4b13c41b4bdb51f13c1813762e213e18655382d670f55d97b2ed83c695488efe5831ec82656c6d42baa154388d4e212fb5c980b87476e62f4d8e84302f23c54b95b7b1b74e0e44219dabb8e8b4d4830a7494b627e1f6e62a634b86dc821dbaef4e3e3b53e69ad670f1588f2aebdb702828098508060b53cca72fa8c92881a20a852eb1315c2439ec89fa183e67a81c6590dd51a743553fa48fa9f10495c6249c7bbc51ed08e703ce7103e28b12a263fbe66466ad66c11bd9c66c27494b9815e1600bcb2e4248a514a421bd0b0363d8888ad8c9c3605b005a51e77af8a3ae4009f34a24ee242a60cf5c0b2860c715cc56337fe9983a893be43fe75c87997d6eff3e87ca34923fb39993dbfddca1d9861b9314bf420dfef04b0edd9fc9a5b6d7ecedbb7d669a5cdb5045f9a7217f83c62e3eaab4fcaeb347062b02857e7c073eee827e0f1a9c37f4fc3a914b2f583f5632a2fb974aa64f08245c706ad94e29f7b8d1b8b5a423bc3b5b4dd9106d1fa787a9d5d6f64f3273f3758600ff39b6ff0690d7f4dde701aa04e664c9c3f622622736704a523f78bcfad7e882cec28183bf15316370dbd4f3164bdb1224f49a27121e57f7cbb7f8a28650fd2589cd109ac1040194c44bcb8479d655800cac9fe82717e9496bf32e8d3e3a4b5fa7826f4cc86878fd4ef9857640c59b60ba7276af3e449679fa78939dc590c1fc392b854c7e8c4528108bda4e4a0c14d27adff03c0429bdabbe2df4249311d5f7a7ec35f023b166f7de5a1a521615db0376cc1237ec902a4f76624a8a5c65a293d4ef3344429aadb482633bccdcbe1160dcd098b71deb84153068083cc6976cb9fdb46dbe226fa587970fdb4fb14b07720de20cae800014c66da833530b84d7f5f1977f74813507715dda0071e845f9c291fcc4fa4513b24af47000d230d72ee0a42c0a356c1f96fca44b313396c974b0849aa95d0062562d0fbb31d47af78e4e857cdbb43f2014ebaa8cf796067863b0444bf7a3a207816c5eb8dac792d15a01f7ce0bd48a5a3687cbd8bedd364d176106561493bb8e83f63bc67fd07f8b11fcf3bf99b2a1daac1a001ee09d6f8d3973c623b8838988b4faaa1d9151233ff1cb89e947ccf322d59b0011fbc1bf66f5a2867c0a35385d55463cb7fd01db5932b9a163ee6cc11ef0d19e09e2dde4245571fa01b8624926e27a9bae527a27dbfa1fec4c5687a6193a5336469ff40fe03eb0338889dadd86d84a6381b2f65cdb3b6880ee67de08572d6fae5c5df6b2ec4e1216a5999cb3c2bbdffacb157d1e94061b4eb985d153b8840c537463e8a15e5533215522f1d4ab74f09a21b1e9c851688c2131f7da84c95f390eebae35dbdfe1e28d5d0755d419707317ea45d75e88c40d5df34316fcc7f59de8af82e1cb0ba3e10ff1775b8d6f6ba2141a1f83b21b577afea554f709fb4c373f6dbc66a4e97c31a500129684d7315874633453e7ac8c10f63ae4708b28361c772725a110fd3e626b020d8b5b3820faf67e02e3feb9d13ba99f40b6ae834ce75881c44d8124f3f234cd003d5cdda116a218c3dfd1019690b31ec2546b0be2660aeea3b11d375cc19ca2c57fb3ca817a53dd3357cd5f0b72c3a06dffee32ee613eb53a0f679662108f42002ea24bf40c2db026dc595710d23bd9ef571dd37134955083c25ab968d23bae81d22c3a16f10f3d75cfd7eac8337226dda9554093db1c60261931c278b11846796d56a477f454ee04053268709b935b198ffa096dce5c9d3bb1bbbd8fc19a38d529603881a9d449f522650aefa9e0530d92f2712c6122bc874587fc80c29beeee0c2f532607cc63aa8a91413bbc351a2a355b40b3fad7871976b6a46491ca94607f27b2018af66a8d6e429b8955a68e10f3585666b40005248f39fa274020d57f76af52e860f6004f20b33174e16b184d39f90ad5c563da44be6a526de1b258a20649fe5b084b546417385ede6ef19ab6770dd56583d2f3d36901aab371a341c1fbf11929950845b05b833dabff5608b2b0346d8f41ffb24b2be3187cd2ca86d06e8adaedd3f3e9ca9f6e1cbb85bf6c34eb3dcdd9931edf2312e4348481d3ba48a33ee57a314c77196fd28b63546963da0c3edb742934e33daed72cbd80b1ff33a716e22fcff53b93b8791238a92f62070a6c8f74d3c16116c1f9743bf100e0fe3e1dfd512e60fb075f193b3d100f8327a8b7011b1a12c519ec902d7183a09958adbb491a9e9de0070fc685b3963f1617112aa4edd1a4bd35bb459ad121e34851230f78913c59ac8d766b84ab510f657257a109de229ddb30b3db025f620604df250741b4eb757f6a0b6d2a0ba2cee7ac1046800eae0519243380c404a133766b685997236bfc73e3317e2da32c9f449aecebbd02c28c5e62226aeec140e4c38dabc0c6ae4d6fbd5aedab7abe0d2b0b0c7533367db3ab39ca127f688ef34aa4a61bf2cd2ca5a0f598b8009e5610efb05da12495c0bf02eec37fb857f7d1943f8f76093a27b422a910f4904cfb836f62d7ac295760ab9f2587f60e83d402854e9a3550a190f59fbcc5c94e6f6bcf9e9d7527ef7e6c4afb13b928fd2fbba2ae008f19da2d385881dfece30a3c9433909bae080e01f09e987a059f368d7712246839159ec183345e5a8607e860bf1948134d1ab791c2446094d012e14e1a82fa5d95106c0c9626df1e7e56cb7e6cbcfbea965f64cc4255319eff09bcc40ab3ccc7294ac369701ac1f083b615e532d13ea809eb68967b031fff2b0536b781e08688a51de3629d4c8e3e29987b4ddbccea41b7060ed9f635da106145bbd4dd2045f2215546edfe71205f5a139bf5e9af5b68d4c34acc19307d23b7971da98ec2ebc8282aeabba8f1a46af4baf00276aa0e9e5c212865d763687335e1ec2d6c813a516bb2f2b79056100b07488ce2fd5089be296ead42ce345ef58f73543ab102ed79e426521fea60dcce47e498180ee94f1e69bf862c9e014ad60f6041819f01107812803c986e547a5fb744dd766b92e22ca8621b56190ab1a7019eb9e288114c4c450d08a95da7282278239f7fa073a8ce444506ae171e0dcd54d1861362f12957b366b92dcb0480017c6a397b27506c55238e1656355786704489fb54bf1257e90a246f92ead455166c4217b610682a6446514a1b59d5facfc7041d4e639046e60262097557cec24c59629b891229e714db79a80e830af7fa7a2112e60ff1fb95741f39c51d37c3be241e9990bf14c325e558483f65408ba25c4cf85e10122cd5cba6010db487930eed9bedac4d533825c657aac9cb709920f6c9a537b76194eab8c330fcc7891e24207f5ca76980d94bd1b6db41692ee6bee117544e98620de4390da019b63757bc78ea7d0e27c2fc6b92d8c0366e23ff1d5a38130e5183340a905cefed2bd332d443c6fc6c3f4601bd3e4927b40388c00c842c93d01ac365bb6272f28ad28ecdbc05dc2e4f61175cd36f5fa5a4771e0dfb6e13cc2ba910e28f11fa13728bf2dc57e279ec67f8046187bdb99cabeb0c3c008c6ef26ca382f9940e0b02771fa6c2f69f1116baac1adedfae6ff68dc8cb249c112ce6f9a6208cf1fb4c4183995326dd690bc4531de9ac85a0be2f6b0795b6f9bc700b7628c272f245de3210d89fb7b552a731672779675ece0963c2835ba8c6ece9cc4e55b2d077489cc8a83558a1261a452dce0317cb8ef4e8642f3d13090305ad345906b180e50dece886830f7a349e3477a0f10df57a81a5f895e8c043085d331cd1bec20f7b8792871912776be3ef4b8b411ca9cd9a9dbd92d1f66c90b23d35b1d0cad3acbffab5141b5171336753289274d897c2449e9316c3d19fecd86e454a51c820c080ceef6421565d481792501b582190d960776cc5c6bcc3f6a33a92e213f7c2e932d8f1513d1d2bc31cdb0c9550ea21fb9d5db1acb01eaa804c594f98777652a7af27184e4ada612201c80f18d1cbd5f9a4a535444934b72f6262d582ac5802bc17c106bcc4a53eb4af6334ec1eec602bef40366d91f4b4df477f2b3b6be2111e0e6223c5f43811ccbb3c31f8f4c2138927377521cee9954a493340596fa0431fb953e7ee3c0a15b37f47592fc4cef4b47c759d6278b4fe5be6519c9927e9c08f6e89c6ff99ca69c9f89e27133b52197520b873c578ade66962bc18d0726db271671bfbe8c21c16eced0675a58ff1497cb00e239481adc4656537b80830b37264e7f1b50e3780f32ea57c9125c73f07e33cb30a51ec6c4dc98c4f9337d62152ba544eebe4d7a8eaef723ab85570d549fb90687f3b4782f7647988ca3e97b6736bdbd7cfb10393405a86118ccc415a16e7614230828430728618da31e37792f043e3777049052d58957a352e54c16b3d93c8595220a2e8322f2da669f11be3817955f1a350d3591ac81d7e627015b0653cff1eae964f89acfa41663a833e65235627eb67d30738f170da134de1d58499997315a329dcb52fedc30171f948f6f23a2be30b49398a2162f469eb161e752faa487c533e0ac6aae88c6d7ed64892a0ae4afbc2b30ace36ca7ddf4f1334b6731641599b0d2540c4fef4ae6d9c0b81c2d356b98178360b853a501dc1866343e81fccfe0e99b1042d10a38ef3a5cb20434118e16eb23244446ae69bcc1a1e699cb5981c206689578a9a2b3f6aa3ca37f1e09346fa2f4f7695a6b8f7087e9763f52d06de4208a4cc02e92801883f89ecd396248db5f0d2ef527a75d924216fcae8c76178c0b7c27f618331fac021e6c9a3a9e585d1c160f53eb39ebf4b1b3d84d97b2cb9d0f616e9b2ae10bb9e592580f27918e4a17be2570f5e4283aa8420189f72137606e2be0a9e2ca81fb2312caa0208747005ffea881f8a44add38303e7d080e4be30b44271aeb4feb37101c201d0f8504e711324ecd3b4dee9d69348c22656b7edc5f68b236030273890e9cad41258e1445ec934f9b4b2b2792365b52d0b44bbccbc721494a5671a60ed4fa289e203c68ab3c4b88ac36f9adc91a4a6c8cc4c52feb2eb34b64667a74c3bcdcd6e438e20d2b6c499500f488edc872165133fadb4fb7713a49de17f60ca4d780918f3cfe19ca1447f83761ee1808436e310fb7cc32db065c5923a4537d233be2f3311a5ea416c6bf280850647c650ac01835351eede816511edf33e59f467d0936af21a4cad0df6fbdd6711e198d896115cc3dcfac0948522e231b34e47dcfd05b921df497b190af5d621c59c94c34bd405c9be00b6dd72cde87e93ea313039c01633335044ffa8bdea20d3b8ca5db2c4516b5a59512d09d281b187722c8a5c9ebdb2064871229640354e9aea165dddafddfee4dfc1001d38229e51ab7fc33460b4b1720300aede7973a8c940e6ff297225d53bfaa6880b2b4c0ac261668eef9d6823dd5b0c6215d16c00df561e9c4abaad3ef0da84ddd599d56691dd1b121a6120c3408bdcdd972f77d207d382983b0a044647cd2b86c91b8bf19426c5742b7378e2f21c0f09bff8669f6b0bf6187d44c3bd1e50dfb65aff3aa88ad00c2e12735ee384347379d2b48ab3f0b36f712e0c1bca9698d29f17924f0343fc573a1115981161036b71f96ed9659b52eafb7794ff9a05b42f5b96aa530e45f1892f49dcde62ee824ab3dd0fd9c511bd8eda0d60eda753cd5d444c0294aac617b6c6453ce8b2274b0cbc5beb68e2761897b1e2ac612d8e6f8605830113bc800c91bdc4d4c89394d33c25f6465d813a453bf89eb3f0baf3b83856665a33d1e0a291b527d6b5704221b5b343a6fcb70f561cb496b727fbff07e48b56cc924ef51b3459449fb5211c02ff081c90645ca392d5e7a13f13160acb5b53ed20b8e01390cc4d1d7ea9533b2e7a21e9ccaf0461f1d5562c1ae28c04c138083f4ebd11f75e3298b2321a20395d3fc685d8c4986eaacc4e97d6481149aed040e07239c051d761379faecbb3c3356ba37358353b204bbe96765bcd4b9375470fac05907fceec5d94cb1195227e02f7b66005f9a76ad9ce1fbae7097b0ba824c9e415f82b0812a3bc6d6ae3824dd1d04837ca39047cc27d57dd5655f2d52e1b28ff24af701c93a169bbde2201b4ef36ef361f111838e2a59b2f1ff32410c91be4b2ca0d4d434a70c0d03d8ca0db8c9cb7b27ef0743c8a0579d6c4df5a76644637bee0d45ffc4ff83a1ff779f22e49e65550b60c279aeef6aeb89c1ff6a8cff8ae5eb645e9e15d694350ac0f7127ecf632f218759eafc04a988a3d23d1347a44b6fb1f79a2a40e41f441bb87823856b221e7cde3652a62b824d5f64b08570320f7729b500e138252a6220da6811707c097dee8d29123511a236f030381533cd5233b1d2b19da47cfefd49179430b70bce17969888c2c02b75d07bf84de64dc91fbaa6cf91052d40001bf83cdd18e8860b3527d9f72895ff0c398d4945ddd569c2c568bbce302506b5bfaaaa9e24f435e73c236730bedd4b8940bccce1cdaa2abd388646474e3d9c0afd61e28a1ebbc83ac84ed644039a7ea3aa270eac6f46f21fbaedd49ce21fe133060e416b03e28059d753b025f0c2b9f25e6fe146bf9f58956083e1baab33570493573b9d05104ec9bd2764793d655bb033c646af6ff8f8bb268d35c43acf614206bd4f5635de334ae2768e8621f24457bd6cc3f81a50c8fff871037415bd543e5515df44253f93ae05241cdeec2eca35eb2908ab07dcee8c795cf0a7442ca1213449a5c03282478bf0c0755fd7f62ca140a303867ebceffc631e8db2249c2c8a9806033a201f6e59becf382ccd5167ad5d1d7a3de10492c6bda89121bb8b075e3b6a5d1b09f6ad972c0605f1fe1610c38f42ab22625341b41c252be0fa80c9d082f56fee0669d7d4eb9762af0fd827e545ac5cb6b225571540f2e6820ad79311ed2fe57e30d12239771d79ad47577647068ae6ec7fa3602e86379f7c56709f822db68b5d35440e855509a2b86ebf86dcaae220a89b6f7dea85fe1fa5cf2d54ea4b242edf1a0b3c1a0b04b398e8a67c3ebd093e4429c9554605e2f4360449420a5fb5aebc158d80ee10349d23e9d68196cebbedbd98afd16670b66231f0f7dd9c0a4404da2db7a00172ff4f48b486183b2cc8d49b126acbf5e04af0a16b36291875c187ca2a65aeb240729167674cf5e26c2eb9f8464605279f47b57ef7584ba7b6662f221cead6f89825748ae7bf4a22d54715db9fa80609be5175c30a50025e54d11ba22ac4cb17fca2ec160184e6bacaa6e49a836697106511a1e65c679cf240b128ce1974b42812eb02105bcc59226faa7f085adf7b7ef5d0b2d5d88f59563e3d2edb1aa9ae253fda7e66996f2426d52ce0d36d5e38c07b94551851f4f1057e10beab078c85c6e1fd86b2200eee7345f0e9713061e0060b6333e91fcb7621d57333d80eeef03e912c2da7985a7dbc393aa597abd67b8240a3e72330488aaf26b53d671fe2effee1db167f747594de4de80bc1f0fd5b1786d276fe31d8a631556ad4080d7674b69f1cda2403280fe1eb962bef003d4df61cba3dd906aba9c01e5644dcba01dd790611da5bc46ee24e2ba26a4abaf8ec1c78ac48e0c5eb9c3079f802ca0dfc6550ba410b8d76885506b27efeb3bca592c2475def2dbd4f5d7ed4836abf086ce8b23d617701275a8d452085043d8b30bf66e0739471bc432a155f249f1c1c58e4fa37a02ae653b0ec71e22b9fcd3d9a00037bcfcbae7c0106d6d13dc60fad3f734ebdcc0d3fe9c29031716c746b52bf7c9db5a09cfe19bd9f0b87cd12f8a6c5e36153e0bf5587c959f3636618864362195078c591502325864283ccd1ebb071baca04a50f4069f1eaea8d8d2af49e76f29e88108df904d59a6631686be5adfca48571442a01cdb1a3de6de174fa9496e9ca50d2db065e61d82c16ef01438125a96e6300de69c30967c58545aa8f96fb66725959eb0bfeb99a54e7286740bdefc161115a404556a1dd0381f772b7ab8c1f7f1ddc0cc0ac782619fad570aa1590eb439455a642cd3122b0edfe9322a19360a98193bc75e4b8d2babaf0764099f36e3dfbce751a8fdac684ca46b0945ba593b6b18514d39542a803a3305b55b8f7c3c5adb4e516379db8ea4570020e95d69afbe165e6559636d9bb9d2936ff3272d757d393937ba4ee4edcc329e17426a7b5ab99aacbd804e453f147bc0439022a3c78b5f4df3f0cc24ea8993bc98fa7e5b4d628d36ecaa66231f2c612837b8c3704ac12862a67df9127885a8b4ce8f06d7dd0d8d2d25e6a9f77ed217869ad2d461c26059d689e4ed9774890e18dfe007ed3ae55f862abd7e4b0a53018c9c18129ec6983491c8fc9bfb26cf5bddddb1fd8db5ba17b8ae0cbaeed5ff553db2f0784cbcd76429699e3490c01a2090cba4b230fba3258d7e5cb18edc5427c1061206a1a5c3dc81e5681777216ec66cfbba42b7b506ce92664db7bfa51a9323eea0169c4cb0e83f756d3ea980461313e39d208ef70dc011722ebf284f75f07cde322ba23f211815e4b616f092652f949e369188a815613f8268a1338fc8f932e4b845739564983555417ac7b0170eb036bb80479ccdc3e9867ace0604f06da016321031402b21ad4ade201e208eed72004c59ba0fc7316c32e3735ba7c8c0dfa322ccef89e50b984bb020571702c2ec6f08d0471e72acbc1d19520ef5c9a110b953ae66ccb4278dd291b08af4621d3bd4d51e84604f6315b9cc52ed68f48f6fb51332718b3bc5e006aca5b50b1bd106ac2fcd300c77ace7517b7badc33ce9b5f0302e752cc1e3f0217cb1fe79e4202cd3614da5cad5ce95ac1c22038df2e690582130000000000000000e77c5fe5829689989e0736353bc0ffd6b69fd0512b5aa1d5292167ba63dc5719c978be1cd9cabdb5c757be48c025e5efbe17937c24504332ec6dcdcfac80e23a01ce2c6d87d0184c1799a254ed03047cf57a3bfd49b8a4438d496d741cf5aca6bd02cc36601959213b6b0cdb96a75c17c3a668a97f0d6a8c5ce164a518ea9ba9a50ea75191fd861b0ff10e62b000000000000000005fd281521444957bab12f47104f5ff295620fd17dbdb799f813f973e1eaed4162bdd8d72b03b4e2f279a05dee99e05f68f38a4b1d7f6952cfafdca675fafbb0a65f0db75a331b2c04f82fed81c6edc0291ace5edb0794c285a5338f20c891195bc1e9d7134d3c05c62b251ccb3ab8473cb5dafc7b19f3b6750e41bf24c6ad882802a60d88a2fb0fd642095e8030000000000005fd281521444957bab12f47104f5ff295620fd17dbdb799f813f973e1eaed41691c948450f0844a9eaa60503567dd7c87ed664db6236c4368575d4beade9741856f5e328f9ba28c32610207662b9638281878e43a31d3f2de4e440d6a792a01a00ecd37228b00c8973da400337300587f48997465959efe09ac5e012e14f51eb2403e78f9aa4048344a45119dc45ebfb2fdd1806662aee645a85d9951b714a42ada39d7b268db0db118784846efe571b2feca12d5dbb15ad28d23c933987607fa4", // Transfer