diff --git a/Cargo-recent.lock b/Cargo-recent.lock index 8bd62145..ae7b3d61 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -29,8 +29,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" dependencies = [ - "bitcoin-internals", - "bitcoin_hashes", + "bitcoin-internals 0.3.0", + "bitcoin_hashes 0.14.1", ] [[package]] @@ -66,16 +66,25 @@ dependencies = [ "base58ck", "base64 0.21.7", "bech32", - "bitcoin-internals", + "bitcoin-internals 0.3.0", "bitcoin-io", "bitcoin-units", - "bitcoin_hashes", + "bitcoin_hashes 0.14.1", "hex-conservative 0.2.2", "hex_lit", "secp256k1", "serde", ] +[[package]] +name = "bitcoin-consensus-encoding" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "207311705279250ba465076a1bac4b1ac982855fff73fc5f67e22158ac58cdc9" +dependencies = [ + "bitcoin-internals 0.6.0", +] + [[package]] name = "bitcoin-internals" version = "0.3.0" @@ -85,6 +94,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bitcoin-internals" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" + [[package]] name = "bitcoin-io" version = "0.1.4" @@ -103,7 +118,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" dependencies = [ - "bitcoin-internals", + "bitcoin-internals 0.3.0", "serde", ] @@ -118,6 +133,17 @@ dependencies = [ "serde", ] +[[package]] +name = "bitcoin_hashes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a67800fcf7f3ca52f7796d4466948a424326b225950c79d1e76d0458760bb25d" +dependencies = [ + "bitcoin-consensus-encoding", + "bitcoin-internals 0.6.0", + "hex-conservative 1.2.0", +] + [[package]] name = "bumpalo" version = "3.19.0" @@ -218,6 +244,15 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "hex-conservative" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35431185f361ccf3ffc58254628af5f1f5d5f28531da2e02e5d6c82bbc282a10" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex_lit" version = "0.1.1" @@ -450,7 +485,7 @@ version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.14.1", "rand", "secp256k1-sys", "serde", @@ -566,7 +601,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75c8fb4a18e63fbce4cf16026c36a6c38066e4f4a09ce5e81be817d0e36d8f8" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.14.1", "byteorder", "getrandom 0.2.16", "hex-conservative 0.1.2", @@ -580,12 +615,12 @@ name = "simplicity-lang" version = "0.8.0" dependencies = [ "bitcoin", - "bitcoin_hashes", + "bitcoin_hashes 1.1.0", "byteorder", "elements", "getrandom 0.2.16", "ghost-cell", - "hex-conservative 0.2.2", + "hex-conservative 1.2.0", "logos", "miniscript", "serde", @@ -599,7 +634,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd2cc5d458a8032d328ea85e824f54f61664ab84c3d42b3b7f8804fb9b81572" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.14.1", "cc", ] @@ -607,7 +642,7 @@ dependencies = [ name = "simplicity-sys" version = "0.7.0" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 1.1.0", "cc", ] diff --git a/Cargo.toml b/Cargo.toml index be08c4ad..8055aa8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,8 +28,8 @@ bitcoin-miniscript = { package = "miniscript", version = "12.0.0" } byteorder = "1.3" elements = { version = "0.25.3", optional = true, default-features = false } ghost-cell = { version = "0.2.6", default-features = false } -hashes = { package = "bitcoin_hashes", version = "0.14" } -hex = { package = "hex-conservative", version = "0.2.1" } +hashes = { package = "bitcoin_hashes", version = "1.1.0" } +hex = { package = "hex-conservative", version = "1.2.0" } logos = { version = "0.15", optional = true } simplicity-sys = { version = "0.7.0", path = "./simplicity-sys" } serde = { version = "1.0.103", features = ["derive"], optional = true } diff --git a/fuzz/fuzz_targets/c_rust_merkle.rs b/fuzz/fuzz_targets/c_rust_merkle.rs index 1df41db2..6283e910 100644 --- a/fuzz/fuzz_targets/c_rust_merkle.rs +++ b/fuzz/fuzz_targets/c_rust_merkle.rs @@ -53,9 +53,18 @@ fn do_test(data: &[u8]) { println!("{}", rust_data.amr()); println!("{}", rust_data.ihr()); */ - assert_eq!(&Midstate::from(c_data.cmr)[..], rust_data.cmr().as_ref(),); - assert_eq!(&Midstate::from(c_data.amr)[..], rust_data.amr().as_ref(),); - assert_eq!(&Midstate::from(c_data.ihr)[..], rust_data.ihr().as_ref(),); + assert_eq!( + &Midstate::from(c_data.cmr).to_parts().0, + rust_data.cmr().as_ref(), + ); + assert_eq!( + &Midstate::from(c_data.amr).to_parts().0, + rust_data.amr().as_ref(), + ); + assert_eq!( + &Midstate::from(c_data.ihr).to_parts().0, + rust_data.ihr().as_ref(), + ); } } } diff --git a/jets-bench/src/data_structures.rs b/jets-bench/src/data_structures.rs index 72e1500a..bcb2ad4a 100644 --- a/jets-bench/src/data_structures.rs +++ b/jets-bench/src/data_structures.rs @@ -5,11 +5,8 @@ use bitcoin::secp256k1; use elements::Txid; use rand::{thread_rng, RngCore}; use simplicity::{ - bitcoin, elements, - hashes::Hash, - hex::FromHex, - types::Final, - BitIter, EarlyEndOfStreamError, Value, + bitcoin, elements, elements::hashes::Hash as _, types::Final, BitIter, EarlyEndOfStreamError, + Value, }; /// Engine to compute SHA256 hash function. @@ -206,7 +203,7 @@ impl SimplicityEncode for elements::confidential::Value { elements::confidential::Value::Explicit(v) => { let left = Final::product(Final::u1(), Final::u256()); Value::right(left, Value::u64(*v)) - }, + } elements::confidential::Value::Confidential(v) => { let ser = v.serialize(); let x_bytes = (&ser[1..33]).try_into().unwrap(); @@ -331,7 +328,7 @@ impl BenchSample for elements::confidential::Asset { elements::confidential::Asset::Explicit(asset) } else { let asset_str = "0abb0d5d5843b9378c7f245fd7329d6fcef6926554f0c95f7cf0316239178f743c"; - let asset_bytes = Vec::::from_hex(asset_str).unwrap(); + let asset_bytes = simplicity::hex::decode_to_vec(asset_str).unwrap(); elements::confidential::Asset::from_commitment(&asset_bytes).unwrap() } } @@ -345,7 +342,7 @@ impl BenchSample for elements::confidential::Value { elements::confidential::Value::Explicit(value) } else { let value_str = "09bb0d5d5843b9378c7f245fd7329d6fcef6926554f0c95f7cf0316239178f743c"; - let value_bytes = Vec::::from_hex(value_str).unwrap(); + let value_bytes = simplicity::hex::decode_to_vec(value_str).unwrap(); elements::confidential::Value::from_commitment(&value_bytes).unwrap() } } @@ -357,7 +354,7 @@ impl BenchSample for elements::confidential::Nonce { elements::confidential::Nonce::Null } else { let nonce_str = "03bb0d5d5843b9378c7f245fd7329d6fcef6926554f0c95f7cf0316239178f743c"; - let nonce_bytes = Vec::::from_hex(nonce_str).unwrap(); + let nonce_bytes = simplicity::hex::decode_to_vec(nonce_str).unwrap(); elements::confidential::Nonce::from_commitment(&nonce_bytes).unwrap() } } diff --git a/jets-bench/src/env.rs b/jets-bench/src/env.rs index 1b55e334..21cf158c 100644 --- a/jets-bench/src/env.rs +++ b/jets-bench/src/env.rs @@ -1,7 +1,7 @@ use elements::locktime::LockTime; use elements::taproot::ControlBlock; use elements::{BlockHash, Transaction}; -use simplicity::hashes::Hash; +use simplicity::elements::hashes::Hash; use simplicity::jet::elements::{ElementsEnv, ElementsUtxo}; use simplicity::Cmr; use simplicity::{bitcoin, elements}; @@ -210,7 +210,7 @@ pub(super) mod txout_utils { use elements::{confidential, encode::deserialize, schnorr::TapTweak, AssetId, Transaction}; use simplicity::{bitcoin, elements}; - const CONF_TX_BYTES : &str = "020000000101a40918d1431b219b2e8c86a7ee49e025c9ad67156e6be7971e7f8cbc10e5c6620000000000fdffffff030a5462b0c147862343b3d81698a591d10c567f8b987e9d9308c671de79da4c3c5008d9a5ea8db9337280a430ea442559303bde86ba8d3c6fa9278711dac9d16f79f002c15772c83e57465b193d6ea6dc992177ff645b8587e9f6f1e8e841e8c2694eb0160014c0c88e225008667047624968c1c286427fd74feb0b8d9cdc6e69c8d6686fda938a45e1529e81cc3e43313cca4a806c1186dcd124a80915da44966d2a31f3f2f61fd95e742353dbf54ad18ba709efa3cc9f377835b251022677700604ef92070c670338c5349a64e82e4f413e581d06a6dea60bb82c013d1600147e172b800e0ac0396a37af6e3aefaf7d3c1293a301230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b20100000000000000f90000650000000000024730440220719dd6e625a2bb75208ef5a38025c50a7bf080ae701b5f72ebb458a8f0ed634f02203190506ee407196683312d0c571cbb574005d53ca32e202ac6035aeae5ec5cd1012103f424277b3d9d05f1b207918854d3a51c3f83629b49ea5c057f7f50c243cef027004301000105f328173c0a001f3544362bdffce10621ae6f0fc8f8b07f7a16364ea7b9dbebbd08cb6970c34f8153737a608b2d78a9bae86d7ac32315d8576bb01af578050cfd4e106033000000000000000147808100efe85e666a8e2a8e8199373b997f3500b8e971c480071bffa4b92e54ac226427d53659b9aeaf2270d7932a75e30acfb7a19082c068f66723574de9d1cf7d0e63fbba5840cf0d04487833a6aa96340b5ca89c8d21847fa5cae77ffe68f0eb0d6c2cf730073c460dcb278588f005f4f6d63ef528b756f50e420906a4e60aebae7102539712f4bde34f90f0e77c403f728070ececbb66af14e82912c3b3585a2debbcbe4c7b98b8e36d0e2b7f12344a31f514231545ac20f441501c1f72b04045defe9f6579be2866f2e1c42381bcff654280be25a4aab13a94a48c75672e3b0586223e0ffd2477515a4a7458b308958a0e459dab3bb338b4684dbf7e13827ce3ef8f8f344c338cb0d89a009bfc2a942eccdaf8581db3221c4f3ca868d42fb18cacf889728260c2323319dd11a6ce00461e28ce8fdd92dcb8e089b9b244510e2ea1546e09f446e2a911532fa4952d077130121ef1564081ea2d7e2c62b12f3c665c65d83f27b3c3138907ec5e44bb8283c0ad07deafddfefd303347f0ac95ca9faac39a6510e16f17b37acaa70389d746cd4475e59703f4f72e40cdc0ce0c84c6c3da6ef11f5a90b15ce7407c49e4da8464de4cfb1b3ac06834e543191de5b6c9c129801bef6034a94760742d4ab5f9f3732e1f1d3954aaead2e46803a19ee3bccf544d3b6580c33ed4670644124564f4c72f444064ce9735aa7b5b69891f690a9ce5c4d89411bf535cffddd7f8c4cb81ad710f4173f7189ccc38ab4b8ccd681b8d4934a4d85d00d390113d2219f49dd7d0b054cd263a72ffb87d7e38ef385272be6be2334f99989adf65ce5b09c6401740e827058765ba104891774126e0e1c51cc471ca91ff50c94d37f46b58ce9ed0bb9f14be1740fe140f0b3f64d8bb6b7859fc199758ddd88d73c31f1a5b4342990f825ae791791407ec4667d5d09b52171adb5499295169a736390abc0971a3fee2c71176e235a67aeab1c7e55a39426cfb03cea77a12b0840b428be140e3202f3ec6efd4492898fb514d11e1ace0849a3c9e723728f5f732d5380aecd870cf5d82935534d967dceefd3e30278843228c5f90774c75b6346417079ebb663c822b5bbfe129d18a53d74da768ead20b7880b0bb8bf80fdc28b3c0a7eed8e8d223a53b4e6fade925e2f533aeb9957acee79863daa55ae469430ddae24779f9b73ecf7d362e19065f4bd5a6f378cf45ce48973308f90536490a597f4f88b0076b553f58e40fdc0ef5b932598f697e7c723103b77918c70736c731a89806f3f88bbaed74822d9ef498c35a6b6a51404bd3a261ecb46c886ce68f2d93347a5fe1c2f0189ab94323e5bc672e8eb7febcc9b658d9a4c7fc14be6c156766224d23559edbf79d58e47185b8563e40a5b5adf0a6fbda9123bb4f468b74bdf8f39370a3810d12fa8e2aed03bc3f0e2e3bc37e9be7b4c1ea706bd1265e013eb892fb84e162475e35b8780be5601e9bb92d7f28655a42e011129628892751369102f920d058beeb49652542bd25939f3a6127b9ce1dbe617a1fe614319a1fb9dec776fc873ea656d8e78e3b56bf748bfaf36b75afa1b691c25276bf5a38d2304fae4324c7c780956eac545f5c23300b0e3d4e907fabee84f377ace50ee00c037b1326cb32d1f4acf65ad31a26b2187ef144f571fe25654bafa03efeb11df9034d2d69a8399ce7e49078e52e36ea259987033aed7cd45b42e73f03166c1f0b198fd1a12f110609c08d7b8d82517b586d460be0778201b9a3a8423fcb311d8421ade50d7775e9abfdb6d3a612f22bd7c640d5901d8413137089e587172e6e622b9219b406274b38c5f047c81bdb92b6675427ec53176e5f00e8c1507ee6c28e7b5ecdd9d3973f3c36bfe0d3074db87f355e8413eaa86334bb4df65851f6fa8aa63a5edb024d03b775feabdb32a97c7a9fbf22337d2b229a62c1d1272e483b4248eb4edffbbf445ed2416e5323a81b2d09a67b38c97a697d17a7595aae868fbefc75352bcc18be8023e20aff61de3b7fa023a56e02362f8716248aa780fae4bb555263ef5b5ab75eb6181d27527d167fcfc051472d0774a9a66a6e7ca82cbc51d93d377d9d8ef95760fa9cbc269df6d54c52d79f46114b237d4370bf736bd52baf38347efcb2d6b2673850a9bc22646cc93626a1221a3e876a5bf6add975e4185376dd9cdb12fc20717eb66e619e9d2ad3befcef1be126dea97b364959a110bcdbef5c587f23de59639db7c5246b0e156358f29de8918057d0bf5401eed35b50da203d635159bca8fe96f69f126e578afa4efd458b4b101bd743dd939e5d03ea5f897779a0f70dbafd68bbdc5a980d41257fb0b485c51cf6f65fd9b75773730f2eb673d7bf60fce792ed9b8b36492f42ea53ba1bdbd8f316e6f7c0a70db871f49185eb9d4c2a76f845ddc7c984e9b6db7c7c00d960d0f707a38ca7bfeeadbec5405bf626849e19c24ccd8ac268887d64cd37cf18b6ac1b616f372ab5fffe93d74f36cde57f1ca585cf199906d9add494fa6ef921d1739b56d282484a50e3e49d1ddacfb09d52794ff88ad6d479645d6f15e2dcebc3d1d28682e19b91304941ae594a5307bb4861f4537c5ee0892b0ed56510c08572293f4e95a30072714f335655b56ae676ac0ef597037b5530fbb4212ca36d6fe6b65afc095106c0e2479d5dfb15bb007f754077970e9bce07151278658ef1e243f1eb51f8c6823e552b65f724cfefbbda8fad20b2e9abc324577acde113d68aff737af78b8a3cc130942166f7d2000e82aed75d616352c1f07736d6dd42bd7d7838aa35353757fb7b8b2faeb8043595763affece4b1eebc0638bc9cca36c6ba62b8fd3334e17772295889faddf7dfdd835d45f3768d1ea546ebf9e04b6aef08952e7f47cd717d71afc106e9973c1641cca80c978918e72030048df55b10a1f9ad237e8384f71a2e1cd7e8ae8251dfb43472f0e2bb7b2493ef0849463748667d976036e271265213c9f1a9acea4293988aaa00310c5c7af437b5e05c875948b099a957a825929fb1d2d907b625d6aea3958e26ceaee1d0b11baca7a8a335b49d990d88c83078d0f4f95f848d7347ca9444ed363379c94356a1700f27c9cffedf5a01d190d3b419c71b63412006677a80a528a27816d740fbbe381b64d2dbd18c62c48f58d45e972dc7d51e7b631eb78396d47331c8df5537115d06ef61de7723e5633f5fd53b843b0226adeb4a46eef83f835c1d9b5c6f96a23be1609d72c9ce62b01ecb3902f841b12b7ed9f5ee0cb7252a4f4b5846d98e881493c1e11bab14380a917c75547fd219f54ea8cfe31b78432aa8608f1a5097afef200302784748492b7e5cd76c02120eee99dc96b8de650c6d8fad4e4b9f97fa9ee2d5782009b12c8f1540c916c4c7fee2050c069750f0be8e0e7ff36c245ffd9578069f93cf9f00cd6c6ad4651b2e351aa3de50743afffba5ac988f7a771adde8dd16730f3a728c49022a2e59fb20b666f49af85084dfdb676a5465b803402c0073c02a7f366ba81fd0fd5b2b7281bb1ebcf8f56ad9f94bf6f9dd7443c240d111a43ecd69b6d2c91c4419c20a6e6f5ac0ed2c5f21ab8ca466effc9af8d31514b52b00bae3a43d2b28549fb474b067ab3be7a2f80fe20ee7041b9d8c27340ba7098618df54c250c899a8786dd518e48ad004b6658ecc7143401c80a45d091b12eaba62065e3b8d44fc963cc186491627eb1e1106074d109546072b3e58e4bbdb8d8e07eeab38e71b524e8541e1e5573a5717f12120398ee3f29cef8405a69c6a87e6f091796a005e3e5689afe3ad47cea9c7cf5c504159b788340fc4f906ee10e7de5ab3af5c30962e0789fd3473c98f0ff4c472b69e68f9175facffd5f0ef93f633db1f8050ab31c31f17b924a84115de3e91c9d6ec6facfc119c3ee3530df5238dd6d7640fc8da31b9348554d30995b61db8e4ad8c0d04449231353876cbd5c0e976cf407f7de0a936a32acc617a3157fdbe694ab4f2634247eb8a15ef23af7adaf1db773e63811489bb5212f08b7c8bf21168b9ba06298d55d32fdb2386d1e0539083ae0c2f117c4675fea596ffcda4c603e98399546d4dfbdae316f5fe3726b059d17d99d41ddcf35130f3f037dea31af4176ba780d6eaa6545cf248d6a3dbb3b7c78cecbe968919d4643a59a77233609a2ca51af67f84cae258e763cd8d4531479fc7230cab7f6c8790b2be3662aaabad2634f9d60f48e9fb2e54e826aac28d3b53dbf83d17d6dcbc2a6cad15d6aeb7412721aa943a41fa8567a62d11e97224b3b0bc5de60c7028beaebb1e84e7b3916251f43e02e77a2663636c0f209e1e8d6dd0b09f8f632caf6e2aa3a4f73f76b6b6abfc67c22266d219aafc8517cf808459eaa806c994dd170c64d42f1bceae2c6b4d3a303b6f2fac20b0aac6fa26ad7895047ee620356ac33c1156a6f259f99eec4b418d753b3d3af1351abedbcbad4b9c6f612c3d83d9bbe81ab7a125c12d27dc4c162c2cfb15168551c922e58c4c078cde8a8d7c27754d28c8dd670254f2550fa9a8e58190e3510eb4d3e7f574fdc4427d63b04dcda35ca60933702b48bf014efe965dcb20278dad284a03a6c01abfe84deae9f5312e228ca45c507e32cb50fd0dbb3718ab747825c690300872386eb629837d02144fc537967950cfecdffa131eea58c39c5f39e6c8f7b12b6599411542ea88c510a006417bad525d1e0ee565bd42efe3a52c7ad4862987b60f75f5fc4dfd6f08f36806296a37e28261ccdbddeb6ad10295b1e681ce9efa51cbde86b2fabe95a7aacdd10246815c9c0681eafb2fcd54f139dda3b91030c4d089c18b2c36217c92851c5e4d96daded234a33c073806e18438d9dde9b29352190e0bc1d25c01d68c5f6a688e3f20cf02038d41896494d5234f3fdf977c11057ae0265444faada96dd6de4a15e032cea2c7210678ca5817b8ae935fcc85a82ad7412c47f15a76d8931013ffcfe282db6095618afb9cd1f7a93c61beb293b459346f1557c8e19ab0f5162b8c0ae85eb366ee424e671af2773654ab308eca5ec41f2c9d2466c5ff1a1de1bae91f811dd05bf8037fc58ecf7012cdacb62d9b1139b50ea768adf866481ed4cf6bd4f87f31bdb8771057ae173d9e2f1a3f28638668d5abddcc4160800392c5a494a45eeff0baa6bcd1816f236023fa6d957e6f389d554681ddddb9e7f9757707c99389e3c683c208e2db65a5689f8cc8695de3a394fcb2628c8b13453b0f15f2fc69040f78e2c08d7600c74393cc92027f039ee4a5c664311d3fcd602d06bd0c9a49304c11b267348a6ace6c996e20d365e3f897e5326f38c3894b5141828668190ef5a608c6dff044fa3f20e62e8f0940a6165c3f1caacffdd1840b1668b8041a53d23e38911e79e549c212eef1bc2c732ea8bad045b88bf3373c5803286bfe22bbf7e4614f1a8590dc67e955e1c70b742932b7bdebf8ab639993f897bb00a75be1fa42c8a688e8ca5765cdbd948d03067928abea055c87ca69338bd2014f4ebd0dd65b8b548ed33deb97916053c6aa0f73cb5d8d12e498b431f55efc7d7a86394298fbc21b93ec94d9c710f43b425ee1b675d3299e63328dbd9c7e183b752ab77e1d6f8e02595d3a7df0409e1d5dc8a69ded0b86fb0750104bdeeb8e7df28778d85324230e9420d25130894e7160b9759f2dff605d5779842e6d1596042336bc806eec3326e1f4f1074ea6d550aa0201fb069462e2c310dd0fa8b3067a7c72c68771443d5cac580b7e92424a16fcda3d275728ff284c11c3c5a2d3d5b011a14c7aff2d9747bb97425b84dec01203f2383254de84368ead3a65b2edf8c51f100669bce6af8c144417059c29d52645536f0e5ef0e3b32cf878e47628e6c7ed824734204243010001ca76ebf794d224e01f3c045a8634bc0070ae5f6fd2657c4e8493c17a2175792ae4f96e8a18c2818d818f0487216e5b0404e47667f87395e3478ac1caed618290fd4e106033000000000000000194deaf01c0d64c7960d57d2678f690628b6a0bee8282d495ceafb8c243420aa9ad64127b076b7f2bd7ca654edf41026d028f8d238fe14537b510197328a66b33e5ff0ac4d1b136fdd60f95a047b2e192da50f7df1aeeb193eaf8f62205527f71ef09bfcf309bfdee7837107638933a97b70a81d6a5bfbc5a5d6ff559d1cff32f76297fca67f80b5e481f9fd15bb79014d39e3b206e8e7a978dac4364a62f0986165e09a0f0a4b48a7b38be5052bc9439e3028952364d8d0e4a55418a9bee7aacc83ec44443ef35207878ef52b8daa60708e3fd4210dc54948f9221aeda4775ddce2a8d1a4809eb5a18ef4cf06c459002cd5d551f35e1e7e153e6690a71b65faba69f79bb5953e763ea444328c0f310bd585c492cf17a1d2527854e15ef69023de32257445a12ce15221cdb06a4cfd1769af134fdda9a11a51aa7a27347d79a30b6cfc7cf9e17aa3a41a5485189ac7cbdf5be5c7556bce4f6cb624f8a1cb9a627205d7a64331a4bc34ea60e86349417245feca5ff586c9bac1bb7a7932731c5fbf4bccfc1f089c5ef3bb3d85c43cc1f23c4b5cec63b1f6e9760b28bf6de0b987cfcb7bbf8e27c6b33e16f48f1c1c51b54cecd1ccc61306033afcfba7bd61c6827e3228b11198189389bbb47a1b169225ba2a2b63acfba0b92d399d4754be855ee723735d803f5a1d9f637cf92e6fe5bdfffc3b7b40ed06013bd852a03a995b38c9f93c80eff7eaa59d444a3647ecc710f3800a51ca608442f047ed85ccf22a821199d2a0cf6e2d092af6060a43a020cc80610cba4909e08e099961c171a07cea8af6575944faecc175cec7bbb008e8e78807d3db576d7abbd65ca4f99d795274bacc6c5fcee513b18342596d175d9a58a394a1f6744b918fe9daf974a5133c291bcb3dca6bd6ca92ba442725c3e2ddbb60e194b672d751798719763d202d4aac2baa88c3d710096e1be267aebe729eb31335dbc10db7ea1b5fe574ca6b6a057f858471afb62c0362765fa03dc262f6168235ef561537b3eb4d6baa65c9a553bf32528549cfdd427161638e4c3cf990f720757ca1ddcd3cd0f4a89bb590cc036244b84ece8988929cdb42aaa6049ca3ae3dc629dfe774c8b85f9a9233c0b9a1e21c6743f0a0b039b65aafa1a1eb8dae131912727219109a5d5ffcf1246afec6f83b34910509e85f8ef6635fa426a203df9cabcf3f04eccacff0fbc6b9d98c709d207dc3d89d706617ac37fd216067c7957662c4a796edb456d7ec7d75d8e7ea42ffedb9735c452751efb1bba99581dc7a137c8ca0a711cf14792c2e8c63139f5aed8efb56220fb77883bec7b810552fb3caf70946d607d98a5ad6cfeec2d17f1e18f6ab0ad1d15a2b0b7f3b2552edef2e2a52a2768bca019354b2bc80558ff759f1914c092bbb50d497c64e747ecd07e06d98271a5902b9f84f7623d0211b9ea31b718067567669615359643c6353aba1e66d9838cfe2430c957a6e4a059e0ce9c9a2a808852fa7dc3eded789d68825dc02c691b44d57e1fd6b2fbd055d18f03161eb38e1bb7b1cc49371b7cd21cb240e9eacfdf871d71353f63d7e71132711f4559adedc59f36e790f6f1ddd4eccabd9ccb2184d1eb9bcd53063c837431c2bd275d4e508aa0319e7123df87679c2283999936683da0e84a4bf792b65eb0823ed240c55171437a277617f4dd4af8ea7da9ebe30ebe741341c0ab9d5ea8d59cead8d0bd5caa6f4978a14aa6efc7cef6ae1db4ccd7937adf5edf20b8b0d446d4864c11bb0238dbdf206bae0d6da4f07c4181f1803f65d9c272f0755e819343dd020548b65b6e4b33f6ff78fb3e3e08a94476aad8d7f532923722270a48f84da7c7427e69324c0648d810f172e4b668ef4c89905c469fd29b03dbcb166a653e15d5aa70d075996fbf000f6771a92e543c0b25f4f4ac7426eca2129fcac04a9d85ce6009ed23baf153cc379cacd5b3b9bb54bbe6a4056e160931ecfe0e79e736781eb3a3a1183dab330a6e54ca848fe866da6c2739a9f37e29c0fe2791b22ba6645546373a718c7c454e8604612aa4769b94dc5416472be4281f1326373e105d8cb512e84c87e7c583579c85bc4d10e0ed075473e09dcafc760be460446a2e715bea5a6325286ed2f9dac4892f9bebcddf0df55591ba60881e4afe894c797440f2f623c0fdaee8366c68a0ff1f9a59f6a7209c145e8501a7f038d2d17a31a2dbdcc740cb22810a56eb481363c53b0ce5f6996d0375003bc4815bb3a46ba14fd5b1f74a4de5b89d64cf6bcf88159c60beb844c129b6ca0b51669dc6c805a5eadc15c7d878acd1c28207821633eea0a0ac10531feffb87f6fe0ec49f79219be19e61424de3d7bd696b1c8076756db2593767600103159e1ac4cb9e9128d681c8ca329e0ab54fc33bd4e10137bb97aaeba69e1f0ca889e1ba498f549025a5cca94419a428e12ec16801d6f8491e81230cfd4418af8dfc9cdba2412a52b16feb6e41a053327a01490f1eeb70e4cf6da9bf66f8cf414bc2a4d08bbd7e5f2013e9d7d080be78e5f196606797e4372f72056048881048a8fecea15561bb06bf85c09656d4b0daf0c9ad1ebb7cbdc5c91ea1397f0f8ed7acac1b97ae49a4119739646b7fc26ded35c47fea91b9160fcd9cc82e4e7f0278f251988f2c216d18f1142cf200fc6bbd4270fafc27fe022fe5a540081876ebeea6afc14c61c2d15e2776e03c758c1b2e0c2665eb84ca91303e3a7ba2377447046ec97e2c42a333b75205a065e9948d0a6e9b9816ae91fe1a4a95054830a67eadf3dd8eeac20bd38c7834a3b5727bba1eaed06c720edaaf2cd7ca20e6ae47be4df6751302080471e85c3a77a365e24d554250b660b7f1a2a331dc16081ec4351f16ca78e25af224e591e3f05229a27f4cc211c7daea32614a2952559d9511de9187a9d30447339a56c7eb48962e3c036549378bcfbec7745419d9ffbeacd98cafe293d118e126c43b7359d8e58cc8dd6afa83cfd54fce8fd2878ad9352c4f1dc8435e356a2b934ce9af296a83cad7847634d9db0cca34cecb4c1161b755462bb6bd4dc9c8d7bcf7c4e48fecc50eab8ae090e4ceaa119223541b2f0d59136680a9f13827c17ae479b5adf85ba36a87601e8e2a98263e442a7d24dc95b29c8b9d90938578a907429df0c2d92d6bee12c73aa1ca2729dda54f0684dd007afbf8444b507977a57c25f99c92c94db96c212258900b6132f5692d7ad86abffcd406ed139107dc8f2ccaaaae342eacb9635494bac6dd9b23f43c1736ec98aa56a4827a0d34f149041224544e1ccff55d541d993399a7e927ff82a5fe17742c10236ef612ab0344dc56a94efceea78ad1394bd461c060e7bc640c18f2f06253c7767fa2e58d15edd131f8595491024bad871d0629b203be2f2390ea748bb4e7b896b9912b59e54c2ba50a74844335ed8222ae65db7c09c2c3183bc8d6590e17551fd03ac80d68564fbbced4c45577ea1134954c7442cacc5fa7246b2343479fcf5d41a0656f9cb30c3f6d1cb9228f505e0ddb415c125a3e3fc128199575454080d15c44dd6f15d670c9b7c480159483f273ec3b48f39799150560410db2c2400c52479e19c0b1473a80211400306635b5f516022369c1f2eeb6b0e1d298a29530f7af574da6d268cd8b2ca3058844c47395f08c3e78e921111a7fc1f837188c98ae8683f1923242cca7d629334f285e0188ef6cbdfa21aa63cef5f6798f933a416edccf2ea5f38a1bc6e0c9e6427e537bee79ccec63efe8be3ff9c258ef81c7c3dbde73e1c2ad298babd3761b057481b78bcc84f2ff5c01a9881fe4e0906ef28dbdbdfb0c9db39e6130ba4a4b3fca79c14500db104ce62fe3424ddf0bf8f5fccf47b2eb45de0a8f1e064673f0b17d4d03a5403a0eb1bf0f950ed9989b6050c067aac7f6b3cb57bd1b3094c38d932a0f9442550afc0a4426415e6d881c957c70ddcfa5a2b1cfdee2c892d0502e71a106c4b9bf45b44ed288555a734f058bcbeabc4bc229389926d8cf5758368080585bc2f4de55e62b1bc5949f94e9f8ffc5d3629b4559f03992b1cd385f4ce62e5c4af590dac1711f180ba1a0a2a1aec446ad9a33d6355fa84c78df71769c5c91026e603b161eb900db96c62b81408f652acd2ea08cd063f673b4bd87a6874f38277d052be0494516a820ed875dc7b84be802a5a1f4460224937a815f93de3385b9f25e125af199d12c3bcf10c2279cdef83a3fafa15449cd599a21ca9c1d73f0f099432c5e5ef2dc094ca7648a3154d2999352d9f8fad81741aae68771e22fa8349260a7a9d892cc2c401e628fa269a26b85bac6c059647186ddda33fd4f284c73b4b279e212c7a76f690307f5bfc18e4197cc32038aa35d2a59d1d96b10fc11fddbd4c6d7c3ba934033af60b88902e50b7701baecdb44338901bc188f4ea383dde437ddac1a9947e797b23507506602cbd14935d137aaa8bd9d51c301363867ff88a9994d89bc992fb437f3d4cfd68e86e27fc04160d83669b0ca30e0cda60caea363876c819472d5b1ff31ed82b659a8d84b159350a35728350b4c771c5e20809c37d03689e31c64a01e601f666e393b6573e5915429456ce25611863d8893475222e9f4f8b3008210725a4c8ee9a7c0c1245004ee004b34d9852ecdf6d9ded38dcff58167e71ba15dad128803b49000405d4d04ce3195e0d30bb8fc3324d4b1c4fdb0839b8c3cc03149cdfdd57db4806ee998842857daa27f01e888280d9eeeedb7d5b9331ddcecc09297b2a906e778cdb14f4a107745a608e9c46fddecbb573663c1702b3ec7a68eb788525fb81d8c69783d68561aee3f90aa13327e836d0589b81f23a3062aea5be6e3c26ffaac3f263c61e22471b758c64c832e754ce0034ae16c6754b7569ae1d84f274efdfec03b7f13016701cbcbd4cf99d87d2acd35d2517ab0c405c9d0087c39e71f05624f6dccd7557dacf3aef4248993934d0694f722196a2c30e1f503168b3c16aa15fb7ceb9fc6eeded321ab0b9460c7f5813437f7c5f9f6218aa72edc18380552c08b40ad2064d2c4663d0c0858404b7d3906fc1fa5460f6de6a7f6c50b245faaaae7cba553031811aa6bcfce051606bdb87571d05105e1dee14f6c96e05f237f52d61c26dfacef2290d52dfc18da38f0ce31bfe8a82cc01a115c410d9fb75a8bfa8a45353c2d02e73f358ff208e953fc282f9cbe872ff1f939a67d180c8f77b648dab80fded543cdf4d8d79be5d4af99ace35113c696ce91a9d7a294e388391bca4c84095baf41bd53d1580aef022e0c8f5115da75b4d51d86ddfb5f00e3b4aefad927c3e5dedf1675203c160d45c07b37f6052c9b6cebd6fb1e55a1c276484d1c479c07757ef2b4ac3da720b7cffad79165b022ee56f746ef09d54e7a3a972a3f4e4e02d8e417b0efa04b9b981cd9158495619226bb090de42fb0cdc88f806454d58519e00ef29e0de6f21c28b556d93134caa6205c913f54940af77c569fc7d2400f39f1b9114918d29e770dda3814e963f59da5b2e45415cd2cf9cbc386ab9fbc39240c359c1b12b021dd629e9828f5fd7e5aee2032ad5249805e7782ee5c44b8e7d08f6097733defe7762703ac48acebe153802f84fd4d154782d7b57d75650df3a9bcca859090422ef135fd9cf92755ff891a852654fa99b47584324b5df7c82f3f852fe03d3e5d4404a77e92c415706cf7f9e458a955810e9704155554a9188b54bce1ad027bb3c345fe8531e6db632bc1c0958d159ce7da49d63073d966923271ef9a3b1d87693029de3169996f45aa5c42ac7c194da680365e503f56ef57b10c1e790b2da17fe545bb679bd449b1d18ef6654fee89f4e4513c6f5bfe206e7850feb5af3c4545341318637bb7a7259806646aedc308069a9b0000"; + const CONF_TX_BYTES: &str = "020000000101a40918d1431b219b2e8c86a7ee49e025c9ad67156e6be7971e7f8cbc10e5c6620000000000fdffffff030a5462b0c147862343b3d81698a591d10c567f8b987e9d9308c671de79da4c3c5008d9a5ea8db9337280a430ea442559303bde86ba8d3c6fa9278711dac9d16f79f002c15772c83e57465b193d6ea6dc992177ff645b8587e9f6f1e8e841e8c2694eb0160014c0c88e225008667047624968c1c286427fd74feb0b8d9cdc6e69c8d6686fda938a45e1529e81cc3e43313cca4a806c1186dcd124a80915da44966d2a31f3f2f61fd95e742353dbf54ad18ba709efa3cc9f377835b251022677700604ef92070c670338c5349a64e82e4f413e581d06a6dea60bb82c013d1600147e172b800e0ac0396a37af6e3aefaf7d3c1293a301230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b20100000000000000f90000650000000000024730440220719dd6e625a2bb75208ef5a38025c50a7bf080ae701b5f72ebb458a8f0ed634f02203190506ee407196683312d0c571cbb574005d53ca32e202ac6035aeae5ec5cd1012103f424277b3d9d05f1b207918854d3a51c3f83629b49ea5c057f7f50c243cef027004301000105f328173c0a001f3544362bdffce10621ae6f0fc8f8b07f7a16364ea7b9dbebbd08cb6970c34f8153737a608b2d78a9bae86d7ac32315d8576bb01af578050cfd4e106033000000000000000147808100efe85e666a8e2a8e8199373b997f3500b8e971c480071bffa4b92e54ac226427d53659b9aeaf2270d7932a75e30acfb7a19082c068f66723574de9d1cf7d0e63fbba5840cf0d04487833a6aa96340b5ca89c8d21847fa5cae77ffe68f0eb0d6c2cf730073c460dcb278588f005f4f6d63ef528b756f50e420906a4e60aebae7102539712f4bde34f90f0e77c403f728070ececbb66af14e82912c3b3585a2debbcbe4c7b98b8e36d0e2b7f12344a31f514231545ac20f441501c1f72b04045defe9f6579be2866f2e1c42381bcff654280be25a4aab13a94a48c75672e3b0586223e0ffd2477515a4a7458b308958a0e459dab3bb338b4684dbf7e13827ce3ef8f8f344c338cb0d89a009bfc2a942eccdaf8581db3221c4f3ca868d42fb18cacf889728260c2323319dd11a6ce00461e28ce8fdd92dcb8e089b9b244510e2ea1546e09f446e2a911532fa4952d077130121ef1564081ea2d7e2c62b12f3c665c65d83f27b3c3138907ec5e44bb8283c0ad07deafddfefd303347f0ac95ca9faac39a6510e16f17b37acaa70389d746cd4475e59703f4f72e40cdc0ce0c84c6c3da6ef11f5a90b15ce7407c49e4da8464de4cfb1b3ac06834e543191de5b6c9c129801bef6034a94760742d4ab5f9f3732e1f1d3954aaead2e46803a19ee3bccf544d3b6580c33ed4670644124564f4c72f444064ce9735aa7b5b69891f690a9ce5c4d89411bf535cffddd7f8c4cb81ad710f4173f7189ccc38ab4b8ccd681b8d4934a4d85d00d390113d2219f49dd7d0b054cd263a72ffb87d7e38ef385272be6be2334f99989adf65ce5b09c6401740e827058765ba104891774126e0e1c51cc471ca91ff50c94d37f46b58ce9ed0bb9f14be1740fe140f0b3f64d8bb6b7859fc199758ddd88d73c31f1a5b4342990f825ae791791407ec4667d5d09b52171adb5499295169a736390abc0971a3fee2c71176e235a67aeab1c7e55a39426cfb03cea77a12b0840b428be140e3202f3ec6efd4492898fb514d11e1ace0849a3c9e723728f5f732d5380aecd870cf5d82935534d967dceefd3e30278843228c5f90774c75b6346417079ebb663c822b5bbfe129d18a53d74da768ead20b7880b0bb8bf80fdc28b3c0a7eed8e8d223a53b4e6fade925e2f533aeb9957acee79863daa55ae469430ddae24779f9b73ecf7d362e19065f4bd5a6f378cf45ce48973308f90536490a597f4f88b0076b553f58e40fdc0ef5b932598f697e7c723103b77918c70736c731a89806f3f88bbaed74822d9ef498c35a6b6a51404bd3a261ecb46c886ce68f2d93347a5fe1c2f0189ab94323e5bc672e8eb7febcc9b658d9a4c7fc14be6c156766224d23559edbf79d58e47185b8563e40a5b5adf0a6fbda9123bb4f468b74bdf8f39370a3810d12fa8e2aed03bc3f0e2e3bc37e9be7b4c1ea706bd1265e013eb892fb84e162475e35b8780be5601e9bb92d7f28655a42e011129628892751369102f920d058beeb49652542bd25939f3a6127b9ce1dbe617a1fe614319a1fb9dec776fc873ea656d8e78e3b56bf748bfaf36b75afa1b691c25276bf5a38d2304fae4324c7c780956eac545f5c23300b0e3d4e907fabee84f377ace50ee00c037b1326cb32d1f4acf65ad31a26b2187ef144f571fe25654bafa03efeb11df9034d2d69a8399ce7e49078e52e36ea259987033aed7cd45b42e73f03166c1f0b198fd1a12f110609c08d7b8d82517b586d460be0778201b9a3a8423fcb311d8421ade50d7775e9abfdb6d3a612f22bd7c640d5901d8413137089e587172e6e622b9219b406274b38c5f047c81bdb92b6675427ec53176e5f00e8c1507ee6c28e7b5ecdd9d3973f3c36bfe0d3074db87f355e8413eaa86334bb4df65851f6fa8aa63a5edb024d03b775feabdb32a97c7a9fbf22337d2b229a62c1d1272e483b4248eb4edffbbf445ed2416e5323a81b2d09a67b38c97a697d17a7595aae868fbefc75352bcc18be8023e20aff61de3b7fa023a56e02362f8716248aa780fae4bb555263ef5b5ab75eb6181d27527d167fcfc051472d0774a9a66a6e7ca82cbc51d93d377d9d8ef95760fa9cbc269df6d54c52d79f46114b237d4370bf736bd52baf38347efcb2d6b2673850a9bc22646cc93626a1221a3e876a5bf6add975e4185376dd9cdb12fc20717eb66e619e9d2ad3befcef1be126dea97b364959a110bcdbef5c587f23de59639db7c5246b0e156358f29de8918057d0bf5401eed35b50da203d635159bca8fe96f69f126e578afa4efd458b4b101bd743dd939e5d03ea5f897779a0f70dbafd68bbdc5a980d41257fb0b485c51cf6f65fd9b75773730f2eb673d7bf60fce792ed9b8b36492f42ea53ba1bdbd8f316e6f7c0a70db871f49185eb9d4c2a76f845ddc7c984e9b6db7c7c00d960d0f707a38ca7bfeeadbec5405bf626849e19c24ccd8ac268887d64cd37cf18b6ac1b616f372ab5fffe93d74f36cde57f1ca585cf199906d9add494fa6ef921d1739b56d282484a50e3e49d1ddacfb09d52794ff88ad6d479645d6f15e2dcebc3d1d28682e19b91304941ae594a5307bb4861f4537c5ee0892b0ed56510c08572293f4e95a30072714f335655b56ae676ac0ef597037b5530fbb4212ca36d6fe6b65afc095106c0e2479d5dfb15bb007f754077970e9bce07151278658ef1e243f1eb51f8c6823e552b65f724cfefbbda8fad20b2e9abc324577acde113d68aff737af78b8a3cc130942166f7d2000e82aed75d616352c1f07736d6dd42bd7d7838aa35353757fb7b8b2faeb8043595763affece4b1eebc0638bc9cca36c6ba62b8fd3334e17772295889faddf7dfdd835d45f3768d1ea546ebf9e04b6aef08952e7f47cd717d71afc106e9973c1641cca80c978918e72030048df55b10a1f9ad237e8384f71a2e1cd7e8ae8251dfb43472f0e2bb7b2493ef0849463748667d976036e271265213c9f1a9acea4293988aaa00310c5c7af437b5e05c875948b099a957a825929fb1d2d907b625d6aea3958e26ceaee1d0b11baca7a8a335b49d990d88c83078d0f4f95f848d7347ca9444ed363379c94356a1700f27c9cffedf5a01d190d3b419c71b63412006677a80a528a27816d740fbbe381b64d2dbd18c62c48f58d45e972dc7d51e7b631eb78396d47331c8df5537115d06ef61de7723e5633f5fd53b843b0226adeb4a46eef83f835c1d9b5c6f96a23be1609d72c9ce62b01ecb3902f841b12b7ed9f5ee0cb7252a4f4b5846d98e881493c1e11bab14380a917c75547fd219f54ea8cfe31b78432aa8608f1a5097afef200302784748492b7e5cd76c02120eee99dc96b8de650c6d8fad4e4b9f97fa9ee2d5782009b12c8f1540c916c4c7fee2050c069750f0be8e0e7ff36c245ffd9578069f93cf9f00cd6c6ad4651b2e351aa3de50743afffba5ac988f7a771adde8dd16730f3a728c49022a2e59fb20b666f49af85084dfdb676a5465b803402c0073c02a7f366ba81fd0fd5b2b7281bb1ebcf8f56ad9f94bf6f9dd7443c240d111a43ecd69b6d2c91c4419c20a6e6f5ac0ed2c5f21ab8ca466effc9af8d31514b52b00bae3a43d2b28549fb474b067ab3be7a2f80fe20ee7041b9d8c27340ba7098618df54c250c899a8786dd518e48ad004b6658ecc7143401c80a45d091b12eaba62065e3b8d44fc963cc186491627eb1e1106074d109546072b3e58e4bbdb8d8e07eeab38e71b524e8541e1e5573a5717f12120398ee3f29cef8405a69c6a87e6f091796a005e3e5689afe3ad47cea9c7cf5c504159b788340fc4f906ee10e7de5ab3af5c30962e0789fd3473c98f0ff4c472b69e68f9175facffd5f0ef93f633db1f8050ab31c31f17b924a84115de3e91c9d6ec6facfc119c3ee3530df5238dd6d7640fc8da31b9348554d30995b61db8e4ad8c0d04449231353876cbd5c0e976cf407f7de0a936a32acc617a3157fdbe694ab4f2634247eb8a15ef23af7adaf1db773e63811489bb5212f08b7c8bf21168b9ba06298d55d32fdb2386d1e0539083ae0c2f117c4675fea596ffcda4c603e98399546d4dfbdae316f5fe3726b059d17d99d41ddcf35130f3f037dea31af4176ba780d6eaa6545cf248d6a3dbb3b7c78cecbe968919d4643a59a77233609a2ca51af67f84cae258e763cd8d4531479fc7230cab7f6c8790b2be3662aaabad2634f9d60f48e9fb2e54e826aac28d3b53dbf83d17d6dcbc2a6cad15d6aeb7412721aa943a41fa8567a62d11e97224b3b0bc5de60c7028beaebb1e84e7b3916251f43e02e77a2663636c0f209e1e8d6dd0b09f8f632caf6e2aa3a4f73f76b6b6abfc67c22266d219aafc8517cf808459eaa806c994dd170c64d42f1bceae2c6b4d3a303b6f2fac20b0aac6fa26ad7895047ee620356ac33c1156a6f259f99eec4b418d753b3d3af1351abedbcbad4b9c6f612c3d83d9bbe81ab7a125c12d27dc4c162c2cfb15168551c922e58c4c078cde8a8d7c27754d28c8dd670254f2550fa9a8e58190e3510eb4d3e7f574fdc4427d63b04dcda35ca60933702b48bf014efe965dcb20278dad284a03a6c01abfe84deae9f5312e228ca45c507e32cb50fd0dbb3718ab747825c690300872386eb629837d02144fc537967950cfecdffa131eea58c39c5f39e6c8f7b12b6599411542ea88c510a006417bad525d1e0ee565bd42efe3a52c7ad4862987b60f75f5fc4dfd6f08f36806296a37e28261ccdbddeb6ad10295b1e681ce9efa51cbde86b2fabe95a7aacdd10246815c9c0681eafb2fcd54f139dda3b91030c4d089c18b2c36217c92851c5e4d96daded234a33c073806e18438d9dde9b29352190e0bc1d25c01d68c5f6a688e3f20cf02038d41896494d5234f3fdf977c11057ae0265444faada96dd6de4a15e032cea2c7210678ca5817b8ae935fcc85a82ad7412c47f15a76d8931013ffcfe282db6095618afb9cd1f7a93c61beb293b459346f1557c8e19ab0f5162b8c0ae85eb366ee424e671af2773654ab308eca5ec41f2c9d2466c5ff1a1de1bae91f811dd05bf8037fc58ecf7012cdacb62d9b1139b50ea768adf866481ed4cf6bd4f87f31bdb8771057ae173d9e2f1a3f28638668d5abddcc4160800392c5a494a45eeff0baa6bcd1816f236023fa6d957e6f389d554681ddddb9e7f9757707c99389e3c683c208e2db65a5689f8cc8695de3a394fcb2628c8b13453b0f15f2fc69040f78e2c08d7600c74393cc92027f039ee4a5c664311d3fcd602d06bd0c9a49304c11b267348a6ace6c996e20d365e3f897e5326f38c3894b5141828668190ef5a608c6dff044fa3f20e62e8f0940a6165c3f1caacffdd1840b1668b8041a53d23e38911e79e549c212eef1bc2c732ea8bad045b88bf3373c5803286bfe22bbf7e4614f1a8590dc67e955e1c70b742932b7bdebf8ab639993f897bb00a75be1fa42c8a688e8ca5765cdbd948d03067928abea055c87ca69338bd2014f4ebd0dd65b8b548ed33deb97916053c6aa0f73cb5d8d12e498b431f55efc7d7a86394298fbc21b93ec94d9c710f43b425ee1b675d3299e63328dbd9c7e183b752ab77e1d6f8e02595d3a7df0409e1d5dc8a69ded0b86fb0750104bdeeb8e7df28778d85324230e9420d25130894e7160b9759f2dff605d5779842e6d1596042336bc806eec3326e1f4f1074ea6d550aa0201fb069462e2c310dd0fa8b3067a7c72c68771443d5cac580b7e92424a16fcda3d275728ff284c11c3c5a2d3d5b011a14c7aff2d9747bb97425b84dec01203f2383254de84368ead3a65b2edf8c51f100669bce6af8c144417059c29d52645536f0e5ef0e3b32cf878e47628e6c7ed824734204243010001ca76ebf794d224e01f3c045a8634bc0070ae5f6fd2657c4e8493c17a2175792ae4f96e8a18c2818d818f0487216e5b0404e47667f87395e3478ac1caed618290fd4e106033000000000000000194deaf01c0d64c7960d57d2678f690628b6a0bee8282d495ceafb8c243420aa9ad64127b076b7f2bd7ca654edf41026d028f8d238fe14537b510197328a66b33e5ff0ac4d1b136fdd60f95a047b2e192da50f7df1aeeb193eaf8f62205527f71ef09bfcf309bfdee7837107638933a97b70a81d6a5bfbc5a5d6ff559d1cff32f76297fca67f80b5e481f9fd15bb79014d39e3b206e8e7a978dac4364a62f0986165e09a0f0a4b48a7b38be5052bc9439e3028952364d8d0e4a55418a9bee7aacc83ec44443ef35207878ef52b8daa60708e3fd4210dc54948f9221aeda4775ddce2a8d1a4809eb5a18ef4cf06c459002cd5d551f35e1e7e153e6690a71b65faba69f79bb5953e763ea444328c0f310bd585c492cf17a1d2527854e15ef69023de32257445a12ce15221cdb06a4cfd1769af134fdda9a11a51aa7a27347d79a30b6cfc7cf9e17aa3a41a5485189ac7cbdf5be5c7556bce4f6cb624f8a1cb9a627205d7a64331a4bc34ea60e86349417245feca5ff586c9bac1bb7a7932731c5fbf4bccfc1f089c5ef3bb3d85c43cc1f23c4b5cec63b1f6e9760b28bf6de0b987cfcb7bbf8e27c6b33e16f48f1c1c51b54cecd1ccc61306033afcfba7bd61c6827e3228b11198189389bbb47a1b169225ba2a2b63acfba0b92d399d4754be855ee723735d803f5a1d9f637cf92e6fe5bdfffc3b7b40ed06013bd852a03a995b38c9f93c80eff7eaa59d444a3647ecc710f3800a51ca608442f047ed85ccf22a821199d2a0cf6e2d092af6060a43a020cc80610cba4909e08e099961c171a07cea8af6575944faecc175cec7bbb008e8e78807d3db576d7abbd65ca4f99d795274bacc6c5fcee513b18342596d175d9a58a394a1f6744b918fe9daf974a5133c291bcb3dca6bd6ca92ba442725c3e2ddbb60e194b672d751798719763d202d4aac2baa88c3d710096e1be267aebe729eb31335dbc10db7ea1b5fe574ca6b6a057f858471afb62c0362765fa03dc262f6168235ef561537b3eb4d6baa65c9a553bf32528549cfdd427161638e4c3cf990f720757ca1ddcd3cd0f4a89bb590cc036244b84ece8988929cdb42aaa6049ca3ae3dc629dfe774c8b85f9a9233c0b9a1e21c6743f0a0b039b65aafa1a1eb8dae131912727219109a5d5ffcf1246afec6f83b34910509e85f8ef6635fa426a203df9cabcf3f04eccacff0fbc6b9d98c709d207dc3d89d706617ac37fd216067c7957662c4a796edb456d7ec7d75d8e7ea42ffedb9735c452751efb1bba99581dc7a137c8ca0a711cf14792c2e8c63139f5aed8efb56220fb77883bec7b810552fb3caf70946d607d98a5ad6cfeec2d17f1e18f6ab0ad1d15a2b0b7f3b2552edef2e2a52a2768bca019354b2bc80558ff759f1914c092bbb50d497c64e747ecd07e06d98271a5902b9f84f7623d0211b9ea31b718067567669615359643c6353aba1e66d9838cfe2430c957a6e4a059e0ce9c9a2a808852fa7dc3eded789d68825dc02c691b44d57e1fd6b2fbd055d18f03161eb38e1bb7b1cc49371b7cd21cb240e9eacfdf871d71353f63d7e71132711f4559adedc59f36e790f6f1ddd4eccabd9ccb2184d1eb9bcd53063c837431c2bd275d4e508aa0319e7123df87679c2283999936683da0e84a4bf792b65eb0823ed240c55171437a277617f4dd4af8ea7da9ebe30ebe741341c0ab9d5ea8d59cead8d0bd5caa6f4978a14aa6efc7cef6ae1db4ccd7937adf5edf20b8b0d446d4864c11bb0238dbdf206bae0d6da4f07c4181f1803f65d9c272f0755e819343dd020548b65b6e4b33f6ff78fb3e3e08a94476aad8d7f532923722270a48f84da7c7427e69324c0648d810f172e4b668ef4c89905c469fd29b03dbcb166a653e15d5aa70d075996fbf000f6771a92e543c0b25f4f4ac7426eca2129fcac04a9d85ce6009ed23baf153cc379cacd5b3b9bb54bbe6a4056e160931ecfe0e79e736781eb3a3a1183dab330a6e54ca848fe866da6c2739a9f37e29c0fe2791b22ba6645546373a718c7c454e8604612aa4769b94dc5416472be4281f1326373e105d8cb512e84c87e7c583579c85bc4d10e0ed075473e09dcafc760be460446a2e715bea5a6325286ed2f9dac4892f9bebcddf0df55591ba60881e4afe894c797440f2f623c0fdaee8366c68a0ff1f9a59f6a7209c145e8501a7f038d2d17a31a2dbdcc740cb22810a56eb481363c53b0ce5f6996d0375003bc4815bb3a46ba14fd5b1f74a4de5b89d64cf6bcf88159c60beb844c129b6ca0b51669dc6c805a5eadc15c7d878acd1c28207821633eea0a0ac10531feffb87f6fe0ec49f79219be19e61424de3d7bd696b1c8076756db2593767600103159e1ac4cb9e9128d681c8ca329e0ab54fc33bd4e10137bb97aaeba69e1f0ca889e1ba498f549025a5cca94419a428e12ec16801d6f8491e81230cfd4418af8dfc9cdba2412a52b16feb6e41a053327a01490f1eeb70e4cf6da9bf66f8cf414bc2a4d08bbd7e5f2013e9d7d080be78e5f196606797e4372f72056048881048a8fecea15561bb06bf85c09656d4b0daf0c9ad1ebb7cbdc5c91ea1397f0f8ed7acac1b97ae49a4119739646b7fc26ded35c47fea91b9160fcd9cc82e4e7f0278f251988f2c216d18f1142cf200fc6bbd4270fafc27fe022fe5a540081876ebeea6afc14c61c2d15e2776e03c758c1b2e0c2665eb84ca91303e3a7ba2377447046ec97e2c42a333b75205a065e9948d0a6e9b9816ae91fe1a4a95054830a67eadf3dd8eeac20bd38c7834a3b5727bba1eaed06c720edaaf2cd7ca20e6ae47be4df6751302080471e85c3a77a365e24d554250b660b7f1a2a331dc16081ec4351f16ca78e25af224e591e3f05229a27f4cc211c7daea32614a2952559d9511de9187a9d30447339a56c7eb48962e3c036549378bcfbec7745419d9ffbeacd98cafe293d118e126c43b7359d8e58cc8dd6afa83cfd54fce8fd2878ad9352c4f1dc8435e356a2b934ce9af296a83cad7847634d9db0cca34cecb4c1161b755462bb6bd4dc9c8d7bcf7c4e48fecc50eab8ae090e4ceaa119223541b2f0d59136680a9f13827c17ae479b5adf85ba36a87601e8e2a98263e442a7d24dc95b29c8b9d90938578a907429df0c2d92d6bee12c73aa1ca2729dda54f0684dd007afbf8444b507977a57c25f99c92c94db96c212258900b6132f5692d7ad86abffcd406ed139107dc8f2ccaaaae342eacb9635494bac6dd9b23f43c1736ec98aa56a4827a0d34f149041224544e1ccff55d541d993399a7e927ff82a5fe17742c10236ef612ab0344dc56a94efceea78ad1394bd461c060e7bc640c18f2f06253c7767fa2e58d15edd131f8595491024bad871d0629b203be2f2390ea748bb4e7b896b9912b59e54c2ba50a74844335ed8222ae65db7c09c2c3183bc8d6590e17551fd03ac80d68564fbbced4c45577ea1134954c7442cacc5fa7246b2343479fcf5d41a0656f9cb30c3f6d1cb9228f505e0ddb415c125a3e3fc128199575454080d15c44dd6f15d670c9b7c480159483f273ec3b48f39799150560410db2c2400c52479e19c0b1473a80211400306635b5f516022369c1f2eeb6b0e1d298a29530f7af574da6d268cd8b2ca3058844c47395f08c3e78e921111a7fc1f837188c98ae8683f1923242cca7d629334f285e0188ef6cbdfa21aa63cef5f6798f933a416edccf2ea5f38a1bc6e0c9e6427e537bee79ccec63efe8be3ff9c258ef81c7c3dbde73e1c2ad298babd3761b057481b78bcc84f2ff5c01a9881fe4e0906ef28dbdbdfb0c9db39e6130ba4a4b3fca79c14500db104ce62fe3424ddf0bf8f5fccf47b2eb45de0a8f1e064673f0b17d4d03a5403a0eb1bf0f950ed9989b6050c067aac7f6b3cb57bd1b3094c38d932a0f9442550afc0a4426415e6d881c957c70ddcfa5a2b1cfdee2c892d0502e71a106c4b9bf45b44ed288555a734f058bcbeabc4bc229389926d8cf5758368080585bc2f4de55e62b1bc5949f94e9f8ffc5d3629b4559f03992b1cd385f4ce62e5c4af590dac1711f180ba1a0a2a1aec446ad9a33d6355fa84c78df71769c5c91026e603b161eb900db96c62b81408f652acd2ea08cd063f673b4bd87a6874f38277d052be0494516a820ed875dc7b84be802a5a1f4460224937a815f93de3385b9f25e125af199d12c3bcf10c2279cdef83a3fafa15449cd599a21ca9c1d73f0f099432c5e5ef2dc094ca7648a3154d2999352d9f8fad81741aae68771e22fa8349260a7a9d892cc2c401e628fa269a26b85bac6c059647186ddda33fd4f284c73b4b279e212c7a76f690307f5bfc18e4197cc32038aa35d2a59d1d96b10fc11fddbd4c6d7c3ba934033af60b88902e50b7701baecdb44338901bc188f4ea383dde437ddac1a9947e797b23507506602cbd14935d137aaa8bd9d51c301363867ff88a9994d89bc992fb437f3d4cfd68e86e27fc04160d83669b0ca30e0cda60caea363876c819472d5b1ff31ed82b659a8d84b159350a35728350b4c771c5e20809c37d03689e31c64a01e601f666e393b6573e5915429456ce25611863d8893475222e9f4f8b3008210725a4c8ee9a7c0c1245004ee004b34d9852ecdf6d9ded38dcff58167e71ba15dad128803b49000405d4d04ce3195e0d30bb8fc3324d4b1c4fdb0839b8c3cc03149cdfdd57db4806ee998842857daa27f01e888280d9eeeedb7d5b9331ddcecc09297b2a906e778cdb14f4a107745a608e9c46fddecbb573663c1702b3ec7a68eb788525fb81d8c69783d68561aee3f90aa13327e836d0589b81f23a3062aea5be6e3c26ffaac3f263c61e22471b758c64c832e754ce0034ae16c6754b7569ae1d84f274efdfec03b7f13016701cbcbd4cf99d87d2acd35d2517ab0c405c9d0087c39e71f05624f6dccd7557dacf3aef4248993934d0694f722196a2c30e1f503168b3c16aa15fb7ceb9fc6eeded321ab0b9460c7f5813437f7c5f9f6218aa72edc18380552c08b40ad2064d2c4663d0c0858404b7d3906fc1fa5460f6de6a7f6c50b245faaaae7cba553031811aa6bcfce051606bdb87571d05105e1dee14f6c96e05f237f52d61c26dfacef2290d52dfc18da38f0ce31bfe8a82cc01a115c410d9fb75a8bfa8a45353c2d02e73f358ff208e953fc282f9cbe872ff1f939a67d180c8f77b648dab80fded543cdf4d8d79be5d4af99ace35113c696ce91a9d7a294e388391bca4c84095baf41bd53d1580aef022e0c8f5115da75b4d51d86ddfb5f00e3b4aefad927c3e5dedf1675203c160d45c07b37f6052c9b6cebd6fb1e55a1c276484d1c479c07757ef2b4ac3da720b7cffad79165b022ee56f746ef09d54e7a3a972a3f4e4e02d8e417b0efa04b9b981cd9158495619226bb090de42fb0cdc88f806454d58519e00ef29e0de6f21c28b556d93134caa6205c913f54940af77c569fc7d2400f39f1b9114918d29e770dda3814e963f59da5b2e45415cd2cf9cbc386ab9fbc39240c359c1b12b021dd629e9828f5fd7e5aee2032ad5249805e7782ee5c44b8e7d08f6097733defe7762703ac48acebe153802f84fd4d154782d7b57d75650df3a9bcca859090422ef135fd9cf92755ff891a852654fa99b47584324b5df7c82f3f852fe03d3e5d4404a77e92c415706cf7f9e458a955810e9704155554a9188b54bce1ad027bb3c345fe8531e6db632bc1c0958d159ce7da49d63073d966923271ef9a3b1d87693029de3169996f45aa5c42ac7c194da680365e503f56ef57b10c1e790b2da17fe545bb679bd449b1d18ef6654fee89f4e4513c6f5bfe206e7850feb5af3c4545341318637bb7a7259806646aedc308069a9b0000"; /// Creates a fee TxOut with explicit asset and amount pub(super) fn fee() -> elements::TxOut { @@ -263,7 +263,7 @@ pub(super) mod txin_utils { use super::Hash; const PEGIN_TX_BYTES: &str = "0200000001013fe9fcf1d5eae66a152efa45ad32baa5eed3cf11ab5e04edde650313b58ed8c90000004000ffffffff0201f80bb0038f482243202f0b2dcf88d9b4e7f930a48a3fcdc003af76b1f9d60e63010000000005f5c88c001976a914d7cc0ea6d5e53af78c7802101519cc100692668e88ac01f80bb0038f482243202f0b2dcf88d9b4e7f930a48a3fcdc003af76b1f9d60e63010000000000001874000000000000000002473044022048cf10f12a31cb0ec36ba3a6f79fad7e0dea3f1aa790a5aed02f8e8455c8cb1502201a2624089ce70c893dfd07a156ba91223edd5680cbd93d3336285ceefcb3dc1401210205914becd15ac5d2f72ad0aa42e84349c825a544d8c16e78ecc21534ef561fd4060800e1f5050000000020f80bb0038f482243202f0b2dcf88d9b4e7f930a48a3fcdc003af76b1f9d60e632006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f1600141ab7f5995cf0dfcb90cbb02b63397e5326eae6febe020000000113244fa59fcb407124038ff9121ed546f6dc217571cb366a50d3193f2c80298c0000000049483045022100d1e212715d2dcbc1c66d76f43d9f326f54ff339b565c68f046ed74040730433b02201d9ccbad57566100a06b4be47a4c777cbd7c99e0a08e17f7bf10458117426cd801feffffff0200e1f5050000000017a914774b87be1ef871d82a01edbb89a70bf4bb59310387a88c8b44000000001976a914b14b7395623921dbbce438f4fc1fc8f1a495affa88acf4010000b700000020a060086af92ac34dbbc8bd89bbbe03ef7e0016930f7fdc806ff15d163b5fda5e32105949c748222d3e1c5b6e0a4d47f8de45b25d63f145c4056682a7b15cc3da56a2815bffff7f20000000000300000003946c969d81a3b0ca473ab54c11fa665234d6ce1ad09e87a1dbc56eb6de4002b83fe9fcf1d5eae66a152efa45ad32baa5eed3cf11ab5e04edde650313b58ed8c9fccdc0d07eaf48f928fecfc07707b95769704d25f855529711ed6450cc9b3c95010b00000000"; - const ISSUANCE_TX_BYTES: &str ="020000000101975891c8466c3eed75f817e16867e4004786afe652c833db38a6d8ff37626ea10000008000fdffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000846378d77e3e759fd90a42a99cf4b95e8819db99d424ce3fd0352ec16ca9115a5093b64687ef20c5c3da2d2695c42aecd775d700d2ddb9a903a3eac8d661b503070040aba8ea89e00fbd0e14bcbeec94b883ac4fba0dafd5e795658bc1d184ad80c625809da1bff65d7958239c186b5dd8ef23606903bf1f5961893b3e3d7dd44902e4ea302f8fceaa7a53a51a24cfb9666f2d798224a5a84b60d26483a9da385362b87300616001455c29f7cf389e353362116c2ee374857c013129e0a3a07091f4e203f46aaa44d17824ba311243ed38a70fb9b393bde312d5b4460c208fcaab83caf46df2ee16c162094809fb9dce4b642224da210702be1b04deb94c70381d9c59df43aa717e026743f9c54c2048756eddb6d5992a56627102194852f9b160014feff6610a74105b33e4e8d2cac73f3d05c944d270bcdadac9b7bf23a96bdbb9f488bf7909f76d7ff997a5551a38778a7e7aa5f8b540889d6c664ee5d6a106dadb07a987843fc2fb965f356821a59db1dc1f7e1aa839f03fbd0226d70bba5a6fc866d4c980b677cd9112e98aaadfc671ff7e39e49a748ab160014b6d0f377bd2d2dc48ebb374b7107a15d4395ff8101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000000000251000065000000fd46104033a1497f00233f81f0687170526b203c1d6f34ef9c7d73bd2ccafc3d7de5fa4e69ccd7bf27571e7b49803282a8dd2fe98845efbfcd2d28de571a1d2c077228effcc0ce338457ab6f3d15ea7fbbdff8024ceefc72eb9345d272a3650b6ad1220a173cc7ad90c7b6d108bc5bf20e8613ee76b8f2280f55dccaf5e0adb7eb50753288693ea99417bc7588db0f4ff798a503885da8ab6ae3be8b628ba48c7a4c80a6067017705486e7c06ec509557f7d75e2e75db784a1293f847a5940e725d86a505f720ce43c27df97ea2fc05e003cc74c924fbe8513454ccf6fc54e73987a59f48297175e81fd8ccae89bcad6006501c6126bb94ae7bbea35f1e996107015eaf7bc4e046bbfff3d324afc1f8a764f91f0775aac6c328a9ee2c28c74673e62a0ed7ad630738329be8d586bda0e6224f42a9d7c4b4dbfe505ce513e9bd2d2f78a7c3977d74ffc5790dc50f913c80e8b1f23e6b236c790a9578e956b039ee8940bcb3f7d87c8da028dd60b078130c52d553d63ec02689a62c4d0f365dc53cf406b6ed3ee3eb60a280776452712635788f6583e875c598ab9ca6a96a2b68e99853abcbc1ba113bed2fbf05fb63c5fef34ab38e10f426309fc0891bbbadc44979409712a3ce7cf01a31985eeac0f4dbe00a57a20ef9f766025102b1428cd8adcfa63a7ffa25f89c2ba8e6b92cfe660135f1714179d455f014a9875005afa019a434d285927dae2e285bdf82721d65d2f095fdbe83cd861497e4875a69961cd44b6895485ce325c074f69ccb60c8831a7437ab08f9d6da84f40f65d5a63b409631d0a65eb5285c0f4837ff71f2a7391f51d10865731bcab4943bbfa3feec2433640329753a134a9035caeb9bca8287c5bd723252c8f6849b80e89f9355200de5548f081a36c5a1cc579be24b8b563aa4025c005cedfc1bb540fa8792749b971835edc741fbd211fb45ed6df5094714a8ba30f3895a8e2a4314d944d00791a19f4f811bd7d67a83df31614b43f13c59ef1d20e28d589a505a1acd37a4ff78e9af2d132063af9f5fd7d9fb3cd6bf9f3dc73cd4cb3479f7c36c23135d8c962fb531d16ed8fb4055151c6216cf53bbf1cf65ee63d9bf85bb74bfae527022619c4f0d9e8d3339bde33bb789bd25bee0241acd1527283f1cd58b7bec00b6f30931024e30c18469002e825bf6d10240b0163585cdff2845a077681996558fd63f1f7c373b54af1c2da61bb015ff70895e1072136d3b603d329c428871750858a70a222ad73560bc73e5ad24f541fc4a32f734a2dc722f24579b505158f39bee0cfc4c9890245c2defb368b6d6644a2579ff4a23082e686c94bcfc4097589efeda5810c363acd8290ee29ee76baecb178a228de614388b79283df07396195c4d2d3aadb7ac3f68cab163c8a1157e5ce4c3502cba9032918bc259434c4b12cbf77d08beb6cab6e19ac3c7d2d82a6b792a0c1de08540ada9c45dfab36b4c71419d54f25959d7c95e2c0301982caddd0fd0f9ac45dfb1cbf9a6cbb3111f7e5a9e8a29870c1029cc0d74df8f8a852dca511aeee22c471b09d8cf18eb85dddf9273c5024de0d0d617f3609d6e09a2d0f2951ae35d07f5f27cd47f217a939d56aae2c8ee2d95836bbdaa5c816082fe2bbb1f5c7a114e1b45ddd448c3ad19cdd7ed04a087ba00524ed8e16f295b4ec28f5ba5e0077d3d0ef30e718ce5a1d9c8430631baefcc9c26551d26f42ab64041129304eb4d76720d94adf9f2cece3627b2833335dce5f562ae2169ef44f12ca36a26d45195847b8eda8836177025bc66aa4f103d662ba1de526db5c7ad2899bd9e22245445022a156a2b47022853ca473588172efdcb8e61c8d20962a8c92d3a86e83050ed75d6a152915eb0e1051800b79d15b473899f4fbd3d47d6e7e72982d6c7adce00e51f0b1033bd1112091418ef979b4aa7f4c942b31ecc5bc9cbddeb3812906a172fbcfe085eafb901140710cea913779510648a79fab4917f96efc5e7f28b0fd2c813805760d657a089a015f6368f2b471539c8ca2b33c4800a6a201a21daf09a194e1962196716e0a645c72dd1168dead6b64b0ef16ef4b73dcb2b56d517c082f218db141059cc7d52d5e5e0832f13030be8e3b36605f9f9749fbb1b1b4805a5720763ae406b32498f1aab809088489a6b71e2274ec26c4964f2e3bb32063013868a030af901e8afb5f563e3a690726b7afd3761ab9cb3abaa4849355d0183dfc66d8e8bace12e739c9a9ae1efa0d9d8f58df3352e7f8c1e24e2a0f71c861ec09428bbe837145ec1b50426c24725bdd025da913b689ede4c8d57c8cda6a48a5b246b0ae57e4dce9cd2cfd0604b217f806650ac957aa01aa9f37e601bf1fbe36e0b3359a4b7477fb4e59c48481797694b5ef3b51ccb7a69910617988bf1b5dae18ff0686d722fcef097d55ec74932643a285242243a96288525dbaab523d55ab28aa0b6df74c4bb2d90042c952c5412d379357c81fa1618e3cbc855132e0e63242fcc2cdcd1d96663ab8f112424f7deb4aa6a01976d4d8c9e82303b3323bf1a77a9392b8141aba33df3a02f3423d50136a65aafa4b993a36ebe4e1ade2f3c8cb70bdd35b4787180cfe14ce82e81f60aea09c052f74a41045eee52844cb50e257dad50ca3e8f3631f82366252cd4dfef33e6106edee6764d25d0a0cf8c9d8dc0b126064d2efec032890724e40ceb350a4d5df729bb22a878fce0b4e698b16c251db70a49f1275ed28a7e678f2ffad0047a118d7f8202f417ba2436b285cbbd0f1c2710f08d73b31e54ad9e1164c6fc1ba1a5c9666b001f5d8b7d03e97a48d338890b41b2968c65859e18907a08e7f96da368f2143f63d09c0948e6e7a3b41210fe7ab7a48668339d0f5e4d4de1b8c0cef095b228dcaaa0ef8c6fc1d8d65cda7e9e81137aa8d0595b53c6bd9f831debee20784e790af3676039b7f4486e28ea28edb66dfdcaaa62f286099bbb62066023278de78030c30a7bc33f5ee2d2fee4705eeebf1768c7ee1b15dca01eb82f818589ba2362feee455c568f38c306d964987e29cd50408c73d171f93c4bb1e4937bbd786d567bd3dd488a440cf95fcc1a54d6e0b35173421daa01512f970e89738251fb75701c5c849720290e49f124fe3c4e2885b23b055b1cf6b7ea10a07614e9f2eba7bc953158e11130167fd2081a09381329449e2f17fe7403db46b0ce380fe846cda85e4c085fc0dfd8a86d4aaa4fd6c1b2804b13c70a4a858180ff1450a4c11aa576327f78e74c3dccbb27d178ef09575c4879e84d49b41caa3b3dd282f1b464d0df4bc01565123ead10bdb9273544b058032807482774fd9e705187bc6a4927dc29a23a8d6e5d7c7b54432546d5a2f341b2d90ab6866d9177f7f8d601083a53444b16b1e9ef4ff24954f4a3bc5fa25af61ce9a5c1ee2e507eb4f4022b4c0bea888491b1201477eeae5ae432a262b8c9da98283b895f484dd911a7c503a258e9b4ea01f07ec66fce68be4372d87b304d7840362064b7443ec227df0d25ad6e695f800be0bff81cb68d8f05c89318561209f6e36952cc30c0a2f3b66855b63d388d55a44013096f2bf8941804cb202685deb23ad0fdb92de3a780051fe5f644483db93c95fbff0a30e0d1b1a476f1161953835665c761b7190b1520b5880896e0c51ae6e3090fab2554ad56dcf0d8311b0256a5a4561460b01741e843d4b0c26abfbabc519dc2141d3aacb6ae7713cf2c95803904ace06f667d114193e655e1ff0ce724441cedfd21ca3268cd2f586eeb150c08c55540e3c621e77986c4fd07d5a586a787fdb976a1549d982c2620dbaff166949c99f5d52664fb350025976d1f78d00994442df6d552cbc567f0d9d7b4d391876bbcc3b9e61f2f764b2464066f3a491042f7376ce4f28e63e6d5d395e5ef6c529d6eb3a792ffd5ea1016fbae24188d96bac0a9c40a3c024a4432a27145d72d0f9c91f373ad0fafb7c4503d3668ebf8c090a805a98d272c6f24ca0f8c98f03f3c9d33f38c92200d4e9a3d8fe2034c195779c2fb261e57b1a067f177af54f2ad7fc49154582a70f7da33500a849212717756f06e53bdcecd41b75faf63afcad04118a3ef956d72643b7d9737107354a3dd3b24f456aa4ab45f00b4a8d2a7617a9bc9dc2afca3c2e478ebde4634a73d1b6b61c99a14bd4b962bb3e3fdd9a00ab928e1a8fc1dcdd0a4ac463ba693ecd9d1ba40199c8f423ecdce7f05f928f5aab1baebbf3c0fa17b114aa12bb3c6ebc7da0b113e3e2886dc092b787a738fa1906c63c815c2ae6d51e97bd44fed8f1b83f5c4f9141971e6947c88490f67637c03cd7e354f74fab3494f40c869a57f67b6525501cf19ee9ae93430b29cc680c8b31dc8e8950a06c43e821ab86f16d275a219e49f9cf94016b7c08c8ceb161c52e8bca8f8044669a96c160c1e38c7e65afa862264ec168dc6d4a6a9d70625341ea83daeb15b93fd20d449157271e01cd591f4ddb9ad8b4bed1e1402cf9ca6964653288c78d673803df2505c680c840ea6fb7411f1cf229184f1331b8c8ed82d1ec7d35a8c4de9fa04b6283eb67fc4a164c122506921e49f9b6726b97168b53b3dac93f302e79dd260723dd077716083060cf2e363361fbba6d8b86723c64382d59fe20920e9d5aa133021a6709901b535c63c4795845c78a7e647e39340dc6b74d3e7341c49bd8af57c9abb2261a328f33122026043e4f32b7ebecca9d9250f186e81906143f409ef36c8865577e797e7d57381a0fe633df73b59544c16ed8b8f852f3e033da0ade4412a4d1ce584b47abf8ac4ac604a90a0f316b621c87ef7200bc10df3221533e1e54a6bde13c549c95f8060b4149d351ee10e33217470125bbb4bf327bfd5d151458dc249aa4b57f14979fd53b09ec2d01b9358739fbe44e7ba4c54a9e6bd483801605cd33dfa84e7dee0bfc30d4b4e2b0c45355e13a92f9c588725829de2e9efc0adfd807c3f8a32b623548785d07d3520f0933e5d5a280efc6724e03dad47013bf54338ef4956c9be1b09681429f9cda18a11df04ac0ed66f28d2f3722aec5fc0f169055e9f5357d15fa6d53edc35dcc5dde72ca418c618994a5e8cac6c2a115e87b27d2d3af81654c23d476f49b606144814cb36ca892b0e0d4673a2091c4f948079eca42f66e000432255da0c99cae1542acf0836398e067b8d13b9e1e9a149bdda367bd65f88af269078ac8cdf79a2b303287ceaac470b050b6884a44c86ccf413e50fecedffd864c16b18bf3c59cc9c0e2c3a84a37f7dc1bb53729f306f40827d30eef367ad5217a5c5f987da8003b08d55b90014cd828fed7beb4c4afe6bca378aae72f8c4c3eeabe1c1b7edb44f1ce8abce27c16154ea58015fcf9ca99200005729abd67473540c4c9f20d1d0875697dcf01b2cfe61aa729a9f90475bbb720e413794df4079bf3199ab1a071ee448dbef39a9d1e2c7462cb6a56d558389244b4542f913325dcc0c2517dfaea6cb67a1e5ee8e9e7f4b4b343b33588c28f6cf82be5aca0c0b51dc7c461dd05ccb9766814a17d0e491a61b87bf134002441fc1c4f3b060df87b065f75f30121545755397015f0c4b8c7cf855defc20af97349281147ce557e40d292b36950d58692162dac1993c0ca4fa61fafdc1baa39e50302666546a8c3f74c2e0b53b3f0b9307dae1834a284aa6a2f811af925037a7a69a0585689d31777cccabb18b7e3f5fb5644339fd998d3a3886e3e1bd5f85fcc39f8e5d14005292c275076636179c93c8b2593661f7ece42551ccc1e43fd78a0331001b7b3b9a7dcdf40ff398a43e07be40ec2946f994fe8647f854b86f8c4fd735dea6cdbe7ee962fe76b788f3405392e747293043c0938545f1d78c00c1cb84991752936fc6f0a72e861f032253c6ba214fd46104033d2543400eebc89cff38ea74f8b07f174b6917951751737e2a917818fffe7172adda4808ad17522f3d76e16489d4b4098b049353c053a7eba7fc13f9129fe41c6c2301782bf9a1ad16f22b7308e97ed7a2f29f5f948a13be4117dcbdf069210d93d508fd03bbf6430f4f788a097f573a34f430304d3518c5e2b1669f23a3d7597a502da41a2f92aaef8922d8603cc0455691cc59d28a004ad2f9385253874c2073d0e2575c48a8c8ed8a273bc2de878e217be56d318821c4205c784b838c6b030ce821246ac7b8876376ac0131735b66d616bf6c89b1f183ba62d53913be941462d67adadbc9f546be9099e895b839875952e2845bf468326b8802d44931d7fa81a1b7a048da7065538c6700c68213f16060bdf5c0ee5cf25066e0a739f0816d551d404e00b632a1f76b254ff8e93e24f45e6ac9f7c62486cdab28131fc9f77aa440295460b38379d347718daa871929743a8b5f19237b6e58c1cfa5645559fe31b99ae317de28d8b7fb5e32aa9f163a8479635e5ff564280ca7dae3ae355fb2319c624f01e6ff1a3c5f6e98dcf5d58c943a49b49e2d10c6b6d7cf355397bcdf3cf9be84e7eb3353f7bfea193f1b6b22309838b081fe37609daf49ac30a8d0929c189ef58148d92cdc584bf9ae21b13e7998d7258811a22738f4cdb7de66967411dffbaeff7947ed330a20f9de0ff91cd2d282153fce0d29637c7c064416611b13ac77585d03cc8862b8a93ddf03c1efc52b49417ac9571312f6d216f74815800ed6827bcec56b0ee6f55ec8e4b8737ddcab9a8e966b7a64fc1adc3eb04e8aeb44ab5633f12f57dbad58d121997621c3fd3a750d78f4a9e581a0c468eed32f8e2d340c76f5604c2e4a9787a153c51588d518ae7f5f3b4d7b072b4a953c594a261870dfeecec4c3865f98a890312f3e6d0ebbceae695f415ae93e18eacb14616f40d69bde463bc2190bd6a73174c26f838b49ac8286544b2a31d8dadd6e8164b823436490bc1d6790de250e7b80de847a856a5bfcacd44c172672d52fb2c8e6e04972596105d3372ab4e9d68845843d457d4d54b21fe2f4e4d117f5fa2ec13d671d9cefaf00fc5d61c3e9b5b565ef84afa535008115f8e6d8b8fb8ad2744a337b967054c6c45da0a6f820bb02f07d51b7ff10820421db70ef08c24d9d913deca3ddd1aa2659cd1d349a9606b35de2735e058ce86d0fd2ccf147867aa9f9c236bb32d82c4bec7d8e77a159d5057650ebb161ef1940d8e64fe4b6fad3bff7621848d3e8401cd5ab25a910352aea58e06c4549d2b7907011e02b1ba94774a4a66b24466f641d376976db27bd23ce682fc4341f5d449d13b9d8fc2f9a67db302083a53bfc79ba2e607cede1c2cf95d1e529262cf48908c889944c4db1b5e8555d86effacb882d148993a1fe91f567b14b43424515aa75852a50f73fbc5751e11c2467e695bb3ccc95f77e11ee44d7c57927a255caf5a5d7365af8af604a097097855d4627b44855549566e00fb530f52e15c1365ba86aa7d60f1ef9929b2fd0ce8526bb7adbeadbba9ed7e9ee60a11cd3952b2b142b3c348d5fb7c1e9a1a7cfc8c75937563097ff18bcfc00cf7bafce608a7caef0f18210ea76d30be1c866697771de2027f4c5dcc15e68342c7b413438939db39677838867379bf6c85c684dc72d00e4a7e15ae21ab863f0901309a673d85563feccc8a5e489bd7ee96ff697f7308a97ca31d71e8db533feb347881d7891f9e82b15a2a0fd725e523b41d67fba6292d177599c7ef1f0860f341d7002dc4566c72e3862abe80711acacfcf8162fd3d6485063a53d73acd54588aca581f1cd846565b0216e871cb65fa6d77cfb46445dd843df7c7ae759951fefaeef9a4301223e296cdf5ec4a6730580a51ce69ae044abe974bad7273534f4b2f668a3a7c978508fe9b9058668e56f6dac062e78205229a831feb67327c12e1a0166e34dba6cef41a72af91ee00c270f1b1abd6aa80116d2f76e6861aae614e7d2a70bc0045dd5005acea24baecd3be3cb6b5fd4dc8e15e396568842ab82e638edc93438050fd7966217c5a523942d2f6bd52a9f414493748c0c26069b30c8b2ee1d2ed5748bd2b79071ec7c53789096ba2e78dc19d4a904bb73e85c18cba02139c429a6f634ca6688034655deacfc4881fe2ab2f9b2d192102ac72084a33cd1525f4e99e52e18e9a7a70fd139e6ffdbd18e9f9f69bd88ca6c51b5f8c164704cd5d5fe9ffd1da9f66ff0b956953f5443cf3b2673718b6e159961e8fd39ff5de5a5bccf92dbb5fc61f58a7e6ca4e5377771ba062faa584965e136af0395ceab6ee5a92b97ddc6935fb14e77f63226a473e7026f62956262d21328fd6a3540415efb91513938c4dd48fa8fd39aa5752591aeecf183c49a3802dbeec2b6acfe2f2b541dbd0957899eac632d053ead0bd0dd743a2f768afbb8e2498763544c6b84972a0a5c809c68db911228f4a5c7bee0ffe26f841e0357117940117299ada73759495898253da36357e0233ef46f36bbde01a0829acb0134653e653aa714708cea625d2b664818a34e96a90fc2c26b6891654ee3afa0156b18fbf336d28bc6f29c8075ac0327f20a9b263a6c821d292de100cb211678874f29aa7c94fe9aa33158d6408e370b23b8b24b5d952fc75a1f3d45624d11d07a262d09aedf64fec483c1663ffe6efbf45afb69b5cff2cbf373ea078cc268965c8754247042acda43d49ab34ac9d8d55023a76d42ae858731cceab3c324a935df0f226b1f4bd1b15843ad68ceaf81a630871d57663dfa60bda287e88a3e36906895213edc1a387c3306d139555679e40abe5ec9bb7af1e1eca841d1e58dbb84282aa4f79f2c7e27a5c8da392930be19ed9bc14abb2b032eca39f6a6b4dd63eea4917d8e3f10c1a24f02ee43b5e61bcda2351f5a9525e50e99244947c8a8b7e36b0ef6a9b5c81d7a463aa8f899f91286e59b5accfde4d60fa0dffdaeea610ebf420042210ea39e841c9369761633a2d91ecd2db4d9b5f6ba3c0234acf6482133ae1dedec1311f66e9b2c596ea7348d2a97f69d3c8f6ed24791a7099e7ca434561cac27bc923398a3791509002ca65e90a363cd24ca901171073fde0ca56cb5a3c30d79730f001f6742fc60d65fe92a9c0ac21c7316b048c8e912a8a15f1258c94b6f70b964e7761e5d66e3a197e36b1d9e7cf0199282cfd8ab6fb08fb6fc416bb5fdc27e90df8f19da219600b3c534eaf857dca7d81ba0e6d39fe200b79580a4d50d7049647c95293bf7da983507844bb0ae18970354124834f2a84fc7a3a6c0d73528465bde9f919ab23e7557e023c9576efd95cb547c0f8eaedbdf889c10c2fb006b1c283c26f7103189a0a7411f5ba4ac1bc1adb8a481c82c470af023cd0198258c02abe70f6a7d3d88ad1db0140f553855fd3ce0c20a1ba30cd2dc66fae3b1a2374f6b70a72b84224e03d3a7d368bd265009a94f49a0e9d4fe9008b2183bb51e3ea4b2ec2eac830f617606403226a88e4ca85ba83f634d5953b3507eb6c9c0085d13cd6deb27beab2e18f0568a506da251d4a2eb0e4e57eb3aa28ae0831ee55bd17814b0231e0d6642540e73f8e56f016f1e9465123f27ed192b1e6fcb84f8cf03dfc0cf107fe3bf68700173c614f5ec79c8a513fb332df753e14d9de20514002dc34d845d78c1d9facef86f9e19717d505b2d00aaf90da2ed5a60d032ea41b893208a838633f084e940bd228a96e6c4af51d320070e096541551149b3a764407ad92b9331eb82ee66e26e49da13e1d433483b5c4edf3fdfdb780f7b76a7bcb467fcdb5db6a9b0e63eb390c780a850c2c9f1d3683ef9ca949c03c6851f04602ace98ae57abcc2d99f330a369c4bd2b4adb13f70d0f421a8e386b572f33d16e70f46891f33c9f978280dcaf5b28a7e98b5022ff33517fc1ebe69fe7846f48fcb3d8c9c3790787d740c4cb42369f52b18c420f76c0046eb3da8b4999b28d6be977a33502608937036a0a770feecba941c08703eb5c3c6adc91e7c7cfb7475e3bff85075aa41f39dcb2813675e13516c0e2c13a5cc1ed685e00f7d8ebb6f0fd5a2d174df43f7d0601f39694df34d7287dbfef1de686277d6d0506c59f73a4792a15b49f4916f0b051f9da8a14ece65871f2dc653b7d5902ee0ba771985d9f0432db9fc22bf468567f4c2e19ac23a2692ad8e155d433f53aec7294c01c9cf7172715391b2c0b53986c6e06f77959780dc0ee0c4b6f2f6fa1753d8582f2bb14f260f34f33df6ddee30c8dc823f7be6c40d0df760590a9445f7ab0cf5d5a3f33e0967bb023e205f7737d390d0ed6c52d22726aded20d3d31928810463bd511b1ee3f6ae11337712fdb8a8869337b4b8819098b8c1b725127c8bc48646634f31ce0487bde029e6960dca8d27d67473a9308a47d1ef35519ac9fa56eb93d840bc5fbdef88f6017c38849fbec727ba4da247a3c560a08776efa9a3743ddd18e9a535d60d74aeb5c0092aa15728cae216fe4eddfc4b99024732eff574d7eec2199a2bdcf9843d61a01bbddcc283e9fd9283ed91e92af964d84e5102470ee5061f66d132602158481362fea94ab9c4dd19c1287d8a38d8722d6f9f5f0a0573bf0f623cc5f2992f881e5101e436b2724b1d671152fbfb38caaf67cf25d5c304cf72b14da11a15c47aa008969a616e67643f5491605971c9e3e2a4f179b7e59df1c0f031903d486e0fafe65e69a2f5c934bc8badad20619c13a1eb9214e989d3a34c562d654a5235fe87469f2400920fff5e0e4c8e5b3e3fa928289ad378bb674e03e9249b7632253278c9fa1a240a2a2e33efa823e538ad267e980fb453037d5e109184515d0486fd2e8474c8219d093f2ce316f5af0e04a8c63005d5f7dd86c152d77557c38c1ae1d168ea06f1d9514091072a917d966ea0d931a559208221ce4b0fac75980b87948f1ad561570cccd0daf0828cacddac0805ab0e5a13df399b61a5e9de6c49adf65dcbe20c2f81ebd4fa4039e80d0727a14e6e0b0d3b800c47ffe37468d21e84e3977dcd64d3cb41cc30007d0cc574f1d3e600acc0c9b8e1f9095e4889ba5905da0af2d97cb68f0c1143cdb3db961d11ddd6971812cc9204f801a7ff5a448c0c9e86a122026d4ea74d0b2def69b056757f7d7910981927ab0cdb3820edb735c170e7379146edde11bbb4964e27aaa72b9c977431a540784df74d9cc601d2a447ab6837d3798fb49497d5931b74aba2a563928daf05c4b95a4a1cf25c38e8a6d7e17bf36e0c621b9351bc8e009cb848c827b4526f0edfbdeadb3d734f78f00fc03570b83f7d5840fbaf905af3b54313055d95e4d697f53e9b61479eb8c428f87a10423fc862266cce54350e81a94cadab7d2f70c67225363a8e63cd002c92b769c4fd30737d444a3c1e897e367ee423b74841bfe50360a49efd0c256731975d9a38d04fa57454a25520ea4f56c1ed65683e6d012b8c8ebc3237c56c54556b3f8ca63f5c766364ed4794a2b15e8b70cdf67d552ed4fd8ef0ac159e3354efb440f323da3d1595707c49924dc0dcb06a9128a7e0524a5fd6b6510dfe539bea327f9745aef775da6ee55455505dd8ac5a2959ed411fda0daea56d25dbc9c7d3a67761cc3ea2e6c65e600e4db7890bc43b4cee6db84e3bdbe9b4c2d7c1e0af8abbb62f8fafacec372a5a30fc935a73904dba6a6e29e5582b1089af5cfe5e98a8591d2a282c4c5c8a52f2701e16278e659c00160ee1d0863905991dac6b290ea877cdf531db4fcb8fd7a2b0826e57601d65f9b4a895ba5869e45678e66dec8654049ab9743ddd9341138eaaf98713b4006b283134ec2a6cf4b12c624a4c620ac77561cead9a5c11a7d5b4b0ae3f92f72a4f3baf13eb4253498d6df7128cf935b075bec291800247304402200279ca59f49ab840062e5d5d3e1448ebbd381ca68ce2c0ce3cf0cdeb5357302302204c6b09eaafad7a019fdace6cab0335cfa1114f026b8ab91a9d11c8888b4e99b201210356f8a8c1cefde4ccee408bcfdf092f7f811da92e4f99d99231b3a5d8c58a490a00830300072589ea79c4dd0b6d3b2aa0c1ac0768a29a6b7a2e78127b2d8ca9c039506b28a3457584b47a73efb3375fe373e21e8395d4c1783d30193e571057dfc6ad94d7eeff5085548b88a7155b56e33b8fe545281765a07217094da53d1688992b1a98396a0451494e974da12ea68ad1a0a372ae160eae0f425ae227ecaa27aba4547f73fd4e10603300000000000000016fb32b0047f0087d30de68fc697678c7f4a95254a2004925e057185ff09d3b7f60b0d7f9af606b1846239a35843c6e6e221eaa9aefc0ff8b33a1b9a10855a59136d84c20cf675d6cffbb62999e452037baa143650e18e81733205817a8420440eea5490608b149366eeb4d1790e9199a04a39d0cae86ad4c5e250fac55f6953a489484938dc7521351a9c1775a658ead3404618ea8f724df382e94358c7ef3e669d9affbd0a2d6b79a6740464940eeba629a7bf7950a049366755b88dd9b2856a937ecb30b063238bf7e694ca78bf56e6e967bb820b340945b34c5d1bc55a60e5905d8c6ece19fab342cb977397a38d983b387ba6f1c846247b81e0d34e1589dc89617fb315eff0f4841898d28bd324db24056b6d5311d41bdff31ff881ac3b08d690a67481ee7bf77dc8f220d33dc1497b080b13a5c8660dd63951a76a1835bbf63505ca76d3d3c15ab4415097787b0571c0967db45556322a54aa0a9b8c5eb9007d92924c193796a3fed9debd71f67c6781a39e198ab227b7a060821dafc703f2abed6aae8103742128f087303107e1ddcedd96acf844b1b26cd795d13ef7cf0f184bc8cebaea710fc0bc0f258500df2c82249975e83a4f53dd74f4d1f692cfc49e5ff3ed5563110079df9f073db12fef044ba5fd1d76d4ed90f3c9f1901e4e57c63e6296c9f7aa36ea95941a6b01d7de20048714bb68964077e520f2d557246b92f3d567ffd4685187709e44d3f66a96cd05bec08f8667d6e29109a8240877b42031e56f213524ff6d881385da86e85765d20b2ad132ae191b5e8bec738df5c533c20fbcfbbeab8f0b5aebccaaab1a6df234a05bca892c048367f07e1747d4deef8536c19d2bf0c4574106bbc4bdedb30606f35559ddfff29ac65517477f69b75f6e6bfbb878129b684d25c3634e907cdc04afa579a815994ef6838348e155ef1dce5c4eae478bc2954e2b45ce5d63cdee731f398b98a8628f317fa1bb7602ce13506d84fc8e8852315df692bd0b6545a8d7884930a06d23be3d31b72878045d83018e7e6cdd437bc241dca6b53f4c9635eebe5bbc610da87d68d91eaab2c82f216049a49dc5e087fdea5c9f7890e05fb20f0f93ce8a008874d25bf377e52a346277069097046d27f8929c320ae1934cc5707eb0146fb73f98af7de49a234a17f371c14f778ba90308b7ace650cd667324b42ca790599d02579a38b64d5840aac48d0fe5b56a77e1fa6d034624da585a766a685c9fe2d1a4dc65d64588ee30d950643224acc1744b46a13687078d2354d68771825acf6f57317959b0c296af72499b1da75054f076135a59126d974ad6ef25764d122d35bd96ac0c4fe9cc47631eda9027bee307a74997b9bbe07a2da7c29b69de863149130be9deb5b9784cf603d0fea396fc5556a36b36a5b6951da1c289e6fd595943d258c8bc58541fdded69e7d97eb90eeab944804cb4262dd87fed48ee071c2fbf5a3df015d0fbe54a80dbc7d4d350aed97e91e2f000abfb8002feac203ec5ffcaa7033ba9b1a82a4edb37706db47199e3855f993ab02b6c8501e9a1215565bfd0194655fd98edd33282c7150a2f9369e46e31724c12b870223b0c9678f04a5f951860018ac96928b5d9c18ff1e06205e27e9bb15576a1a651ead62b7b8ab11057b352e1385c60478c369e34c320fa1b36e856a1f302f1da053d4b220fcbed9be7642319024bc712f082a5517d9f6dfa0c2d85af3d8660cd06e71e9de67053cda59926d9e0845d30939e7aecab1dd26f186239b8392500821d4c8dec5a8d0123cea75354a7dfbaa5ebcc9d75c9a307d633e4aae4ecca0a6216c1509dca5b052644dba73a0244e29f5e95ad2c09d313f607ca6ef424fb74481d4427ecd9b04afd563f46136cf2c6bc596a13ada2564c52450dd9b451da52cc1d82002ee68acb1caa38cdc2fd629e10264e908645eb859173b698918ca8dfa2e3a9350fc0545cae228890316c82f491c281d15bdecf2bcde89e9ac435239eaea24a8cddd08aac147633e822f2c57aae5f59ae901042cc15b045a58d78099da9031528e8182ee93433e4183c969cdef637d421c0fea042c1da85f5f338ea9be23c84f0f53456a695ae5db2d54d2e70e5579a6b81d1d3f5e63de0c746b0361d713d2d1e83118eacc7032bbd31c2ea26bb5d55fc47d7b56033a68c567cbfcf8856a7cfe0d37c59f660ba3226d409205a73b5a7b86a1bd6384e93e1486c33540f2e2e29551294be97204cd85f29d38ff11fb1d3dd34edb1bf7eb9e05c5b20318dab896a5134dadcdd197c74f94c0110a8349bf29fe246a0fd54063603de7a67333ea3830779a8d9bb6f41a82ede66103859a25c9116193c95d4e6a9029bf3e301f1b77c5f31d7c78ba43f34ea027ecd7063919ca1ae583d135c008edba60b020804571e33b9ada9b9072b339400d356a54fa3f4000ab7cbb8337400d2d9f130755d87a8b62a91e1fe95a0999f24b56d89a5ad291b0ee96aecc43b08aade1223cf1cc231bd2a18aa175dfc7064692bde34b0f3b6c55e2d27cbf58554484649f1dc52db19bdf284ba1dea6802a0808d87584a3ed0902988b5d1761234ae091ffd917385ffd419224d29f0d8393acfece765eeebc62406edc65c54ccc38bd118f7203e5bb12d2dd21024b34984731f2fccef7d9e292d18c6651d30b7fe47dec4c643c252c2186bc0ddb095c4f96a7ef7d7d73b0e3a55feab61ac088e03fc5d79b0cd44f538c0750aa592b4f9ec5f2f35446c6c4263c292a3ee1486ef7665569d36eaa9bf52b5ebf394facff338923e5f2a802272cf866a59d325f6893c913c55fa215533add8cedf52a1d79098e3c29d4a54929e762d6eb9f23d7dbb69187ba5c11f441575e603858f6729895ce534ac0261f5f88eefc4e3145a93b960030dfa4a5a8a5e287e932ed2375fb29b66d6b563432692d052a43f6caec6c9be33b6cad83e354225a232d056a614031923f67a0322d9c261a4a730a1427e0537749cf4fe20844674b456eee877070aeb10dc5d424f4f1b3d0fe42ef68ec48ed4ab6b2bd4eb52fa868d510badd443a7b22f5c370864fbf279774d26c6314903c2b3517276477db9a4db8f6514b38eb5e2bd5153b73288a0bdf17bafef3a9067216ce5939f5eedaa3dfe8f3a9a0f10b0933af5b37da4542e1323c22e00fcb9ca2f12b6a644cb6709cf491abfc86f8e842ca568de096e36714e2e043f10b039c62822714fdc65818293c44f03c984546f6c20177f212dd8485c20d07f809748a9f93b666daa2999f044016065afe45a9a8e865379489c1e7daeff03ffdb0b9cda86d913e764676112320cedf6455c2c9aea0df7b90c6462001f4cf02e4dba3afc85f283ea1fd69a8a73a9c85ff54ef7e12515f531a9ed0cfb10fa747cc4131737cd106ecfeda49ca336f9ba8636769f41922dc277b5ae95b01bbc15c36c2d93301e4d87279723c6ac79800f6a9a0533e99b3ce272694d4c6df941389bedf36fc7c8aee32ff954705f96b64ee524b7178af060d8113340b142325581839bf29d02144bbabf4cfaa1f6e178412098e2c175b5132dca9288ea1ceeb4517d78883e432e0cede03a59a6260abb55a1a2410b74f1adcb9730d377448cf8fb57d5c00de0b4000c54fe7ff516694e46e6c2b9b4efd5ad11862f368d29b05d0ad9a8b7891715259f00643060d5b348bd5c355fe294971590ff4c03d1513d4cc91fe5bc32b9531560ca50292cb340a1a3a5d7d5b6591a4e6db94b4807b4d33772304ef838912a142206491d7f3ef8c8a2932ef3c04ca2fc19e726a10ed8dcfcd0a2ec81a049afd6aa24ed7a0df80c809a6f5963e93da0e231b748d0099e12c2ce8e0754eb3d006cfcc381ee495234aec6568c978d7e8f44d6644c4638ce425ed3800c3f445ec7fa85adcb23cf78a18fbb5deaa20b47c12896377d032a67056ea192b187ec4a44c16cd7b728941a087a45f9915668a7ce2f120aff50660d66c8477591a4d2f9a79f39f40401342a5aae0ff8253ac62c9d46b09b458f20069f9170c1aa3ccaee649a3fb37f7de2cec2b7c82a07bdc707580a3371b2387c5e110eb9c945d7013cadd7580f3b0590777e7af620b7e0bbe389b70eee42cde184413cd3530083f612069796c9fb81fbcfcd69de104e8de56afb2fb4998b94100b53aad94f2cfd6bfa3f7d0158d549907c9a07b45a22b1a4a190bfd4c9b67132dfe5e5e5a00d37ddd80f96c1506da71baa95082c2b85e0d354ebff3a329868bc5840b17cb8fc4ddcbfd195e447c17d76632dfaaf6b02c5981f88572791f32810c8272408ca9c12ab24952fa50c5efe817fb21d204d59eeaa8d8282f787a92d1cd27ddc7abf8ccb530dd51f9337fcbef197823ef11a23c76e121079bdfa523eb6cfe78e2e8cec31846c5920dcf6700bb3875b0c9fbac31a6e6a1551205e539d39e0f333197131340d9d74c5b343450f4e0dc756d3dadf1b2d25c1d60f244ce0923682b96de22be8e1fbd6e762cb89bda68c597baf8c6c814f760050381041ddcde5f52dac92dfb3b229491712cb1ac7365dbd1bb62f91b6d124ba65fc8d83b532c8d98d96a2e131b8fb943335cf83a2de63e17888557be979c05c6f05550a49abe1a9b00cb77b756dd16a8edae167f38ae784f816c6d8b05edb9db093e5359963a927370b78f14bca2f472a2974b10a37ef0a78cb78754dcb5ec8f59d5e2f33523aab33abbeaea56a5c688e498fee42d202b0c1f08332077ff5360c189c5d30c4384cc8776bf30a67636c711a1396d7f139c57b2cc365d156898979f9a38a2c02b601ca5448e7a7c2981df170219f5a4550ded4ab499f4ae1e47da52b6c1ea441466b6824cde0e2a53bfd6fc636274760b102edf719473eeb4b68e70097d80e739f8f2e1e284228f731db44d327a52efe258f78795b2257dc3478e0a1732e741c0f68074a5520ce4cfaebcee260538bf4954e669cd17887bb25929330b4b2ceb750d64da0cd6ffa5171c4ffc104ceae609fe777b99908e4f46169144c07c5250fc4fdb051ba5335297357039e5957e77b177b974892b19e27f4b7a5e2522429451f84a2b5b2931fb4d5a47319e1df8434caf337784e6d33783a634d9d4a4ac27bdd96ab4684dbf29d0612a1419ce54db6e4af8209ca7da98b18a1eedfa2473653ffdfbf1472229fcf4c528a99995d48003c85511c87957a862b5308b9d2977ecc82b03ea381336fcae95e9c5ffb60b0b487f4f1c212f7f11265d928d095a16a638faa646ef3969c7cfc2d082ddeea9facd508f2773787508e45a969972fe54576120a834b2d553659efd9d9f605aee04c2a36c12b82a13a97fdc2af4211c87585b0378ec2c0cabd769479bf653bedd83c6796ad4c878114dae20affa06fa66daf9cc0e658dbe87cf51637bdb332e6e231ec30b371297d0c7a73d128f4f909175613243973298cb852b008e117ba7d41e71cdd1e24c3d4f6675a26ac8ae67fd8e7e5945853c11a153842196cebf9c791370556e5f3a72deb5685af7aa52ded66bb4760f3bcee98d1a1750426151d7346a9eff2dddb9b7b3736ac0bd0a590e7af4b25ed7fc3b52a5b52c915a3584e1b2383cdbfd8138dd545abc2734864fce88ef2cf12cabe5eea478095ffcc10fc512adeef7d9e8e27d26adef3e48de380e5b851d2b9cc1a8b2f08081308383f71cc61166efe9c9a139a662b94154ae757cc37225521d5f29d27347606fd2698ab7ff3f2497ef1c8efec0ad281f0932f36a8518ab40b946746c5661aeca2ac446064a7c823b5791f2687fbcca8fd2ce95e4994de25a8fba056ac2d5bb8e5a6f5b312c0893c8a7bcae58dcc967f3917dc3c97b3b7ff9653fdf087ed66b847731656e56f83829b0f20ae395d6b4a7587dc796603620553f7dca83030007025608a8da0159d01ce5cc0f7c8f453c62ef23ba5cac9e87d63d854abe7282c506e5eb6dd22d8168ed895e19432694f3f19f0ecce079a1cf1daa0b7e17a193c90d4fb0a155818e1d6fcfa08ed64e17bfb2573a3fb74a4a1b9210d3bd1034f7ba01079fe75b5448b77807a19f418cc560290c9f9ba4b670a9941d5134ece76c9afd4e10603300000000000000018a6bce01807c9bdd76cc5033fcdc479c5044a055e47db9d282b018dfa90f76d01b9fb00e4081f09465fa45160873ca33bdda257bfb2337278edcd9f6abaa88994ff9a932176975d5f971b6a699ecb55231d2e90f7f8a4b1c29fc8062c17de71f5e6d2489be1dd5b8ed07cbd8f5bdacebfe98569f88510cf266dc9fb9139d654f03efecc270ec323ae588076a64de344f1eb1def577664ba6dd07b1856b68deb5b32d4335e25ea784441dc70039e8fed8e56d9ccfc79c79614a79639f27ffa9497abede452de0cae56414662e511f0c01a938e99e88f5e0a7bce74530b4cfdd5b2b4994941cd7d83739f223ec0f18ed192a7d4730725b2c123682af22b9a1f0aee048fa8610fd813705757070838f826c37911f60a2f132f00910fea698e1f0ed735d7c75772c40a76d0af7f349df5974f95147fe8ea1cea847557889efdc3e836abaa04da71be14cba79ec7ed106c35622312994638863c263482e8291ec65a0d9e5c194c1780781c782684fa3f153823013df06f44ab05e0ea00f396053e3dd784819bd0a616eeb6e4ac8a11a5cab14ef40151fee4d7459dec2caa90e22dd2e2e01c082ababf8024a6553bf0e6d085a65948ab12598ffb1ac59c1df05df073fbd015337355db33701847c803318dd348356a657ed1a50344bada887b2559a6ac186883f5cfb2c28239ba699f9747a967b03e8866dabb72cfadfcb3fbb4c800bf3535c22b1d62a372de4113a2e2b90e85f7515ce84eb0152320c176679ce330c8fcaf17e90b62253a139871e1450822fae80af81fd1bd9dad1b7038db01aa6045a8db0102d7155a5ac6f72bb2afa06950d03ed94c79cb14594f6a01eb4f2b42d8b314306b1166a236f239ab47eac097e4c09126730476133b1d45e046ab098a8ae7a43e6014f284dbd83f1596db401452282471848937668b8392a38f961f88618d7aa9f39c07cece10a9bca1ca9749603a7f49791f553199dfa74e6796412c1e6a2b36f26c9030f513418171700ef7b8839c0381ab346950d5eba26a9d7b5ee4044bc594ec05aa8dc8158304fee3db3aaf0266f64d454e3fb0f5adce7d3501b743d85ffe4e889f0c33ffcaca5fab156e2d3c8f1b64f2c41c556d5a7842ce91c826592506c2d66522e9962bf9122262447105eab88e85cc2184ed0b829cdd9b3acdb9e3a59504a9d03afd81f96bd395861c6979f7e0a1fcb392b347599e34786f9a3998d28d945b23860011610a853237a0b6f249df2d99058b83e4defbc06d9df3bec802b41a2fec1b8510f912cfc67c5cc6c35150fa3d08348b5e4bc6da099fe70c737cd8b1c65ae44633e167c9005593081dc8d136799bcf64d416d0820b36b3c49ad61145585a4e3255a11ef4717e844ec52a40b6277499e5d4079de077985d5cab312e2d886fd2c44d7c0934e308e2b04e0c73fca15e764ee4fab3a24451328b5eada8f9cc79c8607d0831e6cf0d98e819ab89450f991a327d957ba2c01f5d9e5496fe09a173103db2ddbd46ac2368f6c1b37d8f5d6c5b0fb13e44a704f2b105a10bdcab372f47c7f316a3e3509a6cac2aa876c3ba96400ec9afe3e39fbb8cb04dec8cc586c62cf2f6f55fb373f5c59776ad1339f83e0d2d8c5c5ec5730c6258f5d7a3c475e295fc42d3cdb1ed8cee098fe576ad58b5777bafe0bbcac85b5a165589519e66c7306db586f23ca3a2fe5861b71303a3a7b27fac2863bb497f2e56e13478c027d8d0c4ee23b2114d0aab5e055361a06127780214c9ea4d7c5faf76a22e957eed5d9b23bdd9f433e05d6dd1ff19497040c7142b2a47b2427b844352ee0d16bb7f48441f13ed04e10a1f5573294b2dfaa0615c218b32da91d4c3ccbf6e2fb11123555d7c8ae790f0f3bbf219b077da42fba400bfa7c4700039cf7ab83ff4c8f2f78b118a8c8419468d46db65d3cba24ad78482366492a300057181d85997980d07de9607bdc78fdfbbd81bfd7fd38b384b001fba13fbb49bf0754620c781fdb2a7d9ecc949c0dfb31ad72d80669ba9eed13b650f7e9b24439a36fd01ced68f0b9e7786ecc285898cbd9c1d8e16a56a64f22b0fdc238e65d7868e382feeccebdd8ac0f3f07cf7c1498cc579ed7902fd69dba058376297773c853150110b557439a197d354e3cd619df5c58de5318795444356be6d647d56fc9b86a859cc7a759d54210f36c9baa0c2b71f1209d465cd44367ad10f1790de61c7cd0f4d1bc819d750324b15ee03d0d0ed62849796f1bf7e28c76bde2180482ea242a906ac21b87c8cd2a8482d8522cd2eccfe259b14cbeabfb814886effeffe3dc2fab36cf01d87759119dcb0617d18b4751f9e34456acbad90f090c799741189404b4e41568e63f3362f1806e31c5b46fab80e4c88b74fd812fce26fd8be6854568587d7ad4fa48ce23d30dde3aaee41dcd80a52a4aeccc5b831c79588c0640b2e72f6a16066f972bb4328771884b75615ac67752eec45cb09c00535a88fcd04a77ee26fadea9f468a1e3830a6d867236a0dfb3110a039d5367439fe4bdfef0ec654b74ffce087dab53a3c73dfd78de16a5b484aff9b0d87146fdefb708f2af7136ed72850377c6c62281201e6c8b3dd66fcb5ad08b5a1b7def79f9b57a069f44ea38f7512c34d19561e9fa787d2b556d2ff9d80ee4b48d13e350f567c162469fb55bc46b41326b5e2a881fcaa3e46bb1e25eda14e41042346c8c803104f4bf7cfc8b8d3066cda088b515deaab5117b280ed6dbb65b32c464f11c95bcdf33e5fdbc08e770037c45e5ecf66c5be861be4c57fb139588da85db47580d097bd3b566280092d1318c2cb9ef4ce36f9dc5209b51137008ea5d232be54f0dcdf37054e02b082d1f4bb6c3c70b2055465f3809566db0a7bf125c35744b600cc688cc20d2cbe891d91f9b94eec38194902910330009498fda15e39502917580e586223c165cd7fc3605b741aedf4bad4c4d3e6b775da1f14920ae9f1ca16be31eb3b98be97d8948fcd1acdd4d4d640d0e9ff1e25776df6442f85e0246a94c87f07c3e46f850c0010b0c441500393c738db192ad528fdf30738ade085f7fe1aa64ff04fb6d2abb661dc7358706a51e274e76effeaf590533d8871090393acfd805398f90272c9f009da27e9eb4b3f2320ea0a86316539fd39a8b94405f63dc74b47caab38f2a72737dddd45b63e608ce86b4e4e552714bef18b0407fd419f3d9ed7449300f44db5557587d862c16da8104d59d1f36e866bcf159e58f57dc4aae09790e16e4759c44d1817298aac924f78d2ff908e41be664cbf7388c1091dceff0b7451d416f6fc2c86eaaa7cda17f88ef8a3e0e38375a587b16b1bf94e554d9b377cff05f58f6b4c409ee659583e487a9a07a19a4d5f3ea66d44cbc6bda11d95c64c9d0bfb2683775da890d12a8101563ef4a0675f8b45963ea601cf4e0d8eb30ccdea3232d16ec4259b08f3f81624085d09f1e22f30bcfcd39e707d1bec0de5680ae6d62366e4615d462a5ec1bf3c5979dd141f29dccb581549f3c6da3d79e9f93b86dd0c0a00c0ae72a469921e851a87f7b09224e2bb7c76563eb5941b75c9b9ebaf997c0ff02db8f7ec630fe9b9d729a1eb13e625186d24ebe8fc8928d535222db7e86e33abcb8184d674aee60a2368753751deef6cfe58a65db1a782beee3e88390b4ac94c4085f6a27b3c6f4ec6000d4bec14d2ee68e58f409547e983e55fcf6f1b7dee0c03f8ded9abdfeee3a615fa9ff40e5ccc1f96a69b29f6c74d463a84b7326fb572adce7449c513b5570ced7c282d542be281f883878b677ac1a3c2ad30e22001cef97c8c30ab7c2e15d8e620cbb321f8bdf3c935a437981f9095f209e06859c15fb91ddb0c961a320e20b9427c767566fdb8b07b34178887ad189ef2637075c9eed26e3433e2eb93f138baae55224ff443109a873c2a92d2da937cc4ca46e2787027418e8df16a8ff164e1c527c4fb2abbbb53f96649ce18178b24a98f8c6f843ac7c1b9e651383eab7fc50af9534a5337ccf152fc26d1351b2d90ac78775ceb8cef457413c9916094f907e968ee54206c595104bae164f6e84ce9391d3f091d5da0adbfa4c32e705a5528dcfa57ed9e3e41525589385b799518caae2ec3c4b3b3adb8077df13e44b90d7dc47b9920555e7e6d419326b5fe1c806f0c174d0db1cf3cbae91fda2639996a965682eab241eb6e82412d7ff2debc030520859b474d65cc9d9ca7a7bdfbe64a0f288b866e42114cd62080b0b539de61a43b3ad0cb5c768b2fdc36757464af8f25bf2d5bfd8ce8b029b62f39434f80df63acebc7f3b3ed313d4face94be2386d4893cb672b35c9e346de162a4d11a13e3e711b1973ee43f57d82f86a043e9d5ab36024328593e67c8dba959e2c4c64d5be48bcdbc4b75d43ac919b6d768294ab3818fd2aea1cbb5f378c97a13f0b162cf6d7f3745baef6862f74d8631cb1f7181029df2b6c6ddff97fbd240a0ecd3593e8ff286c83221813ec4346915a8a9ba2025e7bb545a5372be950b59f4ce665b2e2188a5db21e52f33e89e197a5ca8d0f8fc1921f291650edf4561fecc0e0c802cb917a94d881b7b6b86f65634502b0f5893dd339ba1e2160b7ff2a28d68d25f53e2f1001e7331238b7b75f70322bd5debffb27a84823a57a1a8b36562b7726313ee770d21da76ac1d0731297cf0d73fbbd7a111a965f56446174d5118eb1f122b77ca04b910e268b0420b95b9dd43f083559cc608909ca31fad8ba978b211a7a583198465a344ceb5cccfc68c5cee065f69b692e49d01a9d91f29148fda8e767f605d8a361f27b6cd43f5d1c0ccace1fc84e510e75d580f3340a5296ad09d67ea96ff117283cf15e5ee7f5fc38ed9c390322718fcd6367a1c5bb3414492cd74acab5ea9dbfb168ad45860bb17c0f9a62cd0d7776934c2a4b26368fba0e668de85d6749c05bc061bb2d98d4f87f7adebb8b5045d572f70ec5baaa4b7b61abb42fe5d01cdba6c32e3599f9393011bdff7a3eb0e2f240b00f67422657f67ff5a74f0c6232d9092fa7da5429565649911081d7b0079835b6a11623559b3069a8f6e5c00ed39f9957b277c57f9354103fb2daff8363c8f5651c6f79c1b6b369ffacfb61fe71f8e866d10c3960b8ae7980312a84fecdb98ae9b6d2705292677a4f184294136095f6266f84ef5327565f349fb6109e8773b7054adfde3833dc994d674bb3b7c13867fcc3420000ef3c7be5ca8fd361384fb1aa04f2a08909ed6c20a86ce9743a38f3d90fe08f88b13dc3fa4b5a373f7951742cecb957e9bf9cedf23d13052222b9b577da222287b2e9712d91a6529e9c846682e4ed7a64be6ea235cb1688b20bafa35302d60a6122a57d735e85f8c483b938d6b933e0e29e52ae9da9dc4e9e4c33990c7cb3cb0eb1f876bd75447759921e99144559c511e51c7cac3bd2bd8e523d15e1b01a11c7f6fac154966e11632cd8bed9bfb9030ae5eb1784ccf20b3bf47c20fd4085d902db751a011928cf117463b7a1334000bd1ae68c2b467d5b998bf1be6f119454f07cc651f6264ff0843e18fd6c4e7c9875fcb4870dc1f11fd39e415b4893f81c180de2d09e4941d942c27cc7b17b1a33242a380b18cae7bd67ea49f7d71f28c7592bae562be8281a0e4bbc9e5f35e44db07c34ca1fc72d3c5cce645afd998edcf42d907b8e7cd140f7a83d4c5782a08cb2e37dd4c9f5cddcaadc2051f852ad8f2fef5b2d3161c2d24e709065db507e6d5f680b9075255a977cc092f3f893c70e39649a842c4ca491ad6a7b282470c28f43879b2204f2cf6e48c744759374657112ea1923289615d2e3e2df6f65578663d4196a59b72dfa98f306e23eaa86b786167dd701eee22a2068115002666e5d4b7686dd5b7951b3795c0b4c6c15290e083030007299c63a202890a17310328540685462ae2007e103086580efa3f1087466958e771ed3b44fd53012ede8c2380ac27ea211e392c2d42aee83aa3984550ced069f6cbf14bdd7410ee4211d61c94c156e7183370dccf7964552ec081c5d34fc304fb3c90a77933d0379b37c11292988866102e7c62c0ed6543d8ba47a13526ed6776fd4e1060330000000000000001944f1e003fe134b5ee3189d6eee87d81b57a1dd8d05d96d2659fc3b1d79d135b194870e79922d1a1c3956cc0d541bbcba70a80bd66818ad8da670562f7550f8acd03257acca9d4dea7d903039507c7a79e5bab415295938dcdd4abafa2c60c3f9b569c0f69d71c286da31da1732f2a5f07615456f83462bd5d52efaaaeeb7c9e4f569bc73df90782a0533d8832753856abee377a322a00c06942ee2ed98368afaa12ab2708cd6afed4aceb52bf68bb80053d9e9190d6ffc0ec355e715fd1c7616d806bd2d0c4319eac7cc33e3fbad9f49afc9c350d6f46ad8cd20cc99a7f304b5a9862b934dcc464b0167ce9424f22edeab320d7afd7a63591d079153df40cd63753e45babb4a14ad9610c79634280a4cf12310aca855beb9bab0691f7e3f3ea928af82fe79c61bf8f1fea647ae9215769e52f204aee66711ce76782aa929e8f5dffb2a3b053895b5c703a2741bd7a33940d6ea68034125e62a1ae0148a98ae2758b53d340756c01ed40b00225eb040dcb339689e7bd3c9f7b49f63035726ec276137f6f114162b9b7c777b7d76c33e74f360a77ad9e70dd77e68ffa7cd368a4e2a709b647f45d1352be8e866a4c10e57e7cd2c99dd1902cf69030aa67ef92ed13e6cc65fa448f348e6c1488d1a559de2daf1f44392771ac574b910595236e7d47c59187592f22ba4720552e47f708e6969c7f3930f6990300224f76416fc17c3a2b362526a2fdcab96d97de78e8a6a308283e467df0d8fca3ba7a097f8bb57eabc3185d70590a10ef8f22f0e98bdc2bec07d4cd4b32e836f14ff4ef94583c18b9d496c35c10ebbc948541eee1a2e574cc271606a3afce260bbc930d2dcb9dfcc0cdb0b11764302d770f3315b0cbb89d60c244ff442b20114432837943d29cc7b57d62927be6e180a2e9fcaad5e512b116f4810b2edf539bacc88d929d6e9e467b8929221d1d94f270d55b26f7d1ef3bde741941941d09f5368d295bd0b7bc26a5a17f7d05e8f111615cc9c9268e62c7d6afce346bd90e7df5d5c5e3c35e35ac58f2e4fbd27df087d2ea563d523229c35065c8fc2f45fe559f3ba015a2ad1e25dff624b3e276e3a26f1c79839a46db1eb568f3a7bd1cba3e70154d3f841f83f3c5311c30d84f0271c3cc6ac30b76d03fdc8a35219db36e610c521794921c21f2677c31c7f62441a3ff4c3ae51f84bf8751896b0ff2ba358a5b682f6b7c82427cc8f237476cf2f33ec253ca282f0e76813b5c39d4d39daf39470a2fe5d7536597ed1f3e5eacccd12d026c6eddae22cc2f824463e409fec56fd2e8a37a45bf3dd5925522cdb5b2be245b329bb906a327a7af9f76ba39abf2d968946cd0811f79492f1137596307afefb7f2e236260f94e3af0a0ee3a2d70b4d4fdf4e43f0cf9db48547bd3c1f0025e2ef7df60bb7106a4f6f7d6a0918ed7c205c9af79dcdfa7604eebbc1c4dd4027fd591afb34ee90796daca1d31f3300ba3d9afa3425ea51003e1b807a6b87533772e83ea6da9f6af8959cd87d1706c6afd98bd287a5b15ec3adb39edee929094a8e8d1193f4c857c023082925019adb6ee174066db2efe400b6d2dcce7ed95f277358fb7a5147408cf1b5e57b3b70c0ce0aeab5f1dc076dda4e3506d74270e6e177614c925d16c9987338da03c8eee837370120d5c3b9db22be11fb6629ee3a3707d9beb266440d093b016f431e12f8e538ec150624389a2445df96e8a555ecf74d6539f094de7297323c0923deecab854201d2199074442b8f13f56f16bed0927cd6f7bb0377ded7a9af040f7b9751c92d631073d659526c72188adf502d7802cb0182c4eb0f3bc0d85f0dc888a1ec4db0ab1d92f5d32b1206106bbe73bdb1c6aea7b0982b03df95cb3392ca24e046aef46afa0eeac40c8d333bb6eab09360a7d0a3c4d9b5e14ed06cb4bbaaf2918ea03b6dec268b4a326ed0063189196e08eeb5ef3013d64faacd12b5244bb927f5fd597565cfc0ea7882b03204b29e7457774745b83f29c4251859215af276aae1e6359659748df526840216efb41ca896491585a430c565fd86b1af9fdac03a7f5d91c9f2ec7b7d8788c416655a3ee3dc7c94fc1624f49e101ec6106b19a6552d9b0a69993aa9b22f550b0ca684ee54c5067c5e5e8e66ec76ce11776966a4857a4a4481ea153ef69c0006393395615d279867620c2a83941bc7117b3a0bbdf9c59aec020ad8472eea1ec2adb957d8f6d06128039846e7987d60b520f95ca54ac3a14114dd8c553aab25e04597ce8aca711cb9917748d0e58497fe3d248d15852da7a532b103d258d91ad24768d7f99f5d4914e5eb08c5c69ec1f0708376ad457734d22d23f2197f2c4bb7326a2af2cc1be87ed78f6aea444638ece8e4371fa6b3ca26d48082c0296dde972d9b613ead8d4a7f13916a093bd99bad57ec024004373232c191a36cbf24145c3d07e3155ef18aee937c0eb2893bff4132ebcf219de041cf0487ad3343b6fdb69bb4a53b96a6ebc6e30a16c95d7f33aff690882031877f7b0b856198d11965b3b1741b4f77dd80f11edc65c495ab032fd8a71566d5d2e5ff9fcb6fff5c82e695223ed373de3783bfd72f833ec2589e32fdb94ed7705b072d2c06bcbd6b6c26f39cec98d851fa4cf2182559699c652dad7ecfa9f92cdcbe3a2ef28b0a45573cbb0a23d983581f6ec4755dd6c6aef0ac2df82c3dd90eb29d14bbfcc7a70743a5d4f8ca94b848084933767f943a97df3ab7514e12c90ce2de7502a9687577e4fed24f252353836f98a3e7b25378f8d2d64089b7224f320817d0ca455e9a025e4b1c8953951346ded61f8b010c6fcef02ca767a116bb8cd17df58a69e9554051324c66699f3dc51895ac262cf9f40e3db2ce425374b87e262210f1945c8ab6aeb774c6d29fb299e91221b6086e74bd5064e7d4f6edde94e52f789ac9252943dd26a470c8911ba10e00faca77a707ad00b17c865fabb4ac1292c767d7862b0f88fd10a706fb7f25273dd23a47190f7583aac30f56903a6af2a53c1147d3d25c567e6cd8d1b7e66f6c79c2213530d78a3a580864a9a88b9f0639cf3f7fbeaea83ca5f5620c37ecdec12c48a5cf453a618960a458e460b3cec42612f891526dc8ccdd2126c3d3361a440e630f1e28e29911de43a516e2ff68013469ba8da4987e75510238a57c4fe2dba2d27baca30167b4004f7d16ae51e892b786d1d1ce49004b86a03ad1642f880a40ac723388f8cd6a7fafa5d24ae8f890c36e2b3090923a135bcd1a63e71933579278b51d17b2032f9b6064538029c14de6e371c5078caa81b1d4bb1264a24ccdea14963c755f6f59894e476b105cd1503c30c42f7439bc38f0a942d4d548eeed23f3f6b0437a3549e8c037cf8b02a2bae9a17e0a625c8ee15e662c8ff2471fe6a096093566b0b6070eef04fae4debf0cbcba425620b29988ffc99403b633bbabf642ab6ba50b4839682e0aca599bcfe6b9d8ab25ffe176f1d03cc3f1c019656a860764131d2b856ebf9c147b57a2f639a387817ea3fd26bed54d26131c68cb78c1bf013ee974d2a98e21a5d17b52ab3662043d268df9fc3df15f1d310bfe4b1c6e6cdb8cad182c5bdaeb130301a47a3405b3d68067821817668d6c31247a2ef709e10b0c650390a029fcf6f49f6f61435adade4a23e067fbab9fee9b41e844f93369ef4bf125a2612ecece8337d5c367a727bb393f56629b1f1835ab8c82197de24b4442697d43013fd056b12d7f798e567711134dd17010afa691b76a382f030491622a8ec99108bf1a63a4d16cdb051ffd304b4841e32aca8cf788a2a228c2332d62d70fa5648db427cd46ff6b25c9eeb4b6cec66ceb8e1f6c1014be5b8b4cb995288e4ef833edfc65d0e921831e6a7116318b644a67e406c823bd4bf565842ec98c65846cb79fd4e3f641a56a3f1607da4a5369fa8665bac3dbf60c60db1e82f15fd6de7333d0c1e6ec39d18ea197ccfefbb465c9576e7a621e9de1005c7d057c0054f5235c2a9f9b861e66a852edde3051bb2e3d9a9bd94f5cee2586ef178ff8e6453766f13e4189a953d2a07fad8ce2798a5e4804cde19729835d458b79632db9ef793b19264167cdc8a45771b24ee882a07531e59f529ac965fddff28548a5616beee0fe568c60f0154e30ba6dba2f4ffdacf3c7e865c38eb8128a1d0a33d3db35d1dbd4abb2a6137f4ff5146ef8743e974b8e39ea2af18e59e79f7d8f19d9fd55f207ae08f634103c7ba3d0e51e815870c4f2201c48ee932b855b6ec5d0620670b35d933886d7c5bd340a8be97da01155ce4f9f9cc3a3348d6069e76f86a6b24293c47f0d7a5103ecb5046c2ac806b66142e330856bbf0b2f13b6c5fc61919df8af8bf4cefeda6575b98aa1258e38768110fc8c70df44c1af37c8a37a25a349836e0b03f0b44b5e7351aef29eaae8046a3053acdeea934e4c9c19c23b72d40b8ef2a5e1145fe30437aa98075036d8789f1d118278822ad95463f310d181a1ad307a5574a2755ed1f897ae80034374d621c3239fe99567f33e9276401a60be33b25fbc9b567f0d4dda1b5a1895f6d7da6298612693ccfd8a5c6fd70605571d07080ad946c6cc4a23fbb951b572ca4495dc341f02d34e4c65cd55e91e20cb9e9236cb291d31bf065edf734761666ed1635d9a3a966c05ef84187bc48c78e4cf3ccf25815028e80f5dc4837e52fdf8b7bb508e1c89701e49ab92a2a7bf296e63f3ba1cd254c9fd0d46f03b3ee0e7d8474bd1bede4047080f704e6a97c90714fd7cf8c2b26031f83bf712a42fbb4fbece25e068fdaf9d9884481bf52e36bd0354b01e9d2c42bcc1f4cf3714b3e10ffacb25d179bd85abb48b4ec52903dab11cf70a493d5110bf9d8b1732d3112b0e3de13b3f2c99a484b7c1d6e889537e96d3528dd3ed5315fafceed1e9cd7cfe145ac07c2a09258cebbb81e4462e9edc53e3ddfd411b95c70cf7ebbe1e09598d2b18f573c3dfad52731b663aaeab850bee6ff9a874f00429779a87049a2845b68106db1d1f0ad17747661ffa8f9c4dc8144ba641c3196312538418f53ca44594afd0c6341c407e4049a6c14c8fac8b3a952c7eb1508288d7e3bbc37ef9b3dc8ab9b856ec80c8a06a3f1da9f8190f8b1d64f628305f9365962690e74162e6e50d916c6792b0f4a6423a05efcc3ad63e3e6acc389859322313c7268bbeac37ea629b6b4353aa48f7ea52055ab45ee5953194ce2b0ddb0054943cb5d02fb7c2ca1740f8101539be45eadad4c048dff9aac92ecff51d5a2f9e853527e000646e4d282df506f96c39b3cfa5ee49bd15efad63e7c20e84e55a3f108626f7a40546fd00dfc84b927b9893717e12c4af7c018c2fed42c9b75d3c2aa8fe381a45cfc7c7ac9291d748ea47019b7e0413dee6bda9e6ee79d045b598484c4e3fcae27242e4d4dc05376b73b0f762566b5f6f82aaf787f2e5a5bedf498f0bc1cb358fe1e2e2792e66c1c45e1b0d246ebfe1dd2d828409e1ecd1655d3d67ec2743e83e3d03f25c98a94d6f4c2c35c3c27a11b23847d508782b66738cb7a2bae10be75bb5baee38f9735a6f35a900e10f047b87b288b10e0ed365a736cf14db7fecfcb2ae3328066f38c9c3ce3666cea772c7dfc3f8f809482b8db6f9f6f890530d5be63a8cb0a666fe9170cb8cf5135ecd99b96fcc9066a6ce63bb68eb15cfcd105b30251b09dca1f6f515d322c0055b1087c21d1628368a6eecb5eefccd57a851629e47a4817b18f5af59f8050e91751b6c9d114b668fd69ad29ac8e465c40810bb5204d7cb3ac5641b9acf7741ba6d321c6934b71d37a454698b3b6652e3f5d3d2b5c2be02c38d44150ac2714d9631dc1f32a6dffe0757c51c1ec009b1aba29f0375eceffae3f5fd585e18d7e332b45e0f627864160000"; + const ISSUANCE_TX_BYTES: &str = "020000000101975891c8466c3eed75f817e16867e4004786afe652c833db38a6d8ff37626ea10000008000fdffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000846378d77e3e759fd90a42a99cf4b95e8819db99d424ce3fd0352ec16ca9115a5093b64687ef20c5c3da2d2695c42aecd775d700d2ddb9a903a3eac8d661b503070040aba8ea89e00fbd0e14bcbeec94b883ac4fba0dafd5e795658bc1d184ad80c625809da1bff65d7958239c186b5dd8ef23606903bf1f5961893b3e3d7dd44902e4ea302f8fceaa7a53a51a24cfb9666f2d798224a5a84b60d26483a9da385362b87300616001455c29f7cf389e353362116c2ee374857c013129e0a3a07091f4e203f46aaa44d17824ba311243ed38a70fb9b393bde312d5b4460c208fcaab83caf46df2ee16c162094809fb9dce4b642224da210702be1b04deb94c70381d9c59df43aa717e026743f9c54c2048756eddb6d5992a56627102194852f9b160014feff6610a74105b33e4e8d2cac73f3d05c944d270bcdadac9b7bf23a96bdbb9f488bf7909f76d7ff997a5551a38778a7e7aa5f8b540889d6c664ee5d6a106dadb07a987843fc2fb965f356821a59db1dc1f7e1aa839f03fbd0226d70bba5a6fc866d4c980b677cd9112e98aaadfc671ff7e39e49a748ab160014b6d0f377bd2d2dc48ebb374b7107a15d4395ff8101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000000000251000065000000fd46104033a1497f00233f81f0687170526b203c1d6f34ef9c7d73bd2ccafc3d7de5fa4e69ccd7bf27571e7b49803282a8dd2fe98845efbfcd2d28de571a1d2c077228effcc0ce338457ab6f3d15ea7fbbdff8024ceefc72eb9345d272a3650b6ad1220a173cc7ad90c7b6d108bc5bf20e8613ee76b8f2280f55dccaf5e0adb7eb50753288693ea99417bc7588db0f4ff798a503885da8ab6ae3be8b628ba48c7a4c80a6067017705486e7c06ec509557f7d75e2e75db784a1293f847a5940e725d86a505f720ce43c27df97ea2fc05e003cc74c924fbe8513454ccf6fc54e73987a59f48297175e81fd8ccae89bcad6006501c6126bb94ae7bbea35f1e996107015eaf7bc4e046bbfff3d324afc1f8a764f91f0775aac6c328a9ee2c28c74673e62a0ed7ad630738329be8d586bda0e6224f42a9d7c4b4dbfe505ce513e9bd2d2f78a7c3977d74ffc5790dc50f913c80e8b1f23e6b236c790a9578e956b039ee8940bcb3f7d87c8da028dd60b078130c52d553d63ec02689a62c4d0f365dc53cf406b6ed3ee3eb60a280776452712635788f6583e875c598ab9ca6a96a2b68e99853abcbc1ba113bed2fbf05fb63c5fef34ab38e10f426309fc0891bbbadc44979409712a3ce7cf01a31985eeac0f4dbe00a57a20ef9f766025102b1428cd8adcfa63a7ffa25f89c2ba8e6b92cfe660135f1714179d455f014a9875005afa019a434d285927dae2e285bdf82721d65d2f095fdbe83cd861497e4875a69961cd44b6895485ce325c074f69ccb60c8831a7437ab08f9d6da84f40f65d5a63b409631d0a65eb5285c0f4837ff71f2a7391f51d10865731bcab4943bbfa3feec2433640329753a134a9035caeb9bca8287c5bd723252c8f6849b80e89f9355200de5548f081a36c5a1cc579be24b8b563aa4025c005cedfc1bb540fa8792749b971835edc741fbd211fb45ed6df5094714a8ba30f3895a8e2a4314d944d00791a19f4f811bd7d67a83df31614b43f13c59ef1d20e28d589a505a1acd37a4ff78e9af2d132063af9f5fd7d9fb3cd6bf9f3dc73cd4cb3479f7c36c23135d8c962fb531d16ed8fb4055151c6216cf53bbf1cf65ee63d9bf85bb74bfae527022619c4f0d9e8d3339bde33bb789bd25bee0241acd1527283f1cd58b7bec00b6f30931024e30c18469002e825bf6d10240b0163585cdff2845a077681996558fd63f1f7c373b54af1c2da61bb015ff70895e1072136d3b603d329c428871750858a70a222ad73560bc73e5ad24f541fc4a32f734a2dc722f24579b505158f39bee0cfc4c9890245c2defb368b6d6644a2579ff4a23082e686c94bcfc4097589efeda5810c363acd8290ee29ee76baecb178a228de614388b79283df07396195c4d2d3aadb7ac3f68cab163c8a1157e5ce4c3502cba9032918bc259434c4b12cbf77d08beb6cab6e19ac3c7d2d82a6b792a0c1de08540ada9c45dfab36b4c71419d54f25959d7c95e2c0301982caddd0fd0f9ac45dfb1cbf9a6cbb3111f7e5a9e8a29870c1029cc0d74df8f8a852dca511aeee22c471b09d8cf18eb85dddf9273c5024de0d0d617f3609d6e09a2d0f2951ae35d07f5f27cd47f217a939d56aae2c8ee2d95836bbdaa5c816082fe2bbb1f5c7a114e1b45ddd448c3ad19cdd7ed04a087ba00524ed8e16f295b4ec28f5ba5e0077d3d0ef30e718ce5a1d9c8430631baefcc9c26551d26f42ab64041129304eb4d76720d94adf9f2cece3627b2833335dce5f562ae2169ef44f12ca36a26d45195847b8eda8836177025bc66aa4f103d662ba1de526db5c7ad2899bd9e22245445022a156a2b47022853ca473588172efdcb8e61c8d20962a8c92d3a86e83050ed75d6a152915eb0e1051800b79d15b473899f4fbd3d47d6e7e72982d6c7adce00e51f0b1033bd1112091418ef979b4aa7f4c942b31ecc5bc9cbddeb3812906a172fbcfe085eafb901140710cea913779510648a79fab4917f96efc5e7f28b0fd2c813805760d657a089a015f6368f2b471539c8ca2b33c4800a6a201a21daf09a194e1962196716e0a645c72dd1168dead6b64b0ef16ef4b73dcb2b56d517c082f218db141059cc7d52d5e5e0832f13030be8e3b36605f9f9749fbb1b1b4805a5720763ae406b32498f1aab809088489a6b71e2274ec26c4964f2e3bb32063013868a030af901e8afb5f563e3a690726b7afd3761ab9cb3abaa4849355d0183dfc66d8e8bace12e739c9a9ae1efa0d9d8f58df3352e7f8c1e24e2a0f71c861ec09428bbe837145ec1b50426c24725bdd025da913b689ede4c8d57c8cda6a48a5b246b0ae57e4dce9cd2cfd0604b217f806650ac957aa01aa9f37e601bf1fbe36e0b3359a4b7477fb4e59c48481797694b5ef3b51ccb7a69910617988bf1b5dae18ff0686d722fcef097d55ec74932643a285242243a96288525dbaab523d55ab28aa0b6df74c4bb2d90042c952c5412d379357c81fa1618e3cbc855132e0e63242fcc2cdcd1d96663ab8f112424f7deb4aa6a01976d4d8c9e82303b3323bf1a77a9392b8141aba33df3a02f3423d50136a65aafa4b993a36ebe4e1ade2f3c8cb70bdd35b4787180cfe14ce82e81f60aea09c052f74a41045eee52844cb50e257dad50ca3e8f3631f82366252cd4dfef33e6106edee6764d25d0a0cf8c9d8dc0b126064d2efec032890724e40ceb350a4d5df729bb22a878fce0b4e698b16c251db70a49f1275ed28a7e678f2ffad0047a118d7f8202f417ba2436b285cbbd0f1c2710f08d73b31e54ad9e1164c6fc1ba1a5c9666b001f5d8b7d03e97a48d338890b41b2968c65859e18907a08e7f96da368f2143f63d09c0948e6e7a3b41210fe7ab7a48668339d0f5e4d4de1b8c0cef095b228dcaaa0ef8c6fc1d8d65cda7e9e81137aa8d0595b53c6bd9f831debee20784e790af3676039b7f4486e28ea28edb66dfdcaaa62f286099bbb62066023278de78030c30a7bc33f5ee2d2fee4705eeebf1768c7ee1b15dca01eb82f818589ba2362feee455c568f38c306d964987e29cd50408c73d171f93c4bb1e4937bbd786d567bd3dd488a440cf95fcc1a54d6e0b35173421daa01512f970e89738251fb75701c5c849720290e49f124fe3c4e2885b23b055b1cf6b7ea10a07614e9f2eba7bc953158e11130167fd2081a09381329449e2f17fe7403db46b0ce380fe846cda85e4c085fc0dfd8a86d4aaa4fd6c1b2804b13c70a4a858180ff1450a4c11aa576327f78e74c3dccbb27d178ef09575c4879e84d49b41caa3b3dd282f1b464d0df4bc01565123ead10bdb9273544b058032807482774fd9e705187bc6a4927dc29a23a8d6e5d7c7b54432546d5a2f341b2d90ab6866d9177f7f8d601083a53444b16b1e9ef4ff24954f4a3bc5fa25af61ce9a5c1ee2e507eb4f4022b4c0bea888491b1201477eeae5ae432a262b8c9da98283b895f484dd911a7c503a258e9b4ea01f07ec66fce68be4372d87b304d7840362064b7443ec227df0d25ad6e695f800be0bff81cb68d8f05c89318561209f6e36952cc30c0a2f3b66855b63d388d55a44013096f2bf8941804cb202685deb23ad0fdb92de3a780051fe5f644483db93c95fbff0a30e0d1b1a476f1161953835665c761b7190b1520b5880896e0c51ae6e3090fab2554ad56dcf0d8311b0256a5a4561460b01741e843d4b0c26abfbabc519dc2141d3aacb6ae7713cf2c95803904ace06f667d114193e655e1ff0ce724441cedfd21ca3268cd2f586eeb150c08c55540e3c621e77986c4fd07d5a586a787fdb976a1549d982c2620dbaff166949c99f5d52664fb350025976d1f78d00994442df6d552cbc567f0d9d7b4d391876bbcc3b9e61f2f764b2464066f3a491042f7376ce4f28e63e6d5d395e5ef6c529d6eb3a792ffd5ea1016fbae24188d96bac0a9c40a3c024a4432a27145d72d0f9c91f373ad0fafb7c4503d3668ebf8c090a805a98d272c6f24ca0f8c98f03f3c9d33f38c92200d4e9a3d8fe2034c195779c2fb261e57b1a067f177af54f2ad7fc49154582a70f7da33500a849212717756f06e53bdcecd41b75faf63afcad04118a3ef956d72643b7d9737107354a3dd3b24f456aa4ab45f00b4a8d2a7617a9bc9dc2afca3c2e478ebde4634a73d1b6b61c99a14bd4b962bb3e3fdd9a00ab928e1a8fc1dcdd0a4ac463ba693ecd9d1ba40199c8f423ecdce7f05f928f5aab1baebbf3c0fa17b114aa12bb3c6ebc7da0b113e3e2886dc092b787a738fa1906c63c815c2ae6d51e97bd44fed8f1b83f5c4f9141971e6947c88490f67637c03cd7e354f74fab3494f40c869a57f67b6525501cf19ee9ae93430b29cc680c8b31dc8e8950a06c43e821ab86f16d275a219e49f9cf94016b7c08c8ceb161c52e8bca8f8044669a96c160c1e38c7e65afa862264ec168dc6d4a6a9d70625341ea83daeb15b93fd20d449157271e01cd591f4ddb9ad8b4bed1e1402cf9ca6964653288c78d673803df2505c680c840ea6fb7411f1cf229184f1331b8c8ed82d1ec7d35a8c4de9fa04b6283eb67fc4a164c122506921e49f9b6726b97168b53b3dac93f302e79dd260723dd077716083060cf2e363361fbba6d8b86723c64382d59fe20920e9d5aa133021a6709901b535c63c4795845c78a7e647e39340dc6b74d3e7341c49bd8af57c9abb2261a328f33122026043e4f32b7ebecca9d9250f186e81906143f409ef36c8865577e797e7d57381a0fe633df73b59544c16ed8b8f852f3e033da0ade4412a4d1ce584b47abf8ac4ac604a90a0f316b621c87ef7200bc10df3221533e1e54a6bde13c549c95f8060b4149d351ee10e33217470125bbb4bf327bfd5d151458dc249aa4b57f14979fd53b09ec2d01b9358739fbe44e7ba4c54a9e6bd483801605cd33dfa84e7dee0bfc30d4b4e2b0c45355e13a92f9c588725829de2e9efc0adfd807c3f8a32b623548785d07d3520f0933e5d5a280efc6724e03dad47013bf54338ef4956c9be1b09681429f9cda18a11df04ac0ed66f28d2f3722aec5fc0f169055e9f5357d15fa6d53edc35dcc5dde72ca418c618994a5e8cac6c2a115e87b27d2d3af81654c23d476f49b606144814cb36ca892b0e0d4673a2091c4f948079eca42f66e000432255da0c99cae1542acf0836398e067b8d13b9e1e9a149bdda367bd65f88af269078ac8cdf79a2b303287ceaac470b050b6884a44c86ccf413e50fecedffd864c16b18bf3c59cc9c0e2c3a84a37f7dc1bb53729f306f40827d30eef367ad5217a5c5f987da8003b08d55b90014cd828fed7beb4c4afe6bca378aae72f8c4c3eeabe1c1b7edb44f1ce8abce27c16154ea58015fcf9ca99200005729abd67473540c4c9f20d1d0875697dcf01b2cfe61aa729a9f90475bbb720e413794df4079bf3199ab1a071ee448dbef39a9d1e2c7462cb6a56d558389244b4542f913325dcc0c2517dfaea6cb67a1e5ee8e9e7f4b4b343b33588c28f6cf82be5aca0c0b51dc7c461dd05ccb9766814a17d0e491a61b87bf134002441fc1c4f3b060df87b065f75f30121545755397015f0c4b8c7cf855defc20af97349281147ce557e40d292b36950d58692162dac1993c0ca4fa61fafdc1baa39e50302666546a8c3f74c2e0b53b3f0b9307dae1834a284aa6a2f811af925037a7a69a0585689d31777cccabb18b7e3f5fb5644339fd998d3a3886e3e1bd5f85fcc39f8e5d14005292c275076636179c93c8b2593661f7ece42551ccc1e43fd78a0331001b7b3b9a7dcdf40ff398a43e07be40ec2946f994fe8647f854b86f8c4fd735dea6cdbe7ee962fe76b788f3405392e747293043c0938545f1d78c00c1cb84991752936fc6f0a72e861f032253c6ba214fd46104033d2543400eebc89cff38ea74f8b07f174b6917951751737e2a917818fffe7172adda4808ad17522f3d76e16489d4b4098b049353c053a7eba7fc13f9129fe41c6c2301782bf9a1ad16f22b7308e97ed7a2f29f5f948a13be4117dcbdf069210d93d508fd03bbf6430f4f788a097f573a34f430304d3518c5e2b1669f23a3d7597a502da41a2f92aaef8922d8603cc0455691cc59d28a004ad2f9385253874c2073d0e2575c48a8c8ed8a273bc2de878e217be56d318821c4205c784b838c6b030ce821246ac7b8876376ac0131735b66d616bf6c89b1f183ba62d53913be941462d67adadbc9f546be9099e895b839875952e2845bf468326b8802d44931d7fa81a1b7a048da7065538c6700c68213f16060bdf5c0ee5cf25066e0a739f0816d551d404e00b632a1f76b254ff8e93e24f45e6ac9f7c62486cdab28131fc9f77aa440295460b38379d347718daa871929743a8b5f19237b6e58c1cfa5645559fe31b99ae317de28d8b7fb5e32aa9f163a8479635e5ff564280ca7dae3ae355fb2319c624f01e6ff1a3c5f6e98dcf5d58c943a49b49e2d10c6b6d7cf355397bcdf3cf9be84e7eb3353f7bfea193f1b6b22309838b081fe37609daf49ac30a8d0929c189ef58148d92cdc584bf9ae21b13e7998d7258811a22738f4cdb7de66967411dffbaeff7947ed330a20f9de0ff91cd2d282153fce0d29637c7c064416611b13ac77585d03cc8862b8a93ddf03c1efc52b49417ac9571312f6d216f74815800ed6827bcec56b0ee6f55ec8e4b8737ddcab9a8e966b7a64fc1adc3eb04e8aeb44ab5633f12f57dbad58d121997621c3fd3a750d78f4a9e581a0c468eed32f8e2d340c76f5604c2e4a9787a153c51588d518ae7f5f3b4d7b072b4a953c594a261870dfeecec4c3865f98a890312f3e6d0ebbceae695f415ae93e18eacb14616f40d69bde463bc2190bd6a73174c26f838b49ac8286544b2a31d8dadd6e8164b823436490bc1d6790de250e7b80de847a856a5bfcacd44c172672d52fb2c8e6e04972596105d3372ab4e9d68845843d457d4d54b21fe2f4e4d117f5fa2ec13d671d9cefaf00fc5d61c3e9b5b565ef84afa535008115f8e6d8b8fb8ad2744a337b967054c6c45da0a6f820bb02f07d51b7ff10820421db70ef08c24d9d913deca3ddd1aa2659cd1d349a9606b35de2735e058ce86d0fd2ccf147867aa9f9c236bb32d82c4bec7d8e77a159d5057650ebb161ef1940d8e64fe4b6fad3bff7621848d3e8401cd5ab25a910352aea58e06c4549d2b7907011e02b1ba94774a4a66b24466f641d376976db27bd23ce682fc4341f5d449d13b9d8fc2f9a67db302083a53bfc79ba2e607cede1c2cf95d1e529262cf48908c889944c4db1b5e8555d86effacb882d148993a1fe91f567b14b43424515aa75852a50f73fbc5751e11c2467e695bb3ccc95f77e11ee44d7c57927a255caf5a5d7365af8af604a097097855d4627b44855549566e00fb530f52e15c1365ba86aa7d60f1ef9929b2fd0ce8526bb7adbeadbba9ed7e9ee60a11cd3952b2b142b3c348d5fb7c1e9a1a7cfc8c75937563097ff18bcfc00cf7bafce608a7caef0f18210ea76d30be1c866697771de2027f4c5dcc15e68342c7b413438939db39677838867379bf6c85c684dc72d00e4a7e15ae21ab863f0901309a673d85563feccc8a5e489bd7ee96ff697f7308a97ca31d71e8db533feb347881d7891f9e82b15a2a0fd725e523b41d67fba6292d177599c7ef1f0860f341d7002dc4566c72e3862abe80711acacfcf8162fd3d6485063a53d73acd54588aca581f1cd846565b0216e871cb65fa6d77cfb46445dd843df7c7ae759951fefaeef9a4301223e296cdf5ec4a6730580a51ce69ae044abe974bad7273534f4b2f668a3a7c978508fe9b9058668e56f6dac062e78205229a831feb67327c12e1a0166e34dba6cef41a72af91ee00c270f1b1abd6aa80116d2f76e6861aae614e7d2a70bc0045dd5005acea24baecd3be3cb6b5fd4dc8e15e396568842ab82e638edc93438050fd7966217c5a523942d2f6bd52a9f414493748c0c26069b30c8b2ee1d2ed5748bd2b79071ec7c53789096ba2e78dc19d4a904bb73e85c18cba02139c429a6f634ca6688034655deacfc4881fe2ab2f9b2d192102ac72084a33cd1525f4e99e52e18e9a7a70fd139e6ffdbd18e9f9f69bd88ca6c51b5f8c164704cd5d5fe9ffd1da9f66ff0b956953f5443cf3b2673718b6e159961e8fd39ff5de5a5bccf92dbb5fc61f58a7e6ca4e5377771ba062faa584965e136af0395ceab6ee5a92b97ddc6935fb14e77f63226a473e7026f62956262d21328fd6a3540415efb91513938c4dd48fa8fd39aa5752591aeecf183c49a3802dbeec2b6acfe2f2b541dbd0957899eac632d053ead0bd0dd743a2f768afbb8e2498763544c6b84972a0a5c809c68db911228f4a5c7bee0ffe26f841e0357117940117299ada73759495898253da36357e0233ef46f36bbde01a0829acb0134653e653aa714708cea625d2b664818a34e96a90fc2c26b6891654ee3afa0156b18fbf336d28bc6f29c8075ac0327f20a9b263a6c821d292de100cb211678874f29aa7c94fe9aa33158d6408e370b23b8b24b5d952fc75a1f3d45624d11d07a262d09aedf64fec483c1663ffe6efbf45afb69b5cff2cbf373ea078cc268965c8754247042acda43d49ab34ac9d8d55023a76d42ae858731cceab3c324a935df0f226b1f4bd1b15843ad68ceaf81a630871d57663dfa60bda287e88a3e36906895213edc1a387c3306d139555679e40abe5ec9bb7af1e1eca841d1e58dbb84282aa4f79f2c7e27a5c8da392930be19ed9bc14abb2b032eca39f6a6b4dd63eea4917d8e3f10c1a24f02ee43b5e61bcda2351f5a9525e50e99244947c8a8b7e36b0ef6a9b5c81d7a463aa8f899f91286e59b5accfde4d60fa0dffdaeea610ebf420042210ea39e841c9369761633a2d91ecd2db4d9b5f6ba3c0234acf6482133ae1dedec1311f66e9b2c596ea7348d2a97f69d3c8f6ed24791a7099e7ca434561cac27bc923398a3791509002ca65e90a363cd24ca901171073fde0ca56cb5a3c30d79730f001f6742fc60d65fe92a9c0ac21c7316b048c8e912a8a15f1258c94b6f70b964e7761e5d66e3a197e36b1d9e7cf0199282cfd8ab6fb08fb6fc416bb5fdc27e90df8f19da219600b3c534eaf857dca7d81ba0e6d39fe200b79580a4d50d7049647c95293bf7da983507844bb0ae18970354124834f2a84fc7a3a6c0d73528465bde9f919ab23e7557e023c9576efd95cb547c0f8eaedbdf889c10c2fb006b1c283c26f7103189a0a7411f5ba4ac1bc1adb8a481c82c470af023cd0198258c02abe70f6a7d3d88ad1db0140f553855fd3ce0c20a1ba30cd2dc66fae3b1a2374f6b70a72b84224e03d3a7d368bd265009a94f49a0e9d4fe9008b2183bb51e3ea4b2ec2eac830f617606403226a88e4ca85ba83f634d5953b3507eb6c9c0085d13cd6deb27beab2e18f0568a506da251d4a2eb0e4e57eb3aa28ae0831ee55bd17814b0231e0d6642540e73f8e56f016f1e9465123f27ed192b1e6fcb84f8cf03dfc0cf107fe3bf68700173c614f5ec79c8a513fb332df753e14d9de20514002dc34d845d78c1d9facef86f9e19717d505b2d00aaf90da2ed5a60d032ea41b893208a838633f084e940bd228a96e6c4af51d320070e096541551149b3a764407ad92b9331eb82ee66e26e49da13e1d433483b5c4edf3fdfdb780f7b76a7bcb467fcdb5db6a9b0e63eb390c780a850c2c9f1d3683ef9ca949c03c6851f04602ace98ae57abcc2d99f330a369c4bd2b4adb13f70d0f421a8e386b572f33d16e70f46891f33c9f978280dcaf5b28a7e98b5022ff33517fc1ebe69fe7846f48fcb3d8c9c3790787d740c4cb42369f52b18c420f76c0046eb3da8b4999b28d6be977a33502608937036a0a770feecba941c08703eb5c3c6adc91e7c7cfb7475e3bff85075aa41f39dcb2813675e13516c0e2c13a5cc1ed685e00f7d8ebb6f0fd5a2d174df43f7d0601f39694df34d7287dbfef1de686277d6d0506c59f73a4792a15b49f4916f0b051f9da8a14ece65871f2dc653b7d5902ee0ba771985d9f0432db9fc22bf468567f4c2e19ac23a2692ad8e155d433f53aec7294c01c9cf7172715391b2c0b53986c6e06f77959780dc0ee0c4b6f2f6fa1753d8582f2bb14f260f34f33df6ddee30c8dc823f7be6c40d0df760590a9445f7ab0cf5d5a3f33e0967bb023e205f7737d390d0ed6c52d22726aded20d3d31928810463bd511b1ee3f6ae11337712fdb8a8869337b4b8819098b8c1b725127c8bc48646634f31ce0487bde029e6960dca8d27d67473a9308a47d1ef35519ac9fa56eb93d840bc5fbdef88f6017c38849fbec727ba4da247a3c560a08776efa9a3743ddd18e9a535d60d74aeb5c0092aa15728cae216fe4eddfc4b99024732eff574d7eec2199a2bdcf9843d61a01bbddcc283e9fd9283ed91e92af964d84e5102470ee5061f66d132602158481362fea94ab9c4dd19c1287d8a38d8722d6f9f5f0a0573bf0f623cc5f2992f881e5101e436b2724b1d671152fbfb38caaf67cf25d5c304cf72b14da11a15c47aa008969a616e67643f5491605971c9e3e2a4f179b7e59df1c0f031903d486e0fafe65e69a2f5c934bc8badad20619c13a1eb9214e989d3a34c562d654a5235fe87469f2400920fff5e0e4c8e5b3e3fa928289ad378bb674e03e9249b7632253278c9fa1a240a2a2e33efa823e538ad267e980fb453037d5e109184515d0486fd2e8474c8219d093f2ce316f5af0e04a8c63005d5f7dd86c152d77557c38c1ae1d168ea06f1d9514091072a917d966ea0d931a559208221ce4b0fac75980b87948f1ad561570cccd0daf0828cacddac0805ab0e5a13df399b61a5e9de6c49adf65dcbe20c2f81ebd4fa4039e80d0727a14e6e0b0d3b800c47ffe37468d21e84e3977dcd64d3cb41cc30007d0cc574f1d3e600acc0c9b8e1f9095e4889ba5905da0af2d97cb68f0c1143cdb3db961d11ddd6971812cc9204f801a7ff5a448c0c9e86a122026d4ea74d0b2def69b056757f7d7910981927ab0cdb3820edb735c170e7379146edde11bbb4964e27aaa72b9c977431a540784df74d9cc601d2a447ab6837d3798fb49497d5931b74aba2a563928daf05c4b95a4a1cf25c38e8a6d7e17bf36e0c621b9351bc8e009cb848c827b4526f0edfbdeadb3d734f78f00fc03570b83f7d5840fbaf905af3b54313055d95e4d697f53e9b61479eb8c428f87a10423fc862266cce54350e81a94cadab7d2f70c67225363a8e63cd002c92b769c4fd30737d444a3c1e897e367ee423b74841bfe50360a49efd0c256731975d9a38d04fa57454a25520ea4f56c1ed65683e6d012b8c8ebc3237c56c54556b3f8ca63f5c766364ed4794a2b15e8b70cdf67d552ed4fd8ef0ac159e3354efb440f323da3d1595707c49924dc0dcb06a9128a7e0524a5fd6b6510dfe539bea327f9745aef775da6ee55455505dd8ac5a2959ed411fda0daea56d25dbc9c7d3a67761cc3ea2e6c65e600e4db7890bc43b4cee6db84e3bdbe9b4c2d7c1e0af8abbb62f8fafacec372a5a30fc935a73904dba6a6e29e5582b1089af5cfe5e98a8591d2a282c4c5c8a52f2701e16278e659c00160ee1d0863905991dac6b290ea877cdf531db4fcb8fd7a2b0826e57601d65f9b4a895ba5869e45678e66dec8654049ab9743ddd9341138eaaf98713b4006b283134ec2a6cf4b12c624a4c620ac77561cead9a5c11a7d5b4b0ae3f92f72a4f3baf13eb4253498d6df7128cf935b075bec291800247304402200279ca59f49ab840062e5d5d3e1448ebbd381ca68ce2c0ce3cf0cdeb5357302302204c6b09eaafad7a019fdace6cab0335cfa1114f026b8ab91a9d11c8888b4e99b201210356f8a8c1cefde4ccee408bcfdf092f7f811da92e4f99d99231b3a5d8c58a490a00830300072589ea79c4dd0b6d3b2aa0c1ac0768a29a6b7a2e78127b2d8ca9c039506b28a3457584b47a73efb3375fe373e21e8395d4c1783d30193e571057dfc6ad94d7eeff5085548b88a7155b56e33b8fe545281765a07217094da53d1688992b1a98396a0451494e974da12ea68ad1a0a372ae160eae0f425ae227ecaa27aba4547f73fd4e10603300000000000000016fb32b0047f0087d30de68fc697678c7f4a95254a2004925e057185ff09d3b7f60b0d7f9af606b1846239a35843c6e6e221eaa9aefc0ff8b33a1b9a10855a59136d84c20cf675d6cffbb62999e452037baa143650e18e81733205817a8420440eea5490608b149366eeb4d1790e9199a04a39d0cae86ad4c5e250fac55f6953a489484938dc7521351a9c1775a658ead3404618ea8f724df382e94358c7ef3e669d9affbd0a2d6b79a6740464940eeba629a7bf7950a049366755b88dd9b2856a937ecb30b063238bf7e694ca78bf56e6e967bb820b340945b34c5d1bc55a60e5905d8c6ece19fab342cb977397a38d983b387ba6f1c846247b81e0d34e1589dc89617fb315eff0f4841898d28bd324db24056b6d5311d41bdff31ff881ac3b08d690a67481ee7bf77dc8f220d33dc1497b080b13a5c8660dd63951a76a1835bbf63505ca76d3d3c15ab4415097787b0571c0967db45556322a54aa0a9b8c5eb9007d92924c193796a3fed9debd71f67c6781a39e198ab227b7a060821dafc703f2abed6aae8103742128f087303107e1ddcedd96acf844b1b26cd795d13ef7cf0f184bc8cebaea710fc0bc0f258500df2c82249975e83a4f53dd74f4d1f692cfc49e5ff3ed5563110079df9f073db12fef044ba5fd1d76d4ed90f3c9f1901e4e57c63e6296c9f7aa36ea95941a6b01d7de20048714bb68964077e520f2d557246b92f3d567ffd4685187709e44d3f66a96cd05bec08f8667d6e29109a8240877b42031e56f213524ff6d881385da86e85765d20b2ad132ae191b5e8bec738df5c533c20fbcfbbeab8f0b5aebccaaab1a6df234a05bca892c048367f07e1747d4deef8536c19d2bf0c4574106bbc4bdedb30606f35559ddfff29ac65517477f69b75f6e6bfbb878129b684d25c3634e907cdc04afa579a815994ef6838348e155ef1dce5c4eae478bc2954e2b45ce5d63cdee731f398b98a8628f317fa1bb7602ce13506d84fc8e8852315df692bd0b6545a8d7884930a06d23be3d31b72878045d83018e7e6cdd437bc241dca6b53f4c9635eebe5bbc610da87d68d91eaab2c82f216049a49dc5e087fdea5c9f7890e05fb20f0f93ce8a008874d25bf377e52a346277069097046d27f8929c320ae1934cc5707eb0146fb73f98af7de49a234a17f371c14f778ba90308b7ace650cd667324b42ca790599d02579a38b64d5840aac48d0fe5b56a77e1fa6d034624da585a766a685c9fe2d1a4dc65d64588ee30d950643224acc1744b46a13687078d2354d68771825acf6f57317959b0c296af72499b1da75054f076135a59126d974ad6ef25764d122d35bd96ac0c4fe9cc47631eda9027bee307a74997b9bbe07a2da7c29b69de863149130be9deb5b9784cf603d0fea396fc5556a36b36a5b6951da1c289e6fd595943d258c8bc58541fdded69e7d97eb90eeab944804cb4262dd87fed48ee071c2fbf5a3df015d0fbe54a80dbc7d4d350aed97e91e2f000abfb8002feac203ec5ffcaa7033ba9b1a82a4edb37706db47199e3855f993ab02b6c8501e9a1215565bfd0194655fd98edd33282c7150a2f9369e46e31724c12b870223b0c9678f04a5f951860018ac96928b5d9c18ff1e06205e27e9bb15576a1a651ead62b7b8ab11057b352e1385c60478c369e34c320fa1b36e856a1f302f1da053d4b220fcbed9be7642319024bc712f082a5517d9f6dfa0c2d85af3d8660cd06e71e9de67053cda59926d9e0845d30939e7aecab1dd26f186239b8392500821d4c8dec5a8d0123cea75354a7dfbaa5ebcc9d75c9a307d633e4aae4ecca0a6216c1509dca5b052644dba73a0244e29f5e95ad2c09d313f607ca6ef424fb74481d4427ecd9b04afd563f46136cf2c6bc596a13ada2564c52450dd9b451da52cc1d82002ee68acb1caa38cdc2fd629e10264e908645eb859173b698918ca8dfa2e3a9350fc0545cae228890316c82f491c281d15bdecf2bcde89e9ac435239eaea24a8cddd08aac147633e822f2c57aae5f59ae901042cc15b045a58d78099da9031528e8182ee93433e4183c969cdef637d421c0fea042c1da85f5f338ea9be23c84f0f53456a695ae5db2d54d2e70e5579a6b81d1d3f5e63de0c746b0361d713d2d1e83118eacc7032bbd31c2ea26bb5d55fc47d7b56033a68c567cbfcf8856a7cfe0d37c59f660ba3226d409205a73b5a7b86a1bd6384e93e1486c33540f2e2e29551294be97204cd85f29d38ff11fb1d3dd34edb1bf7eb9e05c5b20318dab896a5134dadcdd197c74f94c0110a8349bf29fe246a0fd54063603de7a67333ea3830779a8d9bb6f41a82ede66103859a25c9116193c95d4e6a9029bf3e301f1b77c5f31d7c78ba43f34ea027ecd7063919ca1ae583d135c008edba60b020804571e33b9ada9b9072b339400d356a54fa3f4000ab7cbb8337400d2d9f130755d87a8b62a91e1fe95a0999f24b56d89a5ad291b0ee96aecc43b08aade1223cf1cc231bd2a18aa175dfc7064692bde34b0f3b6c55e2d27cbf58554484649f1dc52db19bdf284ba1dea6802a0808d87584a3ed0902988b5d1761234ae091ffd917385ffd419224d29f0d8393acfece765eeebc62406edc65c54ccc38bd118f7203e5bb12d2dd21024b34984731f2fccef7d9e292d18c6651d30b7fe47dec4c643c252c2186bc0ddb095c4f96a7ef7d7d73b0e3a55feab61ac088e03fc5d79b0cd44f538c0750aa592b4f9ec5f2f35446c6c4263c292a3ee1486ef7665569d36eaa9bf52b5ebf394facff338923e5f2a802272cf866a59d325f6893c913c55fa215533add8cedf52a1d79098e3c29d4a54929e762d6eb9f23d7dbb69187ba5c11f441575e603858f6729895ce534ac0261f5f88eefc4e3145a93b960030dfa4a5a8a5e287e932ed2375fb29b66d6b563432692d052a43f6caec6c9be33b6cad83e354225a232d056a614031923f67a0322d9c261a4a730a1427e0537749cf4fe20844674b456eee877070aeb10dc5d424f4f1b3d0fe42ef68ec48ed4ab6b2bd4eb52fa868d510badd443a7b22f5c370864fbf279774d26c6314903c2b3517276477db9a4db8f6514b38eb5e2bd5153b73288a0bdf17bafef3a9067216ce5939f5eedaa3dfe8f3a9a0f10b0933af5b37da4542e1323c22e00fcb9ca2f12b6a644cb6709cf491abfc86f8e842ca568de096e36714e2e043f10b039c62822714fdc65818293c44f03c984546f6c20177f212dd8485c20d07f809748a9f93b666daa2999f044016065afe45a9a8e865379489c1e7daeff03ffdb0b9cda86d913e764676112320cedf6455c2c9aea0df7b90c6462001f4cf02e4dba3afc85f283ea1fd69a8a73a9c85ff54ef7e12515f531a9ed0cfb10fa747cc4131737cd106ecfeda49ca336f9ba8636769f41922dc277b5ae95b01bbc15c36c2d93301e4d87279723c6ac79800f6a9a0533e99b3ce272694d4c6df941389bedf36fc7c8aee32ff954705f96b64ee524b7178af060d8113340b142325581839bf29d02144bbabf4cfaa1f6e178412098e2c175b5132dca9288ea1ceeb4517d78883e432e0cede03a59a6260abb55a1a2410b74f1adcb9730d377448cf8fb57d5c00de0b4000c54fe7ff516694e46e6c2b9b4efd5ad11862f368d29b05d0ad9a8b7891715259f00643060d5b348bd5c355fe294971590ff4c03d1513d4cc91fe5bc32b9531560ca50292cb340a1a3a5d7d5b6591a4e6db94b4807b4d33772304ef838912a142206491d7f3ef8c8a2932ef3c04ca2fc19e726a10ed8dcfcd0a2ec81a049afd6aa24ed7a0df80c809a6f5963e93da0e231b748d0099e12c2ce8e0754eb3d006cfcc381ee495234aec6568c978d7e8f44d6644c4638ce425ed3800c3f445ec7fa85adcb23cf78a18fbb5deaa20b47c12896377d032a67056ea192b187ec4a44c16cd7b728941a087a45f9915668a7ce2f120aff50660d66c8477591a4d2f9a79f39f40401342a5aae0ff8253ac62c9d46b09b458f20069f9170c1aa3ccaee649a3fb37f7de2cec2b7c82a07bdc707580a3371b2387c5e110eb9c945d7013cadd7580f3b0590777e7af620b7e0bbe389b70eee42cde184413cd3530083f612069796c9fb81fbcfcd69de104e8de56afb2fb4998b94100b53aad94f2cfd6bfa3f7d0158d549907c9a07b45a22b1a4a190bfd4c9b67132dfe5e5e5a00d37ddd80f96c1506da71baa95082c2b85e0d354ebff3a329868bc5840b17cb8fc4ddcbfd195e447c17d76632dfaaf6b02c5981f88572791f32810c8272408ca9c12ab24952fa50c5efe817fb21d204d59eeaa8d8282f787a92d1cd27ddc7abf8ccb530dd51f9337fcbef197823ef11a23c76e121079bdfa523eb6cfe78e2e8cec31846c5920dcf6700bb3875b0c9fbac31a6e6a1551205e539d39e0f333197131340d9d74c5b343450f4e0dc756d3dadf1b2d25c1d60f244ce0923682b96de22be8e1fbd6e762cb89bda68c597baf8c6c814f760050381041ddcde5f52dac92dfb3b229491712cb1ac7365dbd1bb62f91b6d124ba65fc8d83b532c8d98d96a2e131b8fb943335cf83a2de63e17888557be979c05c6f05550a49abe1a9b00cb77b756dd16a8edae167f38ae784f816c6d8b05edb9db093e5359963a927370b78f14bca2f472a2974b10a37ef0a78cb78754dcb5ec8f59d5e2f33523aab33abbeaea56a5c688e498fee42d202b0c1f08332077ff5360c189c5d30c4384cc8776bf30a67636c711a1396d7f139c57b2cc365d156898979f9a38a2c02b601ca5448e7a7c2981df170219f5a4550ded4ab499f4ae1e47da52b6c1ea441466b6824cde0e2a53bfd6fc636274760b102edf719473eeb4b68e70097d80e739f8f2e1e284228f731db44d327a52efe258f78795b2257dc3478e0a1732e741c0f68074a5520ce4cfaebcee260538bf4954e669cd17887bb25929330b4b2ceb750d64da0cd6ffa5171c4ffc104ceae609fe777b99908e4f46169144c07c5250fc4fdb051ba5335297357039e5957e77b177b974892b19e27f4b7a5e2522429451f84a2b5b2931fb4d5a47319e1df8434caf337784e6d33783a634d9d4a4ac27bdd96ab4684dbf29d0612a1419ce54db6e4af8209ca7da98b18a1eedfa2473653ffdfbf1472229fcf4c528a99995d48003c85511c87957a862b5308b9d2977ecc82b03ea381336fcae95e9c5ffb60b0b487f4f1c212f7f11265d928d095a16a638faa646ef3969c7cfc2d082ddeea9facd508f2773787508e45a969972fe54576120a834b2d553659efd9d9f605aee04c2a36c12b82a13a97fdc2af4211c87585b0378ec2c0cabd769479bf653bedd83c6796ad4c878114dae20affa06fa66daf9cc0e658dbe87cf51637bdb332e6e231ec30b371297d0c7a73d128f4f909175613243973298cb852b008e117ba7d41e71cdd1e24c3d4f6675a26ac8ae67fd8e7e5945853c11a153842196cebf9c791370556e5f3a72deb5685af7aa52ded66bb4760f3bcee98d1a1750426151d7346a9eff2dddb9b7b3736ac0bd0a590e7af4b25ed7fc3b52a5b52c915a3584e1b2383cdbfd8138dd545abc2734864fce88ef2cf12cabe5eea478095ffcc10fc512adeef7d9e8e27d26adef3e48de380e5b851d2b9cc1a8b2f08081308383f71cc61166efe9c9a139a662b94154ae757cc37225521d5f29d27347606fd2698ab7ff3f2497ef1c8efec0ad281f0932f36a8518ab40b946746c5661aeca2ac446064a7c823b5791f2687fbcca8fd2ce95e4994de25a8fba056ac2d5bb8e5a6f5b312c0893c8a7bcae58dcc967f3917dc3c97b3b7ff9653fdf087ed66b847731656e56f83829b0f20ae395d6b4a7587dc796603620553f7dca83030007025608a8da0159d01ce5cc0f7c8f453c62ef23ba5cac9e87d63d854abe7282c506e5eb6dd22d8168ed895e19432694f3f19f0ecce079a1cf1daa0b7e17a193c90d4fb0a155818e1d6fcfa08ed64e17bfb2573a3fb74a4a1b9210d3bd1034f7ba01079fe75b5448b77807a19f418cc560290c9f9ba4b670a9941d5134ece76c9afd4e10603300000000000000018a6bce01807c9bdd76cc5033fcdc479c5044a055e47db9d282b018dfa90f76d01b9fb00e4081f09465fa45160873ca33bdda257bfb2337278edcd9f6abaa88994ff9a932176975d5f971b6a699ecb55231d2e90f7f8a4b1c29fc8062c17de71f5e6d2489be1dd5b8ed07cbd8f5bdacebfe98569f88510cf266dc9fb9139d654f03efecc270ec323ae588076a64de344f1eb1def577664ba6dd07b1856b68deb5b32d4335e25ea784441dc70039e8fed8e56d9ccfc79c79614a79639f27ffa9497abede452de0cae56414662e511f0c01a938e99e88f5e0a7bce74530b4cfdd5b2b4994941cd7d83739f223ec0f18ed192a7d4730725b2c123682af22b9a1f0aee048fa8610fd813705757070838f826c37911f60a2f132f00910fea698e1f0ed735d7c75772c40a76d0af7f349df5974f95147fe8ea1cea847557889efdc3e836abaa04da71be14cba79ec7ed106c35622312994638863c263482e8291ec65a0d9e5c194c1780781c782684fa3f153823013df06f44ab05e0ea00f396053e3dd784819bd0a616eeb6e4ac8a11a5cab14ef40151fee4d7459dec2caa90e22dd2e2e01c082ababf8024a6553bf0e6d085a65948ab12598ffb1ac59c1df05df073fbd015337355db33701847c803318dd348356a657ed1a50344bada887b2559a6ac186883f5cfb2c28239ba699f9747a967b03e8866dabb72cfadfcb3fbb4c800bf3535c22b1d62a372de4113a2e2b90e85f7515ce84eb0152320c176679ce330c8fcaf17e90b62253a139871e1450822fae80af81fd1bd9dad1b7038db01aa6045a8db0102d7155a5ac6f72bb2afa06950d03ed94c79cb14594f6a01eb4f2b42d8b314306b1166a236f239ab47eac097e4c09126730476133b1d45e046ab098a8ae7a43e6014f284dbd83f1596db401452282471848937668b8392a38f961f88618d7aa9f39c07cece10a9bca1ca9749603a7f49791f553199dfa74e6796412c1e6a2b36f26c9030f513418171700ef7b8839c0381ab346950d5eba26a9d7b5ee4044bc594ec05aa8dc8158304fee3db3aaf0266f64d454e3fb0f5adce7d3501b743d85ffe4e889f0c33ffcaca5fab156e2d3c8f1b64f2c41c556d5a7842ce91c826592506c2d66522e9962bf9122262447105eab88e85cc2184ed0b829cdd9b3acdb9e3a59504a9d03afd81f96bd395861c6979f7e0a1fcb392b347599e34786f9a3998d28d945b23860011610a853237a0b6f249df2d99058b83e4defbc06d9df3bec802b41a2fec1b8510f912cfc67c5cc6c35150fa3d08348b5e4bc6da099fe70c737cd8b1c65ae44633e167c9005593081dc8d136799bcf64d416d0820b36b3c49ad61145585a4e3255a11ef4717e844ec52a40b6277499e5d4079de077985d5cab312e2d886fd2c44d7c0934e308e2b04e0c73fca15e764ee4fab3a24451328b5eada8f9cc79c8607d0831e6cf0d98e819ab89450f991a327d957ba2c01f5d9e5496fe09a173103db2ddbd46ac2368f6c1b37d8f5d6c5b0fb13e44a704f2b105a10bdcab372f47c7f316a3e3509a6cac2aa876c3ba96400ec9afe3e39fbb8cb04dec8cc586c62cf2f6f55fb373f5c59776ad1339f83e0d2d8c5c5ec5730c6258f5d7a3c475e295fc42d3cdb1ed8cee098fe576ad58b5777bafe0bbcac85b5a165589519e66c7306db586f23ca3a2fe5861b71303a3a7b27fac2863bb497f2e56e13478c027d8d0c4ee23b2114d0aab5e055361a06127780214c9ea4d7c5faf76a22e957eed5d9b23bdd9f433e05d6dd1ff19497040c7142b2a47b2427b844352ee0d16bb7f48441f13ed04e10a1f5573294b2dfaa0615c218b32da91d4c3ccbf6e2fb11123555d7c8ae790f0f3bbf219b077da42fba400bfa7c4700039cf7ab83ff4c8f2f78b118a8c8419468d46db65d3cba24ad78482366492a300057181d85997980d07de9607bdc78fdfbbd81bfd7fd38b384b001fba13fbb49bf0754620c781fdb2a7d9ecc949c0dfb31ad72d80669ba9eed13b650f7e9b24439a36fd01ced68f0b9e7786ecc285898cbd9c1d8e16a56a64f22b0fdc238e65d7868e382feeccebdd8ac0f3f07cf7c1498cc579ed7902fd69dba058376297773c853150110b557439a197d354e3cd619df5c58de5318795444356be6d647d56fc9b86a859cc7a759d54210f36c9baa0c2b71f1209d465cd44367ad10f1790de61c7cd0f4d1bc819d750324b15ee03d0d0ed62849796f1bf7e28c76bde2180482ea242a906ac21b87c8cd2a8482d8522cd2eccfe259b14cbeabfb814886effeffe3dc2fab36cf01d87759119dcb0617d18b4751f9e34456acbad90f090c799741189404b4e41568e63f3362f1806e31c5b46fab80e4c88b74fd812fce26fd8be6854568587d7ad4fa48ce23d30dde3aaee41dcd80a52a4aeccc5b831c79588c0640b2e72f6a16066f972bb4328771884b75615ac67752eec45cb09c00535a88fcd04a77ee26fadea9f468a1e3830a6d867236a0dfb3110a039d5367439fe4bdfef0ec654b74ffce087dab53a3c73dfd78de16a5b484aff9b0d87146fdefb708f2af7136ed72850377c6c62281201e6c8b3dd66fcb5ad08b5a1b7def79f9b57a069f44ea38f7512c34d19561e9fa787d2b556d2ff9d80ee4b48d13e350f567c162469fb55bc46b41326b5e2a881fcaa3e46bb1e25eda14e41042346c8c803104f4bf7cfc8b8d3066cda088b515deaab5117b280ed6dbb65b32c464f11c95bcdf33e5fdbc08e770037c45e5ecf66c5be861be4c57fb139588da85db47580d097bd3b566280092d1318c2cb9ef4ce36f9dc5209b51137008ea5d232be54f0dcdf37054e02b082d1f4bb6c3c70b2055465f3809566db0a7bf125c35744b600cc688cc20d2cbe891d91f9b94eec38194902910330009498fda15e39502917580e586223c165cd7fc3605b741aedf4bad4c4d3e6b775da1f14920ae9f1ca16be31eb3b98be97d8948fcd1acdd4d4d640d0e9ff1e25776df6442f85e0246a94c87f07c3e46f850c0010b0c441500393c738db192ad528fdf30738ade085f7fe1aa64ff04fb6d2abb661dc7358706a51e274e76effeaf590533d8871090393acfd805398f90272c9f009da27e9eb4b3f2320ea0a86316539fd39a8b94405f63dc74b47caab38f2a72737dddd45b63e608ce86b4e4e552714bef18b0407fd419f3d9ed7449300f44db5557587d862c16da8104d59d1f36e866bcf159e58f57dc4aae09790e16e4759c44d1817298aac924f78d2ff908e41be664cbf7388c1091dceff0b7451d416f6fc2c86eaaa7cda17f88ef8a3e0e38375a587b16b1bf94e554d9b377cff05f58f6b4c409ee659583e487a9a07a19a4d5f3ea66d44cbc6bda11d95c64c9d0bfb2683775da890d12a8101563ef4a0675f8b45963ea601cf4e0d8eb30ccdea3232d16ec4259b08f3f81624085d09f1e22f30bcfcd39e707d1bec0de5680ae6d62366e4615d462a5ec1bf3c5979dd141f29dccb581549f3c6da3d79e9f93b86dd0c0a00c0ae72a469921e851a87f7b09224e2bb7c76563eb5941b75c9b9ebaf997c0ff02db8f7ec630fe9b9d729a1eb13e625186d24ebe8fc8928d535222db7e86e33abcb8184d674aee60a2368753751deef6cfe58a65db1a782beee3e88390b4ac94c4085f6a27b3c6f4ec6000d4bec14d2ee68e58f409547e983e55fcf6f1b7dee0c03f8ded9abdfeee3a615fa9ff40e5ccc1f96a69b29f6c74d463a84b7326fb572adce7449c513b5570ced7c282d542be281f883878b677ac1a3c2ad30e22001cef97c8c30ab7c2e15d8e620cbb321f8bdf3c935a437981f9095f209e06859c15fb91ddb0c961a320e20b9427c767566fdb8b07b34178887ad189ef2637075c9eed26e3433e2eb93f138baae55224ff443109a873c2a92d2da937cc4ca46e2787027418e8df16a8ff164e1c527c4fb2abbbb53f96649ce18178b24a98f8c6f843ac7c1b9e651383eab7fc50af9534a5337ccf152fc26d1351b2d90ac78775ceb8cef457413c9916094f907e968ee54206c595104bae164f6e84ce9391d3f091d5da0adbfa4c32e705a5528dcfa57ed9e3e41525589385b799518caae2ec3c4b3b3adb8077df13e44b90d7dc47b9920555e7e6d419326b5fe1c806f0c174d0db1cf3cbae91fda2639996a965682eab241eb6e82412d7ff2debc030520859b474d65cc9d9ca7a7bdfbe64a0f288b866e42114cd62080b0b539de61a43b3ad0cb5c768b2fdc36757464af8f25bf2d5bfd8ce8b029b62f39434f80df63acebc7f3b3ed313d4face94be2386d4893cb672b35c9e346de162a4d11a13e3e711b1973ee43f57d82f86a043e9d5ab36024328593e67c8dba959e2c4c64d5be48bcdbc4b75d43ac919b6d768294ab3818fd2aea1cbb5f378c97a13f0b162cf6d7f3745baef6862f74d8631cb1f7181029df2b6c6ddff97fbd240a0ecd3593e8ff286c83221813ec4346915a8a9ba2025e7bb545a5372be950b59f4ce665b2e2188a5db21e52f33e89e197a5ca8d0f8fc1921f291650edf4561fecc0e0c802cb917a94d881b7b6b86f65634502b0f5893dd339ba1e2160b7ff2a28d68d25f53e2f1001e7331238b7b75f70322bd5debffb27a84823a57a1a8b36562b7726313ee770d21da76ac1d0731297cf0d73fbbd7a111a965f56446174d5118eb1f122b77ca04b910e268b0420b95b9dd43f083559cc608909ca31fad8ba978b211a7a583198465a344ceb5cccfc68c5cee065f69b692e49d01a9d91f29148fda8e767f605d8a361f27b6cd43f5d1c0ccace1fc84e510e75d580f3340a5296ad09d67ea96ff117283cf15e5ee7f5fc38ed9c390322718fcd6367a1c5bb3414492cd74acab5ea9dbfb168ad45860bb17c0f9a62cd0d7776934c2a4b26368fba0e668de85d6749c05bc061bb2d98d4f87f7adebb8b5045d572f70ec5baaa4b7b61abb42fe5d01cdba6c32e3599f9393011bdff7a3eb0e2f240b00f67422657f67ff5a74f0c6232d9092fa7da5429565649911081d7b0079835b6a11623559b3069a8f6e5c00ed39f9957b277c57f9354103fb2daff8363c8f5651c6f79c1b6b369ffacfb61fe71f8e866d10c3960b8ae7980312a84fecdb98ae9b6d2705292677a4f184294136095f6266f84ef5327565f349fb6109e8773b7054adfde3833dc994d674bb3b7c13867fcc3420000ef3c7be5ca8fd361384fb1aa04f2a08909ed6c20a86ce9743a38f3d90fe08f88b13dc3fa4b5a373f7951742cecb957e9bf9cedf23d13052222b9b577da222287b2e9712d91a6529e9c846682e4ed7a64be6ea235cb1688b20bafa35302d60a6122a57d735e85f8c483b938d6b933e0e29e52ae9da9dc4e9e4c33990c7cb3cb0eb1f876bd75447759921e99144559c511e51c7cac3bd2bd8e523d15e1b01a11c7f6fac154966e11632cd8bed9bfb9030ae5eb1784ccf20b3bf47c20fd4085d902db751a011928cf117463b7a1334000bd1ae68c2b467d5b998bf1be6f119454f07cc651f6264ff0843e18fd6c4e7c9875fcb4870dc1f11fd39e415b4893f81c180de2d09e4941d942c27cc7b17b1a33242a380b18cae7bd67ea49f7d71f28c7592bae562be8281a0e4bbc9e5f35e44db07c34ca1fc72d3c5cce645afd998edcf42d907b8e7cd140f7a83d4c5782a08cb2e37dd4c9f5cddcaadc2051f852ad8f2fef5b2d3161c2d24e709065db507e6d5f680b9075255a977cc092f3f893c70e39649a842c4ca491ad6a7b282470c28f43879b2204f2cf6e48c744759374657112ea1923289615d2e3e2df6f65578663d4196a59b72dfa98f306e23eaa86b786167dd701eee22a2068115002666e5d4b7686dd5b7951b3795c0b4c6c15290e083030007299c63a202890a17310328540685462ae2007e103086580efa3f1087466958e771ed3b44fd53012ede8c2380ac27ea211e392c2d42aee83aa3984550ced069f6cbf14bdd7410ee4211d61c94c156e7183370dccf7964552ec081c5d34fc304fb3c90a77933d0379b37c11292988866102e7c62c0ed6543d8ba47a13526ed6776fd4e1060330000000000000001944f1e003fe134b5ee3189d6eee87d81b57a1dd8d05d96d2659fc3b1d79d135b194870e79922d1a1c3956cc0d541bbcba70a80bd66818ad8da670562f7550f8acd03257acca9d4dea7d903039507c7a79e5bab415295938dcdd4abafa2c60c3f9b569c0f69d71c286da31da1732f2a5f07615456f83462bd5d52efaaaeeb7c9e4f569bc73df90782a0533d8832753856abee377a322a00c06942ee2ed98368afaa12ab2708cd6afed4aceb52bf68bb80053d9e9190d6ffc0ec355e715fd1c7616d806bd2d0c4319eac7cc33e3fbad9f49afc9c350d6f46ad8cd20cc99a7f304b5a9862b934dcc464b0167ce9424f22edeab320d7afd7a63591d079153df40cd63753e45babb4a14ad9610c79634280a4cf12310aca855beb9bab0691f7e3f3ea928af82fe79c61bf8f1fea647ae9215769e52f204aee66711ce76782aa929e8f5dffb2a3b053895b5c703a2741bd7a33940d6ea68034125e62a1ae0148a98ae2758b53d340756c01ed40b00225eb040dcb339689e7bd3c9f7b49f63035726ec276137f6f114162b9b7c777b7d76c33e74f360a77ad9e70dd77e68ffa7cd368a4e2a709b647f45d1352be8e866a4c10e57e7cd2c99dd1902cf69030aa67ef92ed13e6cc65fa448f348e6c1488d1a559de2daf1f44392771ac574b910595236e7d47c59187592f22ba4720552e47f708e6969c7f3930f6990300224f76416fc17c3a2b362526a2fdcab96d97de78e8a6a308283e467df0d8fca3ba7a097f8bb57eabc3185d70590a10ef8f22f0e98bdc2bec07d4cd4b32e836f14ff4ef94583c18b9d496c35c10ebbc948541eee1a2e574cc271606a3afce260bbc930d2dcb9dfcc0cdb0b11764302d770f3315b0cbb89d60c244ff442b20114432837943d29cc7b57d62927be6e180a2e9fcaad5e512b116f4810b2edf539bacc88d929d6e9e467b8929221d1d94f270d55b26f7d1ef3bde741941941d09f5368d295bd0b7bc26a5a17f7d05e8f111615cc9c9268e62c7d6afce346bd90e7df5d5c5e3c35e35ac58f2e4fbd27df087d2ea563d523229c35065c8fc2f45fe559f3ba015a2ad1e25dff624b3e276e3a26f1c79839a46db1eb568f3a7bd1cba3e70154d3f841f83f3c5311c30d84f0271c3cc6ac30b76d03fdc8a35219db36e610c521794921c21f2677c31c7f62441a3ff4c3ae51f84bf8751896b0ff2ba358a5b682f6b7c82427cc8f237476cf2f33ec253ca282f0e76813b5c39d4d39daf39470a2fe5d7536597ed1f3e5eacccd12d026c6eddae22cc2f824463e409fec56fd2e8a37a45bf3dd5925522cdb5b2be245b329bb906a327a7af9f76ba39abf2d968946cd0811f79492f1137596307afefb7f2e236260f94e3af0a0ee3a2d70b4d4fdf4e43f0cf9db48547bd3c1f0025e2ef7df60bb7106a4f6f7d6a0918ed7c205c9af79dcdfa7604eebbc1c4dd4027fd591afb34ee90796daca1d31f3300ba3d9afa3425ea51003e1b807a6b87533772e83ea6da9f6af8959cd87d1706c6afd98bd287a5b15ec3adb39edee929094a8e8d1193f4c857c023082925019adb6ee174066db2efe400b6d2dcce7ed95f277358fb7a5147408cf1b5e57b3b70c0ce0aeab5f1dc076dda4e3506d74270e6e177614c925d16c9987338da03c8eee837370120d5c3b9db22be11fb6629ee3a3707d9beb266440d093b016f431e12f8e538ec150624389a2445df96e8a555ecf74d6539f094de7297323c0923deecab854201d2199074442b8f13f56f16bed0927cd6f7bb0377ded7a9af040f7b9751c92d631073d659526c72188adf502d7802cb0182c4eb0f3bc0d85f0dc888a1ec4db0ab1d92f5d32b1206106bbe73bdb1c6aea7b0982b03df95cb3392ca24e046aef46afa0eeac40c8d333bb6eab09360a7d0a3c4d9b5e14ed06cb4bbaaf2918ea03b6dec268b4a326ed0063189196e08eeb5ef3013d64faacd12b5244bb927f5fd597565cfc0ea7882b03204b29e7457774745b83f29c4251859215af276aae1e6359659748df526840216efb41ca896491585a430c565fd86b1af9fdac03a7f5d91c9f2ec7b7d8788c416655a3ee3dc7c94fc1624f49e101ec6106b19a6552d9b0a69993aa9b22f550b0ca684ee54c5067c5e5e8e66ec76ce11776966a4857a4a4481ea153ef69c0006393395615d279867620c2a83941bc7117b3a0bbdf9c59aec020ad8472eea1ec2adb957d8f6d06128039846e7987d60b520f95ca54ac3a14114dd8c553aab25e04597ce8aca711cb9917748d0e58497fe3d248d15852da7a532b103d258d91ad24768d7f99f5d4914e5eb08c5c69ec1f0708376ad457734d22d23f2197f2c4bb7326a2af2cc1be87ed78f6aea444638ece8e4371fa6b3ca26d48082c0296dde972d9b613ead8d4a7f13916a093bd99bad57ec024004373232c191a36cbf24145c3d07e3155ef18aee937c0eb2893bff4132ebcf219de041cf0487ad3343b6fdb69bb4a53b96a6ebc6e30a16c95d7f33aff690882031877f7b0b856198d11965b3b1741b4f77dd80f11edc65c495ab032fd8a71566d5d2e5ff9fcb6fff5c82e695223ed373de3783bfd72f833ec2589e32fdb94ed7705b072d2c06bcbd6b6c26f39cec98d851fa4cf2182559699c652dad7ecfa9f92cdcbe3a2ef28b0a45573cbb0a23d983581f6ec4755dd6c6aef0ac2df82c3dd90eb29d14bbfcc7a70743a5d4f8ca94b848084933767f943a97df3ab7514e12c90ce2de7502a9687577e4fed24f252353836f98a3e7b25378f8d2d64089b7224f320817d0ca455e9a025e4b1c8953951346ded61f8b010c6fcef02ca767a116bb8cd17df58a69e9554051324c66699f3dc51895ac262cf9f40e3db2ce425374b87e262210f1945c8ab6aeb774c6d29fb299e91221b6086e74bd5064e7d4f6edde94e52f789ac9252943dd26a470c8911ba10e00faca77a707ad00b17c865fabb4ac1292c767d7862b0f88fd10a706fb7f25273dd23a47190f7583aac30f56903a6af2a53c1147d3d25c567e6cd8d1b7e66f6c79c2213530d78a3a580864a9a88b9f0639cf3f7fbeaea83ca5f5620c37ecdec12c48a5cf453a618960a458e460b3cec42612f891526dc8ccdd2126c3d3361a440e630f1e28e29911de43a516e2ff68013469ba8da4987e75510238a57c4fe2dba2d27baca30167b4004f7d16ae51e892b786d1d1ce49004b86a03ad1642f880a40ac723388f8cd6a7fafa5d24ae8f890c36e2b3090923a135bcd1a63e71933579278b51d17b2032f9b6064538029c14de6e371c5078caa81b1d4bb1264a24ccdea14963c755f6f59894e476b105cd1503c30c42f7439bc38f0a942d4d548eeed23f3f6b0437a3549e8c037cf8b02a2bae9a17e0a625c8ee15e662c8ff2471fe6a096093566b0b6070eef04fae4debf0cbcba425620b29988ffc99403b633bbabf642ab6ba50b4839682e0aca599bcfe6b9d8ab25ffe176f1d03cc3f1c019656a860764131d2b856ebf9c147b57a2f639a387817ea3fd26bed54d26131c68cb78c1bf013ee974d2a98e21a5d17b52ab3662043d268df9fc3df15f1d310bfe4b1c6e6cdb8cad182c5bdaeb130301a47a3405b3d68067821817668d6c31247a2ef709e10b0c650390a029fcf6f49f6f61435adade4a23e067fbab9fee9b41e844f93369ef4bf125a2612ecece8337d5c367a727bb393f56629b1f1835ab8c82197de24b4442697d43013fd056b12d7f798e567711134dd17010afa691b76a382f030491622a8ec99108bf1a63a4d16cdb051ffd304b4841e32aca8cf788a2a228c2332d62d70fa5648db427cd46ff6b25c9eeb4b6cec66ceb8e1f6c1014be5b8b4cb995288e4ef833edfc65d0e921831e6a7116318b644a67e406c823bd4bf565842ec98c65846cb79fd4e3f641a56a3f1607da4a5369fa8665bac3dbf60c60db1e82f15fd6de7333d0c1e6ec39d18ea197ccfefbb465c9576e7a621e9de1005c7d057c0054f5235c2a9f9b861e66a852edde3051bb2e3d9a9bd94f5cee2586ef178ff8e6453766f13e4189a953d2a07fad8ce2798a5e4804cde19729835d458b79632db9ef793b19264167cdc8a45771b24ee882a07531e59f529ac965fddff28548a5616beee0fe568c60f0154e30ba6dba2f4ffdacf3c7e865c38eb8128a1d0a33d3db35d1dbd4abb2a6137f4ff5146ef8743e974b8e39ea2af18e59e79f7d8f19d9fd55f207ae08f634103c7ba3d0e51e815870c4f2201c48ee932b855b6ec5d0620670b35d933886d7c5bd340a8be97da01155ce4f9f9cc3a3348d6069e76f86a6b24293c47f0d7a5103ecb5046c2ac806b66142e330856bbf0b2f13b6c5fc61919df8af8bf4cefeda6575b98aa1258e38768110fc8c70df44c1af37c8a37a25a349836e0b03f0b44b5e7351aef29eaae8046a3053acdeea934e4c9c19c23b72d40b8ef2a5e1145fe30437aa98075036d8789f1d118278822ad95463f310d181a1ad307a5574a2755ed1f897ae80034374d621c3239fe99567f33e9276401a60be33b25fbc9b567f0d4dda1b5a1895f6d7da6298612693ccfd8a5c6fd70605571d07080ad946c6cc4a23fbb951b572ca4495dc341f02d34e4c65cd55e91e20cb9e9236cb291d31bf065edf734761666ed1635d9a3a966c05ef84187bc48c78e4cf3ccf25815028e80f5dc4837e52fdf8b7bb508e1c89701e49ab92a2a7bf296e63f3ba1cd254c9fd0d46f03b3ee0e7d8474bd1bede4047080f704e6a97c90714fd7cf8c2b26031f83bf712a42fbb4fbece25e068fdaf9d9884481bf52e36bd0354b01e9d2c42bcc1f4cf3714b3e10ffacb25d179bd85abb48b4ec52903dab11cf70a493d5110bf9d8b1732d3112b0e3de13b3f2c99a484b7c1d6e889537e96d3528dd3ed5315fafceed1e9cd7cfe145ac07c2a09258cebbb81e4462e9edc53e3ddfd411b95c70cf7ebbe1e09598d2b18f573c3dfad52731b663aaeab850bee6ff9a874f00429779a87049a2845b68106db1d1f0ad17747661ffa8f9c4dc8144ba641c3196312538418f53ca44594afd0c6341c407e4049a6c14c8fac8b3a952c7eb1508288d7e3bbc37ef9b3dc8ab9b856ec80c8a06a3f1da9f8190f8b1d64f628305f9365962690e74162e6e50d916c6792b0f4a6423a05efcc3ad63e3e6acc389859322313c7268bbeac37ea629b6b4353aa48f7ea52055ab45ee5953194ce2b0ddb0054943cb5d02fb7c2ca1740f8101539be45eadad4c048dff9aac92ecff51d5a2f9e853527e000646e4d282df506f96c39b3cfa5ee49bd15efad63e7c20e84e55a3f108626f7a40546fd00dfc84b927b9893717e12c4af7c018c2fed42c9b75d3c2aa8fe381a45cfc7c7ac9291d748ea47019b7e0413dee6bda9e6ee79d045b598484c4e3fcae27242e4d4dc05376b73b0f762566b5f6f82aaf787f2e5a5bedf498f0bc1cb358fe1e2e2792e66c1c45e1b0d246ebfe1dd2d828409e1ecd1655d3d67ec2743e83e3d03f25c98a94d6f4c2c35c3c27a11b23847d508782b66738cb7a2bae10be75bb5baee38f9735a6f35a900e10f047b87b288b10e0ed365a736cf14db7fecfcb2ae3328066f38c9c3ce3666cea772c7dfc3f8f809482b8db6f9f6f890530d5be63a8cb0a666fe9170cb8cf5135ecd99b96fcc9066a6ce63bb68eb15cfcd105b30251b09dca1f6f515d322c0055b1087c21d1628368a6eecb5eefccd57a851629e47a4817b18f5af59f8050e91751b6c9d114b668fd69ad29ac8e465c40810bb5204d7cb3ac5641b9acf7741ba6d321c6934b71d37a454698b3b6652e3f5d3d2b5c2be02c38d44150ac2714d9631dc1f32a6dffe0757c51c1ec009b1aba29f0375eceffae3f5fd585e18d7e332b45e0f627864160000"; /// Creates a TxIn spending a txout explicit asset and amount /// Returns the txin and the spending utxo diff --git a/jets-bench/src/input.rs b/jets-bench/src/input.rs index c6c3f84f..2226706c 100644 --- a/jets-bench/src/input.rs +++ b/jets-bench/src/input.rs @@ -7,7 +7,6 @@ use rand::{Rng, RngCore}; use simplicity::ffi::c_jets::frame_ffi::c_writeBit; use simplicity::ffi::ffi::UWORD; use simplicity::ffi::CFrameItem; -use simplicity::hashes::Hash; use simplicity::jet::{Elements, ElementsTxEnv, JetEnvironment}; use simplicity::types::{self, CompleteBound}; use simplicity::Value; @@ -1274,15 +1273,12 @@ impl InputSample for CheckSigSignature { use simplicity::{bitcoin, hashes}; fn tagged_hash(tag: &[u8], msg_block: [u8; 64]) -> hashes::sha256::Hash { - use simplicity::hashes::{Hash as _, HashEngine as _}; + use hashes::HashEngine as _; - let tag_hash = hashes::sha256::Hash::hash(tag); - let block = [tag_hash.to_byte_array(), tag_hash.to_byte_array()].concat(); - let mut engine = hashes::sha256::Hash::engine(); - engine.input(&block); - engine.input(&msg_block); - - hashes::sha256::Hash::from_engine(engine) + hashes::sha256::Midstate::hash_tag(tag) + .to_engine() + .with_input(&msg_block) + .finalize() } assert_eq!( diff --git a/simplicity-sys/Cargo.toml b/simplicity-sys/Cargo.toml index 027f6c6b..6b49d5bd 100644 --- a/simplicity-sys/Cargo.toml +++ b/simplicity-sys/Cargo.toml @@ -15,7 +15,7 @@ links = "rustsimplicity_0_7" cc = "1.0.83" [dependencies] -hashes = { package = "bitcoin_hashes", version = "0.14" } +hashes = { package = "bitcoin_hashes", version = "1.1.0" } [features] test-utils = [] diff --git a/simplicity-sys/depend/simplicity-HEAD-revision.txt b/simplicity-sys/depend/simplicity-HEAD-revision.txt index dc23e242..a64fdbc4 100644 --- a/simplicity-sys/depend/simplicity-HEAD-revision.txt +++ b/simplicity-sys/depend/simplicity-HEAD-revision.txt @@ -1,2 +1,2 @@ # This file has been automatically generated. -c6ab4dd8f17838506f92f0d829c40618168817cd +ba967b1a0d57e069c37cd4041b8bae53f12fd456 diff --git a/simplicity-sys/depend/simplicity/dag.c b/simplicity-sys/depend/simplicity/dag.c index 3ae3baf6..4c3e04b0 100644 --- a/simplicity-sys/depend/simplicity/dag.c +++ b/simplicity-sys/depend/simplicity/dag.c @@ -116,6 +116,7 @@ sha256_midstate rustsimplicity_0_7_computeWordCMR(const bitstring* value, size_t case 0: i = getBit(value, 0); break; case 1: i = 2 + ((1U * getBit(value, 0) << 1) | getBit(value, 1)); break; case 2: i = 6 + ((1U * getBit(value, 0) << 3) | (1U * getBit(value, 1) << 2) | (1U * getBit(value, 2) << 1) | getBit(value, 3)); break; + default: SIMPLICITY_UNREACHABLE; } memcpy(stack_ptr, &word_cmr[i], sizeof(uint32_t[8])); } else { @@ -174,7 +175,7 @@ void rustsimplicity_0_7_computeCommitmentMerkleRoot(dag_node* dag, const uint_fa case PAIR: memcpy(block + j, dag[dag[i].child[1]].cmr.s, sizeof(uint32_t[8])); j = 0; - /*@fallthrough@*/ + SIMPLICITY_FALLTHROUGH; case DISCONNECT: /* Only the first child is used in the CMR. */ case INJL: case INJR: @@ -182,6 +183,7 @@ void rustsimplicity_0_7_computeCommitmentMerkleRoot(dag_node* dag, const uint_fa case DROP: memcpy(block + j, dag[dag[i].child[0]].cmr.s, sizeof(uint32_t[8])); rustsimplicity_0_7_sha256_compression(dag[i].cmr.s, block); + SIMPLICITY_FALLTHROUGH; case IDEN: case UNIT: case WITNESS: @@ -224,13 +226,14 @@ static void computeIdentityHashRoots(sha256_midstate* ihr, const dag_node* dag, case DISCONNECT: memcpy(block + j, ihr[dag[i].child[1]].s, sizeof(uint32_t[8])); j = 0; - /*@fallthrough@*/ + SIMPLICITY_FALLTHROUGH; case INJL: case INJR: case TAKE: case DROP: memcpy(block + j, ihr[dag[i].child[0]].s, sizeof(uint32_t[8])); rustsimplicity_0_7_sha256_compression(ihr[i].s, block); + SIMPLICITY_FALLTHROUGH; case IDEN: case UNIT: case HIDDEN: @@ -420,6 +423,7 @@ simplicity_err rustsimplicity_0_7_verifyCanonicalOrder(dag_node* dag, const uint continue; } if (bottom == child) bottom++; + SIMPLICITY_FALLTHROUGH; case IDEN: case UNIT: case WITNESS: @@ -444,6 +448,7 @@ simplicity_err rustsimplicity_0_7_verifyCanonicalOrder(dag_node* dag, const uint continue; } if (bottom == child) bottom++; + SIMPLICITY_FALLTHROUGH; case INJL: case INJR: case TAKE: diff --git a/simplicity-sys/depend/simplicity/eval.c b/simplicity-sys/depend/simplicity/eval.c index b923971d..8a388c08 100644 --- a/simplicity-sys/depend/simplicity/eval.c +++ b/simplicity-sys/depend/simplicity/eval.c @@ -462,7 +462,7 @@ static simplicity_err runTCO(evalState state, call* stack, const dag_node* dag, skip(state.activeWriteFrame, pad( INJR == dag[pc].tag , type_dag[INJ_B(dag, type_dag, pc)].bitSize , type_dag[INJ_C(dag, type_dag, pc)].bitSize)); - /*@fallthrough@*/ + SIMPLICITY_FALLTHROUGH; case TAKE: rustsimplicity_0_7_debug_assert(calling); /* TAIL_CALL(dag[pc].child[0], SAME_TCO); */ @@ -496,7 +496,7 @@ static simplicity_err runTCO(evalState state, call* stack, const dag_node* dag, } else { writeValue(state.activeWriteFrame, &dag[pc].compactValue, dag[pc].targetType, type_dag); } - /*@fallthrough@*/ + SIMPLICITY_FALLTHROUGH; case UNIT: rustsimplicity_0_7_debug_assert(calling); if (get_tco_flag(&stack[pc])) { diff --git a/simplicity-sys/depend/simplicity/simplicity_assert.h b/simplicity-sys/depend/simplicity/simplicity_assert.h index 57e9fba8..a0339af3 100644 --- a/simplicity-sys/depend/simplicity/simplicity_assert.h +++ b/simplicity-sys/depend/simplicity/simplicity_assert.h @@ -34,4 +34,18 @@ # define SIMPLICITY_UNREACHABLE assert(NULL == "SIMPLICITY_UNCREACHABLE was reached") #endif +/* Defines a FALLTHROUGH macro to annotate intentional switch fallthroughs, silencing -Wimplicit-fallthrough + * warnings on compilers that support the 'fallthrough' attribute. + * No-op on compilers (e.g. MSVC) that don't support it. + */ +#if defined(__has_attribute) +# if __has_attribute(fallthrough) +# define SIMPLICITY_FALLTHROUGH __attribute__((fallthrough)) +# endif +#endif + +#ifndef SIMPLICITY_FALLTHROUGH +# define SIMPLICITY_FALLTHROUGH ((void)0) +#endif + #endif /* SIMPLICITY_SIMPLICITY_ASSERT_H */ diff --git a/simplicity-sys/src/c_jets/c_env/elements.rs b/simplicity-sys/src/c_jets/c_env/elements.rs index 08d22450..90e62602 100644 --- a/simplicity-sys/src/c_jets/c_env/elements.rs +++ b/simplicity-sys/src/c_jets/c_env/elements.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: CC0-1.0 -use hashes::{sha256, Hash}; +use hashes::sha256; use crate::ffi::sha256::CSha256Midstate; use crate::ffi::{c_size_t, c_uchar, c_uint, c_uint_fast32_t}; @@ -148,8 +148,11 @@ extern "C" { } impl CTxEnv { pub fn sighash_all(&self) -> sha256::Hash { + // This is a little bit sketchy, directly interpreting a midstate as a sha256 hash. Bit + // it is okay because sighash data is public and unambiguously encoded, so there is no + // risk of length-extension attacks. let midstate: sha256::Midstate = self.sighash_all.into(); - sha256::Hash::from_byte_array(midstate.to_byte_array()) + sha256::Hash::from_byte_array(midstate.to_parts().0) } } diff --git a/simplicity-sys/src/ffi.rs b/simplicity-sys/src/ffi.rs index 1cdbcf03..06216c46 100644 --- a/simplicity-sys/src/ffi.rs +++ b/simplicity-sys/src/ffi.rs @@ -123,14 +123,14 @@ pub mod sha256 { for (idx, chunk) in c_midstate.s.iter().enumerate() { inner[idx * 4..(idx + 1) * 4].copy_from_slice(&chunk.to_be_bytes()); } - Midstate(inner) + Midstate::new(inner, 0) } } impl From for CSha256Midstate { fn from(midstate: Midstate) -> CSha256Midstate { let mut s = [0; 8]; - for (idx, chunk) in midstate.0.chunks(4).enumerate() { + for (idx, chunk) in midstate.to_parts().0.chunks(4).enumerate() { s[idx] = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]); } CSha256Midstate { s } diff --git a/src/jet/elements/c_env.rs b/src/jet/elements/c_env.rs index 8a00bb95..fc1b534c 100644 --- a/src/jet/elements/c_env.rs +++ b/src/jet/elements/c_env.rs @@ -4,7 +4,7 @@ //! use bitcoin::taproot::TAPROOT_ANNEX_PREFIX; -use hashes::Hash; +use elements::hashes::Hash as _; use std::os::raw::c_uchar; use elements::{ diff --git a/src/jet/elements/environment.rs b/src/jet/elements/environment.rs index fc3a9433..fb5db4ca 100644 --- a/src/jet/elements/environment.rs +++ b/src/jet/elements/environment.rs @@ -118,8 +118,8 @@ impl ElementsEnv> { /// Return a dummy Elements environment with given locktime pub fn dummy_with(lock_time: elements::LockTime, sequence: elements::Sequence) -> Self { + use elements::hashes::Hash as _; use elements::AssetIssuance; - use hashes::Hash; let ctrl_blk: [u8; 33] = [ 0xc0, 0xeb, 0x04, 0xb6, 0x8e, 0x9a, 0x26, 0xd1, 0x16, 0x04, 0x6c, 0x76, 0xe8, 0xff, diff --git a/src/jet/elements/tests.rs b/src/jet/elements/tests.rs index 42d72fcd..936f3084 100644 --- a/src/jet/elements/tests.rs +++ b/src/jet/elements/tests.rs @@ -7,14 +7,14 @@ use crate::jet::Elements; use crate::node::{ConstructNode, JetConstructible}; use crate::types; use crate::{BitMachine, Cmr, Value}; +use elements::hashes::sha256::Midstate; +use elements::hashes::Hash as _; use elements::secp256k1_zkp::Tweak; use elements::taproot::ControlBlock; use elements::{ confidential, AssetId, AssetIssuance, BlockHash, OutPoint, Sequence, Transaction, TxIn, TxInWitness, TxOut, TxOutWitness, }; -use hashes::sha256::Midstate; -use hashes::Hash; #[test] fn test_ffi_env() { @@ -110,6 +110,6 @@ fn test_ffi_env() { } fn hex_script(s: &str) -> elements::Script { - let v: Vec = hashes::hex::FromHex::from_hex(s).unwrap(); + let v = hex::decode_to_vec(s).unwrap(); elements::Script::from(v) } diff --git a/src/jet/init/bitcoin.rs b/src/jet/init/bitcoin.rs index d6499806..c93f7cfb 100644 --- a/src/jet/init/bitcoin.rs +++ b/src/jet/init/bitcoin.rs @@ -6,7 +6,6 @@ use crate::merkle::cmr::Cmr; use crate::decode_bits; use crate::{decode, BitIter, BitWriter}; use crate::analysis::Cost; -use hashes::sha256::Midstate; use simplicity_sys::CFrameItem; use std::io::Write; use std::{fmt, str}; diff --git a/src/jet/init/core.rs b/src/jet/init/core.rs index 785c81ab..06641d54 100644 --- a/src/jet/init/core.rs +++ b/src/jet/init/core.rs @@ -6,7 +6,6 @@ use crate::merkle::cmr::Cmr; use crate::decode_bits; use crate::{decode, BitIter, BitWriter}; use crate::analysis::Cost; -use hashes::sha256::Midstate; use simplicity_sys::CFrameItem; use std::io::Write; use std::{fmt, str}; @@ -2604,7 +2603,7 @@ impl Jet for Core { ], }; - Cmr(Midstate(bytes)) + Cmr::from_byte_array(bytes) } fn source_ty(&self) -> TypeName { diff --git a/src/jet/init/elements.rs b/src/jet/init/elements.rs index 18c2bf9e..0b578cff 100644 --- a/src/jet/init/elements.rs +++ b/src/jet/init/elements.rs @@ -6,7 +6,6 @@ use crate::merkle::cmr::Cmr; use crate::decode_bits; use crate::{decode, BitIter, BitWriter}; use crate::analysis::Cost; -use hashes::sha256::Midstate; use simplicity_sys::CFrameItem; use std::io::Write; use std::{fmt, str}; @@ -3326,7 +3325,7 @@ impl Jet for Elements { ], }; - Cmr(Midstate(bytes)) + Cmr::from_byte_array(bytes) } fn source_ty(&self) -> TypeName { diff --git a/src/lib.rs b/src/lib.rs index e2830f69..56287773 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -179,7 +179,7 @@ pub enum ParseError { #[cfg(feature = "base64")] Base64(base64::DecodeError), /// Hex decoding error - Hex(hex::error::HexToBytesError), + Hex(hex::error::DecodeVariableLengthBytesError), } impl fmt::Display for ParseError { diff --git a/src/merkle/amr.rs b/src/merkle/amr.rs index 32b04caa..2d07538d 100644 --- a/src/merkle/amr.rs +++ b/src/merkle/amr.rs @@ -1,6 +1,5 @@ // SPDX-License-Identifier: CC0-1.0 -use crate::impl_midstate_wrapper; use crate::jet::Jet; use crate::merkle::compact_value; use crate::types::arrow::FinalArrow; @@ -8,18 +7,18 @@ use crate::value::Word; use crate::{Cmr, Tmr, Value}; use hashes::sha256::Midstate; -use super::FailEntropy; +use super::{impl_mr_type, FailEntropy, MidstateExt as _}; -/// Annotated Merkle root -/// -/// A Merkle root that commits to a node's combinator, its source and target type, -/// its witness data (if present), and recursively its children. -/// -/// Uniquely identifies a program's structure in terms of types at redemption time. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct Amr(Midstate); - -impl_midstate_wrapper!(Amr); +impl_mr_type! { + /// Annotated Merkle root + /// + /// A Merkle root that commits to a node's combinator, its source and target type, + /// its witness data (if present), and recursively its children. + /// + /// Uniquely identifies a program's structure in terms of types at redemption time. + #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] + pub struct Amr([u8; 32]); +} impl From for Amr { fn from(cmr: Cmr) -> Self { @@ -37,13 +36,13 @@ impl Amr { /// Produce a CMR for an iden combinator pub fn iden(ty: &FinalArrow) -> Self { let a = &ty.source; - Self::IDEN_IV.update_1(a.tmr().into()) + Self::IDEN_IV.update_0_then_32(a.tmr()).into_merkle_root() } /// Produce a CMR for a unit combinator pub fn unit(ty: &FinalArrow) -> Self { let a = &ty.source; - Self::UNIT_IV.update_1(a.tmr().into()) + Self::UNIT_IV.update_0_then_32(a.tmr()).into_merkle_root() } /// Produce a CMR for an injl combinator @@ -51,8 +50,9 @@ impl Amr { let a = &ty.source; let (b, c) = ty.target.as_sum().unwrap(); Self::INJL_IV - .update(a.tmr().into(), b.tmr().into()) - .update(c.tmr().into(), child) + .zz_update_2x32(a.tmr(), b.tmr()) + .zz_update_2x32(c.tmr(), child) + .into_merkle_root() } /// Produce a CMR for an injr combinator @@ -60,8 +60,9 @@ impl Amr { let a = &ty.source; let (b, c) = ty.target.as_sum().unwrap(); Self::INJR_IV - .update(a.tmr().into(), b.tmr().into()) - .update(c.tmr().into(), child) + .zz_update_2x32(a.tmr(), b.tmr()) + .zz_update_2x32(c.tmr(), child) + .into_merkle_root() } /// Produce a CMR for a take combinator @@ -69,8 +70,9 @@ impl Amr { let (a, b) = ty.source.as_product().unwrap(); let c = &ty.target; Self::TAKE_IV - .update(a.tmr().into(), b.tmr().into()) - .update(c.tmr().into(), child) + .zz_update_2x32(a.tmr(), b.tmr()) + .zz_update_2x32(c.tmr(), child) + .into_merkle_root() } /// Produce a CMR for a drop combinator @@ -78,8 +80,9 @@ impl Amr { let (a, b) = ty.source.as_product().unwrap(); let c = &ty.target; Self::DROP_IV - .update(a.tmr().into(), b.tmr().into()) - .update(c.tmr().into(), child) + .zz_update_2x32(a.tmr(), b.tmr()) + .zz_update_2x32(c.tmr(), child) + .into_merkle_root() } /// Produce a CMR for a comp combinator @@ -88,18 +91,20 @@ impl Amr { let b = &left_arrow.target; let c = &ty.target; Self::COMP_IV - .update_1(a.tmr().into()) - .update(b.tmr().into(), c.tmr().into()) - .update(left, right) + .update_0_then_32(a.tmr()) + .zz_update_2x32(b.tmr(), c.tmr()) + .zz_update_2x32(left, right) + .into_merkle_root() } - fn case_helper(iv: Amr, ty: &FinalArrow, left: Amr, right: Amr) -> Self { + fn case_helper(iv: Midstate, ty: &FinalArrow, left: Amr, right: Amr) -> Self { let (sum_a_b, c) = ty.source.as_product().unwrap(); let (a, b) = sum_a_b.as_sum().unwrap(); let d = &ty.target; - iv.update(a.tmr().into(), b.tmr().into()) - .update(c.tmr().into(), d.tmr().into()) - .update(left, right) + iv.zz_update_2x32(a.tmr(), b.tmr()) + .zz_update_2x32(c.tmr(), d.tmr()) + .zz_update_2x32(left, right) + .into_merkle_root() } /// Produce a CMR for a case combinator @@ -129,9 +134,10 @@ impl Amr { let b = &left_arrow.target; let c = &right_arrow.target; Self::PAIR_IV - .update_1(a.tmr().into()) - .update(b.tmr().into(), c.tmr().into()) - .update(left, right) + .update_0_then_32(a.tmr()) + .zz_update_2x32(b.tmr(), c.tmr()) + .zz_update_2x32(left, right) + .into_merkle_root() } /// Produce a CMR for a disconnect combinator @@ -140,9 +146,10 @@ impl Amr { let (b, d) = ty.target.as_product().unwrap(); let c = &right_arrow.source; Self::DISCONNECT_IV - .update(a.tmr().into(), b.tmr().into()) - .update(c.tmr().into(), d.tmr().into()) - .update(left, right) + .zz_update_2x32(a.tmr(), b.tmr()) + .zz_update_2x32(c.tmr(), d.tmr()) + .zz_update_2x32(left, right) + .into_merkle_root() } /// Produce a CMR for a witness combinator @@ -150,13 +157,16 @@ impl Amr { let a = &ty.source; let b = &ty.target; Self::WITNESS_IV - .update_1(a.tmr().into()) - .update(b.tmr().into(), Amr::from_byte_array(compact_value(value))) + .update_0_then_32(a.tmr()) + .zz_update_2x32(b.tmr(), compact_value(value)) + .into_merkle_root() } /// Produce an AMR for a fail combinator pub fn fail(entropy: FailEntropy) -> Self { - Self::FAIL_IV.update_fail_entropy(entropy) + Self::FAIL_IV + .zz_update_64(entropy.to_byte_array()) + .into_merkle_root() } /// Produce a CMR for a jet @@ -173,116 +183,116 @@ impl Amr { } #[rustfmt::skip] - const IDEN_IV: Amr = Amr(Midstate([ + const IDEN_IV: Midstate = Midstate::new([ 0x45, 0x1f, 0x68, 0x8c, 0x41, 0x50, 0x99, 0x68, 0x3a, 0x8d, 0x36, 0xa7, 0x09, 0xba, 0x42, 0x49, 0xe6, 0xde, 0xf7, 0x1b, 0x35, 0x29, 0xb4, 0xc1, 0xf2, 0xe4, 0xd5, 0x63, 0x85, 0x54, 0x91, 0xaf, - ])); + ], 64); #[rustfmt::skip] - const UNIT_IV: Amr = Amr(Midstate([ + const UNIT_IV: Midstate = Midstate::new([ 0xdb, 0xfe, 0xe7, 0x84, 0x3b, 0x59, 0x1e, 0x8f, 0x7c, 0xe4, 0x85, 0xdc, 0xb5, 0xd6, 0x6b, 0xcc, 0x23, 0x98, 0x48, 0x10, 0xbd, 0x6b, 0xe5, 0x2a, 0xa7, 0x6e, 0xaf, 0x2b, 0x25, 0xcb, 0x44, 0x23, - ])); + ], 64); #[rustfmt::skip] - const INJL_IV: Amr = Amr(Midstate([ + const INJL_IV: Midstate = Midstate::new([ 0xba, 0xcb, 0x63, 0x56, 0x69, 0x80, 0xdc, 0xac, 0x45, 0x59, 0x1f, 0x5a, 0xef, 0x0d, 0x91, 0xd4, 0x9e, 0xa4, 0xc1, 0xe0, 0x07, 0x2b, 0x1c, 0xe3, 0xdd, 0x20, 0x75, 0x21, 0xc9, 0xea, 0xd8, 0x08, - ])); + ], 64); #[rustfmt::skip] - const INJR_IV: Amr = Amr(Midstate([ + const INJR_IV: Midstate = Midstate::new([ 0x7b, 0x11, 0x6e, 0xbc, 0x3f, 0x90, 0x9e, 0xef, 0x13, 0x87, 0x98, 0x71, 0x99, 0x84, 0x4b, 0x91, 0xbc, 0xc7, 0x40, 0x2c, 0x24, 0x30, 0xc4, 0x85, 0x29, 0x75, 0xd4, 0x8f, 0x49, 0x0c, 0x54, 0x7a, - ])); + ], 64); #[rustfmt::skip] - const TAKE_IV: Amr = Amr(Midstate([ + const TAKE_IV: Midstate = Midstate::new([ 0x41, 0x75, 0xa1, 0x34, 0xe6, 0xb4, 0xd1, 0x5b, 0xb4, 0x8a, 0xc5, 0x82, 0xd9, 0xbc, 0x83, 0xa2, 0x94, 0x84, 0xc7, 0xec, 0x27, 0x00, 0x4d, 0xff, 0x94, 0xaa, 0xe4, 0xcb, 0x5d, 0x87, 0x6f, 0xb6, - ])); + ], 64); #[rustfmt::skip] - const DROP_IV: Amr = Amr(Midstate([ + const DROP_IV: Midstate = Midstate::new([ 0x3b, 0x32, 0x93, 0xab, 0xbc, 0xc9, 0xb9, 0xdb, 0xe1, 0xbd, 0x70, 0xa3, 0x31, 0x5f, 0x38, 0xff, 0x26, 0x05, 0xfb, 0x30, 0xe3, 0x1f, 0x3c, 0xee, 0x7b, 0x25, 0x1a, 0x88, 0x31, 0xc1, 0x56, 0x89, - ])); + ], 64); #[rustfmt::skip] - const COMP_IV: Amr = Amr(Midstate([ + const COMP_IV: Midstate = Midstate::new([ 0x73, 0x7f, 0xd0, 0x53, 0x08, 0xd6, 0x05, 0x00, 0x3b, 0xde, 0x8e, 0xb2, 0x0a, 0x3b, 0x93, 0x97, 0xc1, 0xda, 0xaa, 0xe5, 0x43, 0xd8, 0xa4, 0xe4, 0xfe, 0xef, 0x48, 0x38, 0x76, 0x85, 0x93, 0x84, - ])); + ], 64); #[rustfmt::skip] - const CASE_IV: Amr = Amr(Midstate([ + const CASE_IV: Midstate = Midstate::new([ 0x59, 0x58, 0x14, 0xf9, 0xe0, 0xc8, 0x89, 0xb4, 0x84, 0x7a, 0xba, 0x1d, 0x51, 0xe1, 0x4e, 0x04, 0x8f, 0x95, 0x34, 0x4b, 0x2a, 0x43, 0x20, 0x91, 0xf8, 0x1b, 0xd3, 0xb8, 0x36, 0xec, 0x35, 0x04, - ])); + ], 64); #[rustfmt::skip] - const ASSERTL_IV: Amr = Amr(Midstate([ + const ASSERTL_IV: Midstate = Midstate::new([ 0xd5, 0xdd, 0xd7, 0x41, 0xd7, 0x27, 0xe2, 0x20, 0x35, 0x56, 0xb0, 0xf8, 0xba, 0x4f, 0x7f, 0xd0, 0xb4, 0x8f, 0x8d, 0xbf, 0x7a, 0x85, 0x84, 0x87, 0x9b, 0xab, 0xa8, 0xd6, 0xec, 0xaa, 0x43, 0xef, - ])); + ], 64); #[rustfmt::skip] - const ASSERTR_IV: Amr = Amr(Midstate([ + const ASSERTR_IV: Midstate = Midstate::new([ 0xc5, 0xfe, 0xcd, 0x9c, 0xad, 0xc1, 0x6a, 0x7a, 0x08, 0xcf, 0x21, 0x5a, 0xe7, 0x58, 0x40, 0xf6, 0xd2, 0x05, 0xe7, 0xaa, 0x27, 0xf6, 0xff, 0xf2, 0x6b, 0xfa, 0x1e, 0x6b, 0x3a, 0x0f, 0x76, 0xfa, - ])); + ], 64); #[rustfmt::skip] - const PAIR_IV: Amr = Amr(Midstate([ + const PAIR_IV: Midstate = Midstate::new([ 0x79, 0x96, 0xb3, 0xbf, 0xa6, 0x03, 0x36, 0x73, 0x25, 0x15, 0xa1, 0x24, 0x3f, 0x6b, 0x3c, 0x23, 0x6b, 0xff, 0x0e, 0xb0, 0x66, 0x2f, 0x8a, 0xdd, 0x54, 0xaf, 0x0c, 0x98, 0xd1, 0xfd, 0xdd, 0xa7, - ])); + ], 64); #[rustfmt::skip] - const DISCONNECT_IV: Amr = Amr(Midstate([ + const DISCONNECT_IV: Midstate = Midstate::new([ 0x27, 0xeb, 0xbc, 0xaa, 0x5a, 0x53, 0x7c, 0x25, 0xd8, 0xbb, 0x43, 0x40, 0x37, 0xbb, 0x2f, 0xff, 0x6b, 0x88, 0x15, 0x35, 0x43, 0x5e, 0x6d, 0x60, 0xa5, 0xf8, 0x5c, 0xd0, 0xf0, 0x5a, 0x61, 0x47, - ])); + ], 64); #[rustfmt::skip] - const WITNESS_IV: Amr = Amr(Midstate([ + const WITNESS_IV: Midstate = Midstate::new([ 0x6b, 0xc6, 0x20, 0xcb, 0x52, 0xf9, 0x0c, 0x63, 0x0e, 0xf8, 0xc8, 0x0d, 0x5f, 0x69, 0x0d, 0xb4, 0xe9, 0x93, 0x0a, 0xd0, 0xfa, 0x70, 0x71, 0x36, 0x5b, 0x5c, 0x93, 0xe0, 0x39, 0x1b, 0xd7, 0x96, - ])); + ], 64); #[rustfmt::skip] - const FAIL_IV: Amr = Amr(Midstate([ + const FAIL_IV: Midstate = Midstate::new([ 0x36, 0x9c, 0xdd, 0x29, 0x3f, 0xd5, 0x8a, 0x85, 0xa5, 0x90, 0x38, 0x0f, 0x12, 0x20, 0x7e, 0x18, 0x10, 0x02, 0x0d, 0x39, 0x1c, 0x46, 0xd1, 0xab, 0xbe, 0xb2, 0x12, 0x2e, 0xa2, 0xfd, 0x55, 0xec, - ])); + ], 64); } #[cfg(test)] @@ -312,23 +322,23 @@ mod tests { #[test] fn ivs() { - fn check_iv(target: Amr, s: &'static str) { + fn check_iv(target: Midstate, s: &'static str) { let name = s.trim_start_matches("Simplicity\x1fAnnotated\x1f"); // Uncomment this if the IVs ever change /* let target = Amr(bip340_iv(s.as_bytes())); println!(" #[rustfmt::skip]"); - println!(" const {}_IV: Amr = Amr(Midstate([", name.to_ascii_uppercase()); + println!(" const {}_IV: Midstate = Midstate::new([", name.to_ascii_uppercase()); print!(" "); for ch in &target.0[0..8] { print!(" 0x{:02x},", ch); }; println!(); print!(" "); for ch in &target.0[8..16] { print!(" 0x{:02x},", ch); }; println!(); print!(" "); for ch in &target.0[16..24] { print!(" 0x{:02x},", ch); }; println!(); print!(" "); for ch in &target.0[24..32] { print!(" 0x{:02x},", ch); }; println!(); - println!(" ]));"); + println!(" ], 64);"); println!(); */ assert_eq!( target, - Amr(bip340_iv(s.as_bytes())), + bip340_iv(s.as_bytes()), "mismatch on IV for {}", name ); diff --git a/src/merkle/cmr.rs b/src/merkle/cmr.rs index 525d1758..8aebca84 100644 --- a/src/merkle/cmr.rs +++ b/src/merkle/cmr.rs @@ -1,6 +1,5 @@ // SPDX-License-Identifier: CC0-1.0 -use crate::impl_midstate_wrapper; use crate::jet::Jet; use crate::node::{ CoreConstructible, DisconnectConstructible, JetConstructible, WitnessConstructible, @@ -10,20 +9,20 @@ use crate::value::Word; use crate::{FailEntropy, Tmr}; use hashes::sha256::Midstate; -use super::bip340_iv; +use super::{bip340_iv, impl_mr_type, MidstateExt as _}; -/// Commitment Merkle root -/// -/// A Merkle root that commits to a node's combinator and recursively its children. -/// -/// Importantly, witness data and right disconnect branches are _not_ included in the hash. -/// This makes these elements malleable while preserving program identity (SegWit, delegation). -/// -/// Uniquely identifies a program's structure in terms of combinators at commitment time. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct Cmr(pub(crate) Midstate); - -impl_midstate_wrapper!(Cmr); +impl_mr_type! { + /// Commitment Merkle root + /// + /// A Merkle root that commits to a node's combinator and recursively its children. + /// + /// Importantly, witness data and right disconnect branches are _not_ included in the hash. + /// This makes these elements malleable while preserving program identity (SegWit, delegation). + /// + /// Uniquely identifies a program's structure in terms of combinators at commitment time. + #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] + pub struct Cmr([u8; 32]); +} impl From for Cmr { fn from(tmr: Tmr) -> Self { @@ -33,63 +32,67 @@ impl From for Cmr { impl Cmr { /// Produce a CMR for an iden combinator - pub const fn iden() -> Self { - Self::IDEN_IV + pub fn iden() -> Self { + Self::IDEN_IV.into_merkle_root() } /// Produce a CMR for a unit combinator - pub const fn unit() -> Self { - Self::UNIT_IV + pub fn unit() -> Self { + Self::UNIT_IV.into_merkle_root() } /// Produce a CMR for an injl combinator pub fn injl(child: Cmr) -> Self { - Self::INJL_IV.update_1(child) + Self::INJL_IV.update_0_then_32(child).into_merkle_root() } /// Produce a CMR for an injr combinator pub fn injr(child: Cmr) -> Self { - Self::INJR_IV.update_1(child) + Self::INJR_IV.update_0_then_32(child).into_merkle_root() } /// Produce a CMR for a take combinator pub fn take(child: Cmr) -> Self { - Self::TAKE_IV.update_1(child) + Self::TAKE_IV.update_0_then_32(child).into_merkle_root() } /// Produce a CMR for a drop combinator pub fn drop(child: Cmr) -> Self { - Self::DROP_IV.update_1(child) + Self::DROP_IV.update_0_then_32(child).into_merkle_root() } /// Produce a CMR for a comp combinator pub fn comp(left: Cmr, right: Cmr) -> Self { - Self::COMP_IV.update(left, right) + Self::COMP_IV.zz_update_2x32(left, right).into_merkle_root() } /// Produce a CMR for a case combinator pub fn case(left: Cmr, right: Cmr) -> Self { - Self::CASE_IV.update(left, right) + Self::CASE_IV.zz_update_2x32(left, right).into_merkle_root() } /// Produce a CMR for a pair combinator pub fn pair(left: Cmr, right: Cmr) -> Self { - Self::PAIR_IV.update(left, right) + Self::PAIR_IV.zz_update_2x32(left, right).into_merkle_root() } /// Produce a CMR for a disconnect combinator pub fn disconnect(left: Cmr) -> Self { - Self::DISCONNECT_IV.update_1(left) + Self::DISCONNECT_IV + .update_0_then_32(left) + .into_merkle_root() } /// Produce a CMR for a witness combinator pub fn witness() -> Self { - Self::WITNESS_IV + Self::WITNESS_IV.into_merkle_root() } /// Produce a CMR for a fail combinator pub fn fail(entropy: FailEntropy) -> Self { - Self::FAIL_IV.update_fail_entropy(entropy) + Self::FAIL_IV + .zz_update_64(entropy.to_byte_array()) + .into_merkle_root() } /// Produce a CMR for a jet @@ -112,139 +115,141 @@ impl Cmr { while j & 1 == 1 { let right_cmr = cmr_stack.pop().unwrap(); let left_cmr = cmr_stack.pop().unwrap(); - cmr_stack.push(Cmr::PAIR_IV.update(left_cmr, right_cmr)); + cmr_stack.push(Cmr::pair(left_cmr, right_cmr)); j >>= 1; } } assert_eq!(cmr_stack.len(), 1); let ihr_iv = Self::CONST_WORD_IV; - let ihr_pass1 = ihr_iv.update_1(cmr_stack[0]); + let ihr_pass1 = ihr_iv.update_0_then_32(cmr_stack[0]); // 2. Add TMRs to get the pass-two IHR - let ihr_pass2 = ihr_pass1.update(Tmr::unit().into(), Tmr::TWO_TWO_N[w - 1].into()); + let ihr_pass2 = ihr_pass1.zz_update_2x32(Tmr::unit(), Tmr::TWO_TWO_N[w - 1]); // 3. Convert to a jet CMR - Cmr(bip340_iv(b"Simplicity\x1fJet")).update_with_weight(word.len() as u64, ihr_pass2) + bip340_iv(b"Simplicity\x1fJet") + .update_weight_then_32(word.len() as u64, ihr_pass2.to_parts().0) + .into_merkle_root() } #[rustfmt::skip] - const UNIT_IV: Cmr = Cmr(Midstate([ + const UNIT_IV: Midstate = Midstate::new([ 0xc4, 0x0a, 0x10, 0x26, 0x3f, 0x74, 0x36, 0xb4, 0x16, 0x0a, 0xcb, 0xef, 0x1c, 0x36, 0xfb, 0xa4, 0xbe, 0x4d, 0x95, 0xdf, 0x18, 0x1a, 0x96, 0x8a, 0xfe, 0xab, 0x5e, 0xac, 0x24, 0x7a, 0xdf, 0xf7, - ])); + ], 64); #[rustfmt::skip] - const IDEN_IV: Cmr = Cmr(Midstate([ + const IDEN_IV: Midstate = Midstate::new([ 0x54, 0x1a, 0x1a, 0x69, 0xbd, 0x4b, 0xcb, 0xda, 0x7f, 0x34, 0x31, 0x0e, 0x30, 0x78, 0xf7, 0x26, 0x44, 0x31, 0x22, 0xfb, 0xcc, 0x1c, 0xb5, 0x36, 0x0c, 0x78, 0x64, 0xec, 0x0d, 0x32, 0x3a, 0xc0, - ])); + ], 64); #[rustfmt::skip] - const INJL_IV: Cmr = Cmr(Midstate([ + const INJL_IV: Midstate = Midstate::new([ 0x54, 0xe9, 0x1d, 0x18, 0xd8, 0xf8, 0x1f, 0x6d, 0x29, 0x86, 0xbb, 0x58, 0x47, 0x9a, 0x54, 0xeb, 0x63, 0x0e, 0x95, 0x23, 0xb6, 0x9e, 0xe8, 0x53, 0x29, 0x80, 0xd0, 0x55, 0x58, 0x19, 0x4f, 0x15, - ])); + ], 64); #[rustfmt::skip] - const INJR_IV: Cmr = Cmr(Midstate([ + const INJR_IV: Midstate = Midstate::new([ 0xd7, 0x0f, 0xfd, 0xce, 0x97, 0x77, 0x7b, 0x4d, 0xfe, 0x31, 0xfd, 0x9f, 0xf5, 0xd0, 0x17, 0xa6, 0x30, 0x5d, 0x7e, 0xc6, 0x0d, 0xf3, 0xb1, 0xbf, 0x6d, 0x25, 0xe8, 0x16, 0x33, 0xde, 0xd4, 0xbf, - ])); + ], 64); #[rustfmt::skip] - const TAKE_IV: Cmr = Cmr(Midstate([ + const TAKE_IV: Midstate = Midstate::new([ 0x50, 0x5f, 0xc0, 0x81, 0xb5, 0xba, 0x2a, 0xcd, 0x09, 0x50, 0x67, 0xc3, 0xdf, 0xb8, 0xea, 0x12, 0x6f, 0xa1, 0x5d, 0x55, 0xcb, 0x21, 0x1e, 0x6a, 0xed, 0x34, 0xe8, 0xd1, 0xe3, 0x7a, 0xf0, 0xfa, - ])); + ], 64); #[rustfmt::skip] - const DROP_IV: Cmr = Cmr(Midstate([ + const DROP_IV: Midstate = Midstate::new([ 0x8a, 0x30, 0x8d, 0x38, 0xa1, 0x13, 0xa2, 0x60, 0xb4, 0xc7, 0x14, 0x5a, 0xbd, 0xc5, 0x22, 0x4d, 0xeb, 0x70, 0x13, 0x79, 0x59, 0x0e, 0x0c, 0x8c, 0x38, 0x86, 0x0b, 0xab, 0x12, 0x71, 0xa8, 0xa8, - ])); + ], 64); #[rustfmt::skip] - const COMP_IV: Cmr = Cmr(Midstate([ + const COMP_IV: Midstate = Midstate::new([ 0x57, 0xec, 0x23, 0xa2, 0xa4, 0x77, 0x8e, 0x01, 0x58, 0xa6, 0x21, 0x7a, 0xea, 0x3e, 0xf7, 0x42, 0x8b, 0xa0, 0x90, 0x92, 0x73, 0xb9, 0x73, 0xfa, 0x14, 0x32, 0xa9, 0x27, 0x84, 0x3e, 0x92, 0x7a, - ])); + ], 64); #[rustfmt::skip] - const CASE_IV: Cmr = Cmr(Midstate([ + const CASE_IV: Midstate = Midstate::new([ 0x29, 0x5e, 0x2a, 0x6d, 0xc8, 0xc5, 0xce, 0x59, 0xe4, 0xed, 0xcf, 0xe9, 0xb4, 0xd8, 0xf7, 0x64, 0x13, 0x3a, 0xa5, 0x51, 0x4b, 0xd3, 0xee, 0x8b, 0x4b, 0x75, 0xec, 0x8f, 0x4d, 0xeb, 0x08, 0xbe, - ])); + ], 64); #[rustfmt::skip] - const PAIR_IV: Cmr = Cmr(Midstate([ + const PAIR_IV: Midstate = Midstate::new([ 0x7d, 0x5e, 0x6d, 0xac, 0x15, 0xb1, 0x42, 0x8a, 0x0d, 0x26, 0x0c, 0x94, 0x29, 0xdb, 0xe8, 0x89, 0x65, 0x93, 0xf3, 0x1f, 0x70, 0x86, 0x27, 0xee, 0x75, 0xb2, 0x7e, 0xee, 0xfd, 0xd0, 0x50, 0x05, - ])); + ], 64); #[rustfmt::skip] - const DISCONNECT_IV: Cmr = Cmr(Midstate([ + const DISCONNECT_IV: Midstate = Midstate::new([ 0x35, 0x33, 0x8b, 0x5b, 0x81, 0x74, 0x0c, 0x6d, 0x67, 0xdc, 0x1e, 0xa3, 0xc8, 0x31, 0xe4, 0xc0, 0xaf, 0xd8, 0x64, 0x09, 0xbc, 0x04, 0xd0, 0xdd, 0x43, 0x24, 0xb7, 0xd9, 0xd5, 0x83, 0xf4, 0xeb, - ])); + ], 64); #[rustfmt::skip] - const WITNESS_IV: Cmr = Cmr(Midstate([ + const WITNESS_IV: Midstate = Midstate::new([ 0xa0, 0xfc, 0x8d, 0xeb, 0xd6, 0x79, 0x69, 0x17, 0xc8, 0x6b, 0x77, 0xad, 0xed, 0x82, 0xe6, 0xc6, 0x16, 0x49, 0x88, 0x9a, 0xe8, 0xf2, 0xed, 0x65, 0xb5, 0x7b, 0x41, 0xaa, 0x9d, 0x90, 0xe3, 0x75, - ])); + ], 64); #[rustfmt::skip] - const FAIL_IV: Cmr = Cmr(Midstate([ + const FAIL_IV: Midstate = Midstate::new([ 0x22, 0x83, 0xc1, 0x81, 0x9e, 0x69, 0x2f, 0x96, 0x85, 0xfe, 0x95, 0x40, 0x76, 0xc5, 0x16, 0x7c, 0x03, 0xbd, 0xe7, 0xcc, 0xda, 0xab, 0x00, 0x5e, 0x55, 0x36, 0x12, 0x2e, 0x18, 0xf7, 0x23, 0x7a, - ])); + ], 64); #[rustfmt::skip] - const CONST_WORD_IV: Cmr = Cmr(Midstate([ + const CONST_WORD_IV: Midstate = Midstate::new([ 0x0c, 0x5b, 0xc1, 0xce, 0xc8, 0xf1, 0x41, 0x85, 0x0e, 0x33, 0x3e, 0x28, 0xea, 0x01, 0xc0, 0x5a, 0xc6, 0x42, 0xeb, 0x30, 0xb4, 0x9e, 0x69, 0x2c, 0x56, 0xb1, 0x22, 0xbb, 0x69, 0x49, 0xc5, 0xcd, - ])); + ], 64); /// CMRs for the bits 0 and 1 -- injl(unit) and injr(unit) respectively #[rustfmt::skip] const BITS: [Cmr; 2] = [ - Cmr(Midstate([ + Cmr([ 0x88, 0x81, 0xaf, 0xf5, 0x16, 0x0c, 0xc0, 0xc9, 0xf8, 0xec, 0xea, 0xd8, 0xb4, 0x01, 0xfa, 0x97, 0xee, 0xf5, 0xfc, 0x60, 0x75, 0x2e, 0x98, 0xd2, 0x47, 0x56, 0x1a, 0x4d, 0xa6, 0xce, 0x96, 0x5e, - ])), - Cmr(Midstate([ + ]), + Cmr([ 0xa0, 0x43, 0x8b, 0x72, 0x36, 0x48, 0x72, 0x7b, 0x3f, 0x2d, 0x18, 0x5f, 0xcd, 0x95, 0x69, 0xe0, 0x22, 0xa4, 0x47, 0x8e, 0xb2, 0x5f, 0xdf, 0xa5, 0x38, 0xea, 0xc5, 0x9d, 0x81, 0x7c, 0x31, 0x1c, - ])), + ]), ]; } @@ -439,7 +444,7 @@ mod tests { #[test] fn ivs() { - fn check_iv(target: Cmr, s: &'static str) { + fn check_iv(target: Midstate, s: &'static str) { let name = s .trim_start_matches("Simplicity\x1f") .strip_prefix("Commitment\x1f") @@ -448,17 +453,17 @@ mod tests { /* let target = Cmr(bip340_iv(s.as_bytes())); println!(" #[rustfmt::skip]"); - println!(" const {}_IV: Cmr = Cmr(Midstate([", name.to_ascii_uppercase()); + println!(" const {}_IV: Midstate = Midstate::new([", name.to_ascii_uppercase()); print!(" "); for ch in &target.0[0..8] { print!(" 0x{:02x},", ch); }; println!(); print!(" "); for ch in &target.0[8..16] { print!(" 0x{:02x},", ch); }; println!(); print!(" "); for ch in &target.0[16..24] { print!(" 0x{:02x},", ch); }; println!(); print!(" "); for ch in &target.0[24..32] { print!(" 0x{:02x},", ch); }; println!(); - println!(" ]));"); + println!(" ], 64);"); println!(); */ assert_eq!( target, - Cmr(bip340_iv(s.as_bytes())), + bip340_iv(s.as_bytes()), "mismatch on IV for {}", name ); diff --git a/src/merkle/ihr.rs b/src/merkle/ihr.rs index e5eecbd6..bd5e8d04 100644 --- a/src/merkle/ihr.rs +++ b/src/merkle/ihr.rs @@ -1,34 +1,33 @@ // SPDX-License-Identifier: CC0-1.0 -use crate::impl_midstate_wrapper; use crate::jet::Jet; use crate::types::arrow::FinalArrow; use crate::value::Word; use crate::{Cmr, Tmr, Value}; use hashes::sha256::Midstate; -use super::{bip340_iv, compact_value, FailEntropy}; +use super::{bip340_iv, compact_value, impl_mr_type, FailEntropy, MidstateExt as _}; -/// Identity Merkle Root -/// -/// A Merkle root that commits to a node's combinator, its witness data (if present), -/// and recursively its children. Used as input to the [`Ihr`] type which is probably -/// actually what you want. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct Imr(Midstate); - -impl_midstate_wrapper!(Imr); - -/// Identity Hash Root -/// -/// A Merkle root that commits to a node's [`Imr`] (which recursively commits to its -/// childrens' [`Imr`]s) as well as its source and target types. -/// -/// Uniquely identifies a program's structure in terms of combinators at redemption time. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct Ihr(Midstate); +impl_mr_type! { + /// Identity Merkle Root + /// + /// A Merkle root that commits to a node's combinator, its witness data (if present), + /// and recursively its children. Used as input to the [`Ihr`] type which is probably + /// actually what you want. + #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] + pub struct Imr([u8; 32]); +} -impl_midstate_wrapper!(Ihr); +impl_mr_type! { + /// Identity Hash Root + /// + /// A Merkle root that commits to a node's [`Imr`] (which recursively commits to its + /// childrens' [`Imr`]s) as well as its source and target types. + /// + /// Uniquely identifies a program's structure in terms of combinators at redemption time. + #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] + pub struct Ihr([u8; 32]); +} impl From for Imr { fn from(cmr: Cmr) -> Self { @@ -50,71 +49,69 @@ impl From for Ihr { impl Imr { /// Produce a CMR for an iden combinator - pub const fn iden() -> Self { - Self::IDEN_IV + pub fn iden() -> Self { + Self::IDEN_IV.into_merkle_root() } /// Produce a CMR for a unit combinator - pub const fn unit() -> Self { - Self::UNIT_IV + pub fn unit() -> Self { + Self::UNIT_IV.into_merkle_root() } /// Produce a CMR for an injl combinator pub fn injl(child: Self) -> Self { - Self::INJL_IV.update_1(child) + Self::INJL_IV.update_0_then_32(child).into_merkle_root() } /// Produce a CMR for an injr combinator pub fn injr(child: Self) -> Self { - Self::INJR_IV.update_1(child) + Self::INJR_IV.update_0_then_32(child).into_merkle_root() } /// Produce a CMR for a take combinator pub fn take(child: Self) -> Self { - Self::TAKE_IV.update_1(child) + Self::TAKE_IV.update_0_then_32(child).into_merkle_root() } /// Produce a CMR for a drop combinator pub fn drop(child: Self) -> Self { - Self::DROP_IV.update_1(child) + Self::DROP_IV.update_0_then_32(child).into_merkle_root() } /// Produce a CMR for a comp combinator pub fn comp(left: Self, right: Self) -> Self { - Self::COMP_IV.update(left, right) + Self::COMP_IV.zz_update_2x32(left, right).into_merkle_root() } /// Produce a CMR for a case combinator pub fn case(left: Self, right: Self) -> Self { - Self::CASE_IV.update(left, right) + Self::CASE_IV.zz_update_2x32(left, right).into_merkle_root() } /// Produce a CMR for a pair combinator pub fn pair(left: Self, right: Self) -> Self { - Self::PAIR_IV.update(left, right) + Self::PAIR_IV.zz_update_2x32(left, right).into_merkle_root() } /// Produce a CMR for a disconnect combinator pub fn disconnect(left: Self, right: Self) -> Self { - Self::DISCONNECT_IV.update(left, right) + Self::DISCONNECT_IV + .zz_update_2x32(left, right) + .into_merkle_root() } /// Produce a CMR for a witness combinator pub fn witness(ty: &FinalArrow, value: &Value) -> Self { - use hashes::{sha256, HashEngine}; - - // 1 Bit-wise hash of `value` - let value_hash = compact_value(value); - // 2 Hash of hash of `value` and TMR of `value_type` - let mut engine = sha256::HashEngine::from_midstate(Self::WITNESS_IV.0, 0); - engine.input(&value_hash[..]); - engine.input(ty.target.tmr().as_ref()); - Self(engine.midstate()) + Self::WITNESS_IV + .zz_update_2x32(compact_value(value), ty.target.tmr()) + .into_merkle_root() } /// Produce an IHR for a fail combinator pub fn fail(entropy: FailEntropy) -> Self { - Self::FAIL_IV.update_fail_entropy(entropy) + Self::FAIL_IV + .zz_update_64(entropy.to_byte_array()) + .into_merkle_root() } /// Produce a CMR for a jet @@ -131,108 +128,109 @@ impl Imr { } #[rustfmt::skip] - const IDEN_IV: Self = Self(Midstate([ + const IDEN_IV: Midstate = Midstate::new([ 0x54, 0x1a, 0x1a, 0x69, 0xbd, 0x4b, 0xcb, 0xda, 0x7f, 0x34, 0x31, 0x0e, 0x30, 0x78, 0xf7, 0x26, 0x44, 0x31, 0x22, 0xfb, 0xcc, 0x1c, 0xb5, 0x36, 0x0c, 0x78, 0x64, 0xec, 0x0d, 0x32, 0x3a, 0xc0, - ])); + ], 64); #[rustfmt::skip] - const UNIT_IV: Self = Self(Midstate([ + const UNIT_IV: Midstate = Midstate::new([ 0xc4, 0x0a, 0x10, 0x26, 0x3f, 0x74, 0x36, 0xb4, 0x16, 0x0a, 0xcb, 0xef, 0x1c, 0x36, 0xfb, 0xa4, 0xbe, 0x4d, 0x95, 0xdf, 0x18, 0x1a, 0x96, 0x8a, 0xfe, 0xab, 0x5e, 0xac, 0x24, 0x7a, 0xdf, 0xf7, - ])); + ], 64); #[rustfmt::skip] - const INJL_IV: Self = Self(Midstate([ + const INJL_IV: Midstate = Midstate::new([ 0x54, 0xe9, 0x1d, 0x18, 0xd8, 0xf8, 0x1f, 0x6d, 0x29, 0x86, 0xbb, 0x58, 0x47, 0x9a, 0x54, 0xeb, 0x63, 0x0e, 0x95, 0x23, 0xb6, 0x9e, 0xe8, 0x53, 0x29, 0x80, 0xd0, 0x55, 0x58, 0x19, 0x4f, 0x15, - ])); + ], 64); #[rustfmt::skip] - const INJR_IV: Self = Self(Midstate([ + const INJR_IV: Midstate = Midstate::new([ 0xd7, 0x0f, 0xfd, 0xce, 0x97, 0x77, 0x7b, 0x4d, 0xfe, 0x31, 0xfd, 0x9f, 0xf5, 0xd0, 0x17, 0xa6, 0x30, 0x5d, 0x7e, 0xc6, 0x0d, 0xf3, 0xb1, 0xbf, 0x6d, 0x25, 0xe8, 0x16, 0x33, 0xde, 0xd4, 0xbf, - ])); + ], 64); #[rustfmt::skip] - const TAKE_IV: Self = Self(Midstate([ + const TAKE_IV: Midstate = Midstate::new([ 0x50, 0x5f, 0xc0, 0x81, 0xb5, 0xba, 0x2a, 0xcd, 0x09, 0x50, 0x67, 0xc3, 0xdf, 0xb8, 0xea, 0x12, 0x6f, 0xa1, 0x5d, 0x55, 0xcb, 0x21, 0x1e, 0x6a, 0xed, 0x34, 0xe8, 0xd1, 0xe3, 0x7a, 0xf0, 0xfa, - ])); + ], 64); #[rustfmt::skip] - const DROP_IV: Self = Self(Midstate([ + const DROP_IV: Midstate = Midstate::new([ 0x8a, 0x30, 0x8d, 0x38, 0xa1, 0x13, 0xa2, 0x60, 0xb4, 0xc7, 0x14, 0x5a, 0xbd, 0xc5, 0x22, 0x4d, 0xeb, 0x70, 0x13, 0x79, 0x59, 0x0e, 0x0c, 0x8c, 0x38, 0x86, 0x0b, 0xab, 0x12, 0x71, 0xa8, 0xa8, - ])); + ], 64); #[rustfmt::skip] - const COMP_IV: Self = Self(Midstate([ + const COMP_IV: Midstate = Midstate::new([ 0x57, 0xec, 0x23, 0xa2, 0xa4, 0x77, 0x8e, 0x01, 0x58, 0xa6, 0x21, 0x7a, 0xea, 0x3e, 0xf7, 0x42, 0x8b, 0xa0, 0x90, 0x92, 0x73, 0xb9, 0x73, 0xfa, 0x14, 0x32, 0xa9, 0x27, 0x84, 0x3e, 0x92, 0x7a, - ])); + ], 64); #[rustfmt::skip] - const CASE_IV: Self = Self(Midstate([ + const CASE_IV: Midstate = Midstate::new([ 0x29, 0x5e, 0x2a, 0x6d, 0xc8, 0xc5, 0xce, 0x59, 0xe4, 0xed, 0xcf, 0xe9, 0xb4, 0xd8, 0xf7, 0x64, 0x13, 0x3a, 0xa5, 0x51, 0x4b, 0xd3, 0xee, 0x8b, 0x4b, 0x75, 0xec, 0x8f, 0x4d, 0xeb, 0x08, 0xbe, - ])); + ], 64); #[rustfmt::skip] - const PAIR_IV: Self = Self(Midstate([ + const PAIR_IV: Midstate = Midstate::new([ 0x7d, 0x5e, 0x6d, 0xac, 0x15, 0xb1, 0x42, 0x8a, 0x0d, 0x26, 0x0c, 0x94, 0x29, 0xdb, 0xe8, 0x89, 0x65, 0x93, 0xf3, 0x1f, 0x70, 0x86, 0x27, 0xee, 0x75, 0xb2, 0x7e, 0xee, 0xfd, 0xd0, 0x50, 0x05, - ])); + ], 64); #[rustfmt::skip] - const DISCONNECT_IV: Self = Self(Midstate([ + const DISCONNECT_IV: Midstate = Midstate::new([ 0x4e, 0xb7, 0x99, 0x5f, 0xb5, 0xdd, 0xe5, 0xd0, 0x85, 0xf4, 0x70, 0x85, 0xcd, 0x95, 0x3d, 0x16, 0x84, 0x54, 0x11, 0xed, 0xc6, 0x89, 0xe2, 0x7a, 0xf9, 0xc3, 0xde, 0xa2, 0xfb, 0x12, 0x25, 0xd5, - ])); + ], 64); #[rustfmt::skip] - const WITNESS_IV: Self = Self(Midstate([ + const WITNESS_IV: Midstate = Midstate::new([ 0xcb, 0x37, 0xff, 0x70, 0x01, 0xc6, 0x2d, 0x94, 0x42, 0x4f, 0x98, 0x7f, 0x30, 0x23, 0xb3, 0x5e, 0x30, 0xd2, 0x17, 0x23, 0x96, 0x27, 0x6f, 0x89, 0xd0, 0x9f, 0x07, 0xaa, 0x67, 0xb6, 0x21, 0x96, - ])); + ], 64); #[rustfmt::skip] - const FAIL_IV: Self = Self(Midstate([ + const FAIL_IV: Midstate = Midstate::new([ 0x22, 0x83, 0xc1, 0x81, 0x9e, 0x69, 0x2f, 0x96, 0x85, 0xfe, 0x95, 0x40, 0x76, 0xc5, 0x16, 0x7c, 0x03, 0xbd, 0xe7, 0xcc, 0xda, 0xab, 0x00, 0x5e, 0x55, 0x36, 0x12, 0x2e, 0x18, 0xf7, 0x23, 0x7a, - ])); + ], 64); } impl Ihr { /// Construct an IHR from its components: an IMR and the source and target types. pub fn from_imr(imr: Imr, ty: &FinalArrow) -> Ihr { - let iv = Ihr(bip340_iv(b"Simplicity\x1fIdentity")); - iv.update_1(Ihr(imr.0)) - .update(ty.source.tmr().into(), ty.target.tmr().into()) + bip340_iv(b"Simplicity\x1fIdentity") + .update_0_then_32(imr) + .zz_update_2x32(ty.source.tmr(), ty.target.tmr()) + .into_merkle_root() } } @@ -243,7 +241,7 @@ mod tests { #[test] #[rustfmt::skip] // wants to split up the check_iv lines below fn ivs() { - fn check_iv(target: Imr, s: &'static str) { + fn check_iv(target: Midstate, s: &'static str) { let name = s .trim_start_matches("Simplicity\x1f") .trim_start_matches("Commitment\x1f") @@ -252,15 +250,15 @@ mod tests { /* let target = Imr(bip340_iv(s.as_bytes())); println!(" #[rustfmt::skip]"); - println!(" const {}_IV: Imr = Imr(Midstate([", name.to_ascii_uppercase()); + println!(" const {}_IV: Midstate = Midstate::new([", name.to_ascii_uppercase()); print!(" "); for ch in &target.0[0..8] { print!(" 0x{:02x},", ch); }; println!(); print!(" "); for ch in &target.0[8..16] { print!(" 0x{:02x},", ch); }; println!(); print!(" "); for ch in &target.0[16..24] { print!(" 0x{:02x},", ch); }; println!(); print!(" "); for ch in &target.0[24..32] { print!(" 0x{:02x},", ch); }; println!(); - println!(" ]));"); + println!(" ], 64);"); println!(); */ - assert_eq!(target, Imr(bip340_iv(s.as_bytes())), "mismatch on IV for {}", name); + assert_eq!(target, bip340_iv(s.as_bytes()), "mismatch on IV for {}", name); } // Note that these are the same as those for CMRs **except** for disconnect and witness. diff --git a/src/merkle/midstate.rs b/src/merkle/midstate.rs new file mode 100644 index 00000000..1b070bda --- /dev/null +++ b/src/merkle/midstate.rs @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! # Midstate Extensions +//! +//! Private extensions to the `sha256::Midstate` type (some of which we should probably upstream). + +use hashes::sha256::{HashEngine, Midstate}; +use hashes::HashEngine as _; + +/// Private trait to abstract over the various byte arrays that we "cast" to and from midstates +/// during construction (but not after). +pub trait ByteArrayExt: Sized { + fn to_byte_array(self) -> [u8; 32]; + fn from_byte_array(bytes: [u8; 32]) -> Self; +} + +impl ByteArrayExt for [u8; 32] { + #[inline] + fn to_byte_array(self) -> [u8; 32] { + self + } + #[inline] + fn from_byte_array(bytes: [u8; 32]) -> Self { + bytes + } +} + +impl ByteArrayExt for super::amr::Amr { + #[inline] + fn to_byte_array(self) -> [u8; 32] { + self.to_byte_array() + } + #[inline] + fn from_byte_array(bytes: [u8; 32]) -> Self { + Self::from_byte_array(bytes) + } +} + +impl ByteArrayExt for super::cmr::Cmr { + #[inline] + fn to_byte_array(self) -> [u8; 32] { + self.to_byte_array() + } + #[inline] + fn from_byte_array(bytes: [u8; 32]) -> Self { + Self::from_byte_array(bytes) + } +} + +impl ByteArrayExt for super::ihr::Ihr { + #[inline] + fn to_byte_array(self) -> [u8; 32] { + self.to_byte_array() + } + #[inline] + fn from_byte_array(bytes: [u8; 32]) -> Self { + Self::from_byte_array(bytes) + } +} + +impl ByteArrayExt for super::ihr::Imr { + #[inline] + fn to_byte_array(self) -> [u8; 32] { + self.to_byte_array() + } + #[inline] + fn from_byte_array(bytes: [u8; 32]) -> Self { + Self::from_byte_array(bytes) + } +} + +impl ByteArrayExt for super::tmr::Tmr { + #[inline] + fn to_byte_array(self) -> [u8; 32] { + self.to_byte_array() + } + #[inline] + fn from_byte_array(bytes: [u8; 32]) -> Self { + Self::from_byte_array(bytes) + } +} + +/// Private extension trait to add methods to the `sha256::Midstate` type. +pub trait MidstateExt: Sized { + /// Input two 32-byte blocks to the midstate to produce a new midstate. + /// + /// This one should be upstreamed (at least in the non-generic form which + /// just takes a pair of `[u8; 32]`s). + /// + /// `zz_` prefix is so that when/if we upstream an inherent method called `update_2x32`, it + /// won't break our code. + fn zz_update_2x32(self, left: MR1, right: MR2) -> Self; + + /// Input one 64-byte block to the midstate to produce a new midstate. + /// + /// This one should be upstreamed. + /// + /// `zz_` prefix is so that when/if we upstream an inherent method called `update_64`, it + /// won't break our code. + fn zz_update_64(self, bytes: [u8; 64]) -> Self; + + /// Input a 24-byte block of zeros, a big-endian 64-bit number, then a 32-byte block to produce + /// a new midstate. + #[inline] + fn update_weight_then_32(self, left_weight: u64, right: MR) -> Self { + let mut left = [0; 32]; + left[24..].copy_from_slice(&left_weight.to_be_bytes()); + self.zz_update_2x32(left, right) + } + + /// Input a 32-byte block of 0s, then the given 32-byte block, to produce a new midstate. + #[inline] + fn update_0_then_32(self, bytes: MR) -> Self { + self.zz_update_2x32([0; 32], bytes) + } + + /// Reinterpret the bytes of the midstate as an AMR. + fn into_merkle_root(self) -> MR; +} + +impl MidstateExt for Midstate { + #[inline] + fn zz_update_2x32(self, left: MR1, right: MR2) -> Self { + let mut engine = HashEngine::from_midstate(self); + engine.input(&left.to_byte_array()); + engine.input(&right.to_byte_array()); + engine.midstate().expect("hashed exactly 64 bytes") + } + + #[inline] + fn zz_update_64(self, bytes: [u8; 64]) -> Self { + let mut engine = HashEngine::from_midstate(self); + engine.input(bytes.as_ref()); + engine.midstate().expect("hashed exactly 64 bytes") + } + + /// Reinterpret the bytes of the midstate as an AMR. + #[inline] + fn into_merkle_root(self) -> MR { + MR::from_byte_array(self.to_parts().0) + } +} diff --git a/src/merkle/mod.rs b/src/merkle/mod.rs index b692778a..10353022 100644 --- a/src/merkle/mod.rs +++ b/src/merkle/mod.rs @@ -5,14 +5,17 @@ //! Tools for creating Merkle roots. //! There exist different Merkle roots for commitment and for redemption. +mod midstate; + pub mod amr; pub mod cmr; pub mod ihr; pub mod tmr; +use self::midstate::MidstateExt; use crate::bit_encoding::BitCollector; use crate::Value; -use hashes::{sha256, Hash, HashEngine}; +use hashes::{sha256, HashEngine}; use std::fmt; /// Trait for types that have a Commitment Merkle Root. @@ -33,11 +36,16 @@ impl FailEntropy { pub fn from_byte_array(data: [u8; 64]) -> Self { FailEntropy(data) } + + /// Extract the raw bytes from a [`FailEntropy`]. + pub fn to_byte_array(self) -> [u8; 64] { + self.0 + } } impl fmt::Display for FailEntropy { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&hex::DisplayHex::as_hex(&self.0), f) + fmt::Display::fmt(&hex::DisplayHex::as_hex(&self.0[..]), f) } } @@ -86,24 +94,29 @@ fn compact_value(value: &Value) -> [u8; 32] { consumed += 16; } debug_assert!(consumed == bytes.len()); - engine.midstate().to_byte_array() + engine + .midstate() + .expect("hash input length was a multiple of 64") + .to_parts() + .0 } fn bip340_iv(tag: &[u8]) -> sha256::Midstate { - let tag_hash = sha256::Hash::hash(tag); - let mut engine = sha256::Hash::engine(); - engine.input(tag_hash.as_ref()); - engine.input(tag_hash.as_ref()); - engine.midstate() + sha256::Midstate::hash_tag(tag) } -/// Convenience macro for wrappers of `Midstate`. +/// Convenience macro for wrappers of `[u8; 32]`. /// /// Implements `From` to and from `[u8; 32]`, /// `MerkleRoot`, `AsRef<[u8]>`, `Debug` and `Display` -#[macro_export] -macro_rules! impl_midstate_wrapper { - ($wrapper:ident) => { +macro_rules! impl_mr_type { + { + $(#[$($struct_attr:tt)*])* + pub struct $wrapper:ident([u8; 32]); + } => { + $(#[$($struct_attr)*])* + pub struct $wrapper([u8; 32]); + impl AsRef<[u8]> for $wrapper { fn as_ref(&self) -> &[u8] { self.0.as_ref() @@ -123,76 +136,27 @@ macro_rules! impl_midstate_wrapper { } impl std::str::FromStr for $wrapper { - type Err = hashes::hex::HexToArrayError; + type Err = hex::error::DecodeFixedLengthBytesError; fn from_str(s: &str) -> Result { - let x: [u8; 32] = hashes::hex::FromHex::from_hex(s)?; - Ok($wrapper(Midstate::from_byte_array(x))) + let x: [u8; 32] = hex::decode_to_array(s)?; + Ok($wrapper(x)) } } impl $wrapper { - /// Extend the given tagged hash by the given `left` and `right` hashes. - /// - /// The hash `self` is taken as initial value, - /// `left` and `right` hash are combined to create a 512-bit block, - /// and the compression function is run once - pub fn update(self, left: Self, right: Self) -> Self { - use $crate::hashes::{sha256, HashEngine}; - - let mut engine = sha256::HashEngine::from_midstate(self.0, 0); - engine.input(left.as_ref()); - engine.input(right.as_ref()); - $wrapper(engine.midstate()) - } - - /// Extend the given tagged hash by 256 bits of zeroes and the `right` hash. - /// - /// The hash `self` is taken as initial value, - /// 256 bits of zeroes and `right` hash are combined to create a 512-bit block, - /// and the compression function is run once - pub fn update_1(self, right: Self) -> Self { - use $crate::hashes::{sha256, HashEngine}; - - let mut engine = sha256::HashEngine::from_midstate(self.0, 0); - engine.input(&[0; 32]); - engine.input(&right.as_ref()); - $wrapper(engine.midstate()) - } - - /// Updates the given tagged hash with given `left` cost and `right` hash. - /// - /// The cost is serialized as the last 64 bits in the left block - pub fn update_with_weight(self, left_weight: u64, right: Self) -> Self { - use $crate::hashes::{sha256, HashEngine}; - - let mut engine = sha256::HashEngine::from_midstate(self.0, 0); - let mut left_blk = [0; 32]; - left_blk[24..].copy_from_slice(&left_weight.to_be_bytes()); - engine.input(&left_blk); - engine.input(right.as_ref()); - $wrapper(engine.midstate()) - } - - pub fn update_fail_entropy(self, entropy: $crate::FailEntropy) -> Self { - use $crate::hashes::{sha256, HashEngine}; - - let mut engine = sha256::HashEngine::from_midstate(self.0, 0); - engine.input(entropy.as_ref()); - $wrapper(engine.midstate()) - } - /// Converts the given tagged hash into a byte array pub fn from_byte_array(data: [u8; 32]) -> Self { - $wrapper(Midstate::from_byte_array(data)) + $wrapper(data) } /// Converts the given tagged hash into a byte array pub fn to_byte_array(self) -> [u8; 32] { - self.0.to_byte_array() + self.0 } } impl_serde_string!($wrapper); }; } +use impl_mr_type; diff --git a/src/merkle/tmr.rs b/src/merkle/tmr.rs index 0c292577..f049beb7 100644 --- a/src/merkle/tmr.rs +++ b/src/merkle/tmr.rs @@ -1,354 +1,354 @@ // SPDX-License-Identifier: CC0-1.0 -use crate::impl_midstate_wrapper; +use crate::merkle::midstate::MidstateExt; use hashes::sha256::Midstate; -/// Type Merkle root -/// -/// A Merkle root that commits to a type's primitive (unit, sum, product) -/// and recursively its sub-types. -/// -/// Uniquely identifies a type. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct Tmr(pub(crate) Midstate); - -impl_midstate_wrapper!(Tmr); +super::impl_mr_type! { + /// Type Merkle root + /// + /// A Merkle root that commits to a type's primitive (unit, sum, product) + /// and recursively its sub-types. + /// + /// Uniquely identifies a type. + #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] + pub struct Tmr([u8; 32]); +} impl Tmr { #[rustfmt::skip] - const UNIT_IV: Tmr = Tmr(Midstate([ + const UNIT_IV: Midstate = Midstate::new([ 0x50, 0xb3, 0x8c, 0xd7, 0x64, 0x75, 0xff, 0x89, 0x29, 0x28, 0x8b, 0xfc, 0xd0, 0xd9, 0xdf, 0x0e, 0x4a, 0x24, 0x1c, 0x0a, 0x57, 0x08, 0x57, 0x2a, 0xd2, 0x64, 0x19, 0x2a, 0x4f, 0xe6, 0x7b, 0xee, - ])); + ], 64); #[rustfmt::skip] - const SUM_IV: Tmr = Tmr(Midstate([ + const SUM_IV: Midstate = Midstate::new([ 0x92, 0x0c, 0xfd, 0x83, 0xcf, 0x96, 0xbb, 0x32, 0x73, 0x17, 0x36, 0x0f, 0x6d, 0x3a, 0xd7, 0x60, 0x1e, 0xef, 0x0a, 0x16, 0xdd, 0x53, 0x14, 0x6c, 0x2e, 0x5d, 0xe3, 0x5f, 0x51, 0xef, 0x8d, 0xa4, - ])); + ], 64); #[rustfmt::skip] - const PROD_IV: Tmr = Tmr(Midstate([ + const PROD_IV: Midstate = Midstate::new([ 0xae, 0x99, 0x2a, 0x3e, 0xe0, 0x71, 0x3b, 0xf6, 0x19, 0x5d, 0x3b, 0xac, 0x1a, 0xf5, 0x05, 0xa7, 0x29, 0xc1, 0x4d, 0x47, 0x95, 0x58, 0xf0, 0xd6, 0x29, 0x96, 0x30, 0xf7, 0xfa, 0x97, 0x2e, 0xf8, - ])); + ], 64); /// The TMRs of the types TWO^(2^n) for small values of n #[rustfmt::skip] pub const TWO_TWO_N: [Tmr; 32] = [ - Tmr(Midstate([ + Tmr([ 0x88, 0x5a, 0x22, 0xde, 0x3e, 0xdb, 0x3f, 0x40, 0xdb, 0x06, 0x09, 0xc2, 0x40, 0x23, 0x30, 0x3f, 0xec, 0x67, 0x87, 0xbe, 0x4d, 0x49, 0x21, 0xe4, 0x5e, 0xe9, 0xd9, 0x83, 0xac, 0xb5, 0xd8, 0x9d, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x55, 0x22, 0xba, 0x1c, 0x92, 0x89, 0x4d, 0xf2, 0x57, 0x23, 0xcf, 0xeb, 0x5a, 0xf2, 0x59, 0xf4, 0xc5, 0x5a, 0x5c, 0xfc, 0x82, 0x00, 0xd5, 0x9b, 0xad, 0x09, 0x64, 0x99, 0x7f, 0x9b, 0x2b, 0x04, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0xfa, 0x28, 0xba, 0x9f, 0x0f, 0x07, 0xb4, 0xfa, 0xe4, 0xdd, 0x86, 0x23, 0x61, 0x41, 0x12, 0xff, 0x3b, 0xaf, 0xab, 0xe2, 0x9c, 0x41, 0x5d, 0x2e, 0x0c, 0x1e, 0xd3, 0xc4, 0x13, 0x51, 0x28, 0xe0, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0xd3, 0x43, 0xe4, 0xd5, 0xfa, 0xa4, 0xce, 0xa4, 0x4b, 0xb7, 0x11, 0xef, 0x4b, 0x1b, 0x96, 0xb7, 0xe0, 0xfb, 0x26, 0xcf, 0xa8, 0x5a, 0x96, 0x04, 0x39, 0xba, 0x84, 0x58, 0xc0, 0xb6, 0xc5, 0xc3, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x62, 0x8c, 0x36, 0x1f, 0x09, 0xcb, 0x2e, 0x5b, 0xaa, 0xd9, 0xc6, 0xb7, 0x26, 0xa9, 0xfa, 0x95, 0xd1, 0xda, 0x67, 0x79, 0x7e, 0xe2, 0x4c, 0xcf, 0x01, 0x41, 0x0c, 0x97, 0x93, 0x39, 0x95, 0x00, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0xb1, 0xa9, 0xf9, 0x68, 0x78, 0x33, 0xdc, 0x44, 0xbc, 0x8f, 0x17, 0xd3, 0xcb, 0xc2, 0x55, 0xb7, 0xaa, 0x49, 0x9a, 0x78, 0xfe, 0xc8, 0x5c, 0xc9, 0x8a, 0x0c, 0x33, 0x5c, 0x86, 0xed, 0x26, 0xe9, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0xfd, 0x75, 0xbc, 0xd8, 0xcb, 0x55, 0xa5, 0xe4, 0x24, 0xe0, 0x92, 0x88, 0xac, 0x8e, 0xb4, 0x29, 0x42, 0x53, 0x4e, 0xc1, 0x09, 0x9b, 0x72, 0xbe, 0xce, 0x0e, 0xa2, 0xc5, 0x47, 0x9b, 0xf3, 0xe7, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0xe3, 0x94, 0xd2, 0xa0, 0x51, 0x6d, 0x73, 0x8d, 0x6e, 0x04, 0x59, 0xb9, 0x1b, 0xb0, 0xdd, 0x47, 0x1d, 0x0d, 0xaa, 0x77, 0x96, 0x71, 0xa5, 0x31, 0x3b, 0x62, 0x14, 0x0e, 0xc7, 0x0a, 0x22, 0x6e, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0xe4, 0xc4, 0x72, 0x8d, 0x87, 0x67, 0x49, 0xbe, 0xbe, 0x4d, 0x62, 0x97, 0xdf, 0xad, 0xd4, 0x2c, 0xe2, 0x68, 0x44, 0x50, 0x0d, 0x96, 0xc5, 0xe9, 0x14, 0x61, 0x57, 0x50, 0x8e, 0xb9, 0x09, 0x8f, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x1a, 0xe5, 0x45, 0xb7, 0xc3, 0xfd, 0x85, 0xfe, 0x81, 0x6c, 0x70, 0xea, 0x4c, 0x78, 0xb7, 0x41, 0x9d, 0x8b, 0xe3, 0xc1, 0xfb, 0x3f, 0x72, 0x23, 0xfa, 0x7d, 0x8d, 0x50, 0x36, 0x75, 0x51, 0x8f, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x6a, 0x9c, 0x72, 0x40, 0xe5, 0xd7, 0x1d, 0x5a, 0x83, 0x30, 0xb3, 0xb3, 0xb6, 0x65, 0x0b, 0xc3, 0x66, 0x6c, 0x3f, 0xef, 0x2e, 0x80, 0x3e, 0xd8, 0x64, 0xf1, 0x2f, 0xcd, 0x8f, 0xea, 0xc1, 0xca, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x0a, 0x2a, 0x58, 0x7a, 0xeb, 0xc9, 0x6b, 0x0e, 0x3e, 0x26, 0x01, 0x38, 0x2e, 0xb3, 0x2a, 0x9f, 0x1e, 0x29, 0xc8, 0x50, 0x1c, 0xbd, 0xd8, 0x0e, 0x87, 0x05, 0xc0, 0xe7, 0xd3, 0xf5, 0xb3, 0x4f, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0xb3, 0xed, 0xb6, 0xfd, 0x83, 0x8e, 0xc4, 0x05, 0x7b, 0x76, 0x8c, 0x86, 0xb0, 0xe8, 0xa9, 0x6d, 0x1c, 0xe3, 0xf1, 0x69, 0x54, 0x31, 0x4d, 0xcd, 0x3d, 0xbf, 0x72, 0x85, 0xeb, 0xa5, 0x63, 0xa9, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0xd0, 0x47, 0x4c, 0x0c, 0x1e, 0x2c, 0x09, 0xd9, 0xc5, 0xf0, 0x50, 0x98, 0x93, 0x57, 0xce, 0x71, 0xab, 0x5d, 0x90, 0x4e, 0x8e, 0x2e, 0x82, 0x87, 0x42, 0x86, 0xc0, 0xc2, 0x12, 0xde, 0x3e, 0x71, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x37, 0x92, 0x46, 0xdc, 0x86, 0x6d, 0x37, 0x0d, 0xbf, 0xc9, 0x70, 0x4f, 0xbc, 0x57, 0xcd, 0x7a, 0x6e, 0xb5, 0xed, 0xe7, 0x97, 0xa0, 0x0c, 0x4a, 0x72, 0xb3, 0xb4, 0xa5, 0xd9, 0xf0, 0x9f, 0xe1, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x8a, 0x59, 0xe8, 0x3f, 0x43, 0xe2, 0xa1, 0xf9, 0x07, 0xd3, 0xa2, 0x96, 0x67, 0x53, 0x33, 0x6a, 0x1d, 0xa2, 0x0d, 0x23, 0xfe, 0x63, 0x63, 0x28, 0x58, 0xf9, 0x4e, 0xc0, 0x7d, 0x19, 0x13, 0xd2, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x0f, 0x51, 0x95, 0x68, 0x9f, 0x55, 0x6d, 0x00, 0xbe, 0x92, 0xfc, 0x2d, 0xc5, 0x7b, 0xcb, 0xb1, 0x25, 0x62, 0x0f, 0xfc, 0x00, 0x96, 0x3c, 0x09, 0x14, 0xb1, 0x96, 0x15, 0x1a, 0x77, 0x84, 0x1c, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x6d, 0x2a, 0x47, 0x99, 0xa1, 0x6e, 0xb6, 0x78, 0x4e, 0x49, 0x66, 0xb8, 0x1d, 0xa6, 0x27, 0xd3, 0x5a, 0xa7, 0x08, 0xfd, 0x97, 0x25, 0xd6, 0x04, 0x77, 0x7b, 0x14, 0x3e, 0x28, 0xd4, 0xac, 0xf0, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x2a, 0x07, 0x5c, 0x50, 0xca, 0x10, 0xe5, 0x00, 0x92, 0x77, 0x86, 0xca, 0x86, 0xa5, 0x2b, 0xb7, 0xe5, 0x2d, 0xdb, 0x16, 0x42, 0xe3, 0x77, 0x45, 0xfb, 0x44, 0x14, 0x91, 0x39, 0xb3, 0x2d, 0x67, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0xf5, 0x67, 0xc8, 0x1b, 0x82, 0x5d, 0xc9, 0x00, 0xe0, 0xbc, 0x0c, 0x59, 0xce, 0xa6, 0xaa, 0x1f, 0x1f, 0x34, 0x28, 0x3f, 0xcb, 0xee, 0xff, 0x73, 0x2d, 0xb8, 0xe2, 0x17, 0x57, 0x6c, 0x22, 0x55, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x0a, 0xee, 0x10, 0x39, 0xd3, 0xc8, 0xaf, 0x91, 0x41, 0xbc, 0xb9, 0xfd, 0xf3, 0xf8, 0x24, 0x1e, 0xf9, 0xfd, 0x4b, 0x85, 0x85, 0x3a, 0x79, 0xba, 0xbc, 0xfb, 0x2a, 0xb4, 0xdb, 0x8e, 0xf3, 0x8c, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x30, 0xaa, 0xe9, 0xb8, 0xf2, 0xc6, 0xfa, 0xf0, 0x7a, 0x62, 0x07, 0x2f, 0x55, 0xb2, 0x96, 0x98, 0x5d, 0xd7, 0x3b, 0xc1, 0x04, 0x42, 0xc8, 0x02, 0x79, 0x32, 0xff, 0xf4, 0x1f, 0x2a, 0x2d, 0x72, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0xbc, 0x14, 0x6c, 0xb5, 0xae, 0x67, 0xe0, 0x0a, 0x6d, 0x8e, 0x3f, 0xd0, 0x56, 0xfb, 0x7a, 0x72, 0x82, 0x04, 0x84, 0xc6, 0x1a, 0xcf, 0x17, 0xfe, 0xcc, 0xf1, 0x30, 0xcb, 0xa6, 0x22, 0xd2, 0x7f, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0xa8, 0x67, 0xba, 0xcf, 0xf8, 0xd1, 0xc2, 0x5c, 0x9e, 0x82, 0x4f, 0x1b, 0x63, 0xa4, 0x9c, 0x17, 0x67, 0x9b, 0xba, 0x8a, 0xd8, 0x76, 0x99, 0xa0, 0xcd, 0xf7, 0x7d, 0xef, 0x0a, 0x1b, 0x75, 0x11, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0xc1, 0x28, 0xdc, 0x7e, 0x8d, 0x79, 0x4a, 0x7a, 0x48, 0x08, 0x14, 0x95, 0xa6, 0xeb, 0x2b, 0x2d, 0x3c, 0xb6, 0x6b, 0xc0, 0xe0, 0x33, 0x27, 0x3b, 0xf9, 0xdd, 0xbc, 0x98, 0x6c, 0x22, 0x68, 0x92, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x19, 0x9f, 0xfd, 0xe4, 0x7d, 0x27, 0x91, 0xa3, 0x58, 0xf2, 0x3a, 0xe5, 0x08, 0x25, 0x16, 0xc6, 0x5e, 0x57, 0xf5, 0x5a, 0xe6, 0x6d, 0xdf, 0x44, 0x65, 0xba, 0xab, 0x4b, 0xfa, 0x3c, 0x3a, 0x5f, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x40, 0x38, 0x09, 0x5d, 0xbb, 0x32, 0xe2, 0x7e, 0x6e, 0x31, 0x66, 0x52, 0x7b, 0xce, 0xdd, 0xa8, 0x4e, 0x8b, 0x21, 0xf6, 0xd4, 0x2f, 0x7e, 0x56, 0xea, 0xff, 0x21, 0x85, 0x6a, 0xd2, 0x04, 0xa2, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0xc3, 0xa0, 0xa6, 0xf8, 0x7e, 0x6c, 0x3f, 0x02, 0xbf, 0x98, 0x05, 0x1c, 0xe4, 0x5d, 0x6d, 0x69, 0x55, 0x38, 0x24, 0x78, 0xf1, 0x0f, 0xa0, 0x6a, 0xdd, 0x51, 0x30, 0xe5, 0x99, 0x17, 0xf8, 0xc7, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x7c, 0x25, 0xbf, 0x44, 0x7a, 0x45, 0xba, 0x13, 0x23, 0xe8, 0xdb, 0x8b, 0x81, 0x78, 0x87, 0xa8, 0x85, 0x8b, 0xee, 0x25, 0x6e, 0x5c, 0xa3, 0x52, 0x8e, 0x79, 0xcf, 0xbc, 0x2d, 0x41, 0x9c, 0xe3, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x75, 0xa7, 0x7a, 0x6c, 0x7b, 0x6d, 0xe9, 0x1d, 0x23, 0xc1, 0x00, 0x4d, 0x8e, 0xb0, 0xb3, 0x5d, 0x9c, 0xdf, 0x6f, 0xa1, 0xdf, 0xc7, 0xca, 0xbd, 0x04, 0x66, 0x92, 0xf1, 0xa2, 0x43, 0x14, 0x17, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x9f, 0xc2, 0x4a, 0xb6, 0x99, 0x70, 0xf8, 0x66, 0x6d, 0x7b, 0x1e, 0x52, 0x5a, 0x3a, 0x8e, 0x79, 0x43, 0x99, 0x76, 0xe7, 0x79, 0x2b, 0xfe, 0x1a, 0x1b, 0x0d, 0x0d, 0x84, 0xff, 0x03, 0x84, 0x34, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x64, 0x25, 0x1a, 0xb9, 0x70, 0x77, 0x17, 0x0e, 0x19, 0x91, 0x15, 0x76, 0x5e, 0x93, 0x43, 0xe7, 0x1b, 0x59, 0x0e, 0x41, 0x78, 0x88, 0xf2, 0xa8, 0x36, 0x43, 0x11, 0xd8, 0x65, 0x40, 0xed, 0xfa, - ])), + ]), ]; /// The TMRs of the types (TWO^8)^<(2^(N + 1)) for small values of n #[rustfmt::skip] pub const BUFFER8_TWO_N_PLUS_ONE: [Tmr; 16] = [ - Tmr(Midstate([ + Tmr([ 0x66, 0x8c, 0x79, 0xa6, 0xf0, 0xe0, 0x86, 0xa6, 0x2d, 0x73, 0xf0, 0xc6, 0x52, 0xb1, 0x5c, 0x56, 0xb1, 0xd3, 0x53, 0x68, 0xe1, 0xff, 0xad, 0x76, 0xc9, 0xd4, 0xdb, 0xae, 0xb5, 0x2d, 0xe6, 0x2f, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x9f, 0x91, 0xf5, 0x19, 0x9b, 0xb8, 0x16, 0x7a, 0x3b, 0x6c, 0xf8, 0xd0, 0x39, 0x75, 0x6e, 0x1b, 0x33, 0x18, 0x42, 0x6e, 0xef, 0x3a, 0x9b, 0xa0, 0x90, 0x2c, 0x91, 0xdc, 0x5f, 0x7e, 0x63, 0x39, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x16, 0x98, 0x45, 0xb0, 0xe2, 0xf0, 0x4a, 0x83, 0x76, 0xfe, 0x91, 0x14, 0xbd, 0x35, 0x3a, 0x29, 0xe9, 0xb7, 0x80, 0x42, 0xb7, 0x70, 0x1b, 0xa1, 0x6d, 0x02, 0xc4, 0x62, 0x18, 0xef, 0x8b, 0xdf, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0xbd, 0x59, 0x57, 0x8c, 0x45, 0x78, 0xfe, 0xc4, 0xef, 0x2a, 0x29, 0xc1, 0xad, 0xea, 0x4c, 0xe1, 0x30, 0x6c, 0x1a, 0x2b, 0x10, 0xd6, 0x18, 0xbc, 0x58, 0x44, 0x67, 0x84, 0x17, 0x97, 0x3b, 0x8e, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x88, 0xd1, 0x8c, 0x11, 0x5f, 0x86, 0x54, 0x40, 0x5f, 0xd7, 0x1d, 0xe8, 0x7e, 0x6f, 0xf0, 0x80, 0x6c, 0x5d, 0xa7, 0x97, 0xca, 0xaf, 0x48, 0xe2, 0x47, 0xea, 0xcc, 0x43, 0x99, 0x96, 0x89, 0x1a, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x90, 0x65, 0x22, 0xe1, 0x6d, 0xf3, 0x6f, 0x83, 0xa4, 0x50, 0x32, 0x8a, 0xf8, 0x4e, 0x2f, 0x3e, 0x52, 0xf6, 0xd6, 0x49, 0xa7, 0x88, 0xe3, 0xb2, 0x1c, 0xca, 0xe3, 0xcd, 0x9a, 0x24, 0x66, 0x77, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x3a, 0xba, 0x4c, 0xce, 0x0e, 0x46, 0xc5, 0x54, 0xb4, 0x4f, 0x70, 0x21, 0x3d, 0x4d, 0x8b, 0xd8, 0xe8, 0xf1, 0x7e, 0x2b, 0xd1, 0x7b, 0xdf, 0xb7, 0x15, 0x0a, 0xdf, 0xfd, 0x9a, 0xdf, 0x80, 0x9d, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x7d, 0x81, 0x2a, 0xf0, 0x44, 0xcb, 0x04, 0xd2, 0xaf, 0x4f, 0xc8, 0xe9, 0xb5, 0x30, 0x66, 0x03, 0x9b, 0xd5, 0x89, 0x2a, 0x56, 0xa5, 0xd5, 0x64, 0x1c, 0x5c, 0x58, 0xc8, 0x6e, 0xed, 0x8a, 0x35, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x23, 0x0e, 0xa9, 0x70, 0x03, 0x3e, 0x47, 0xc5, 0x61, 0x68, 0x1e, 0xe7, 0x20, 0x58, 0xb9, 0xc0, 0x75, 0x5b, 0x8d, 0x87, 0x22, 0x80, 0x18, 0x4f, 0x2e, 0x3f, 0xe3, 0x93, 0xc0, 0x47, 0x8f, 0x1a, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x6e, 0x90, 0x88, 0x04, 0x20, 0x46, 0x2b, 0x84, 0xf2, 0x16, 0x30, 0x2a, 0x09, 0x56, 0x9c, 0xc8, 0xd4, 0x47, 0x54, 0x4a, 0xcd, 0x8a, 0xbc, 0x53, 0xd8, 0xa7, 0xcf, 0x21, 0x9b, 0x44, 0x9c, 0x95, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x7a, 0xd1, 0x5d, 0xed, 0x85, 0x02, 0xa6, 0xf1, 0x1e, 0xb8, 0xa4, 0x9b, 0x25, 0xc8, 0xd7, 0x62, 0xa0, 0xfe, 0x00, 0x4f, 0xb5, 0x7d, 0x73, 0x43, 0xbd, 0xf4, 0x90, 0xed, 0x7e, 0xd2, 0xf4, 0x07, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0xaa, 0x30, 0x30, 0x15, 0x71, 0xcf, 0x8d, 0x2d, 0x64, 0xe9, 0x65, 0x4b, 0x0c, 0xbd, 0xe0, 0x54, 0x01, 0x77, 0xab, 0xc2, 0xf2, 0xef, 0xfb, 0x7f, 0x05, 0x2e, 0x27, 0xa8, 0xcc, 0xcc, 0xee, 0x62, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x28, 0x6d, 0x4d, 0x57, 0x6d, 0xb8, 0xe8, 0x45, 0x00, 0x9c, 0x74, 0x84, 0xe0, 0x0a, 0x9f, 0xa3, 0xf3, 0x18, 0xa8, 0x21, 0x9e, 0x38, 0x66, 0x2c, 0xda, 0x11, 0x75, 0x78, 0x22, 0xe4, 0x53, 0xbb, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x10, 0x56, 0xa7, 0x17, 0xdc, 0x0c, 0x86, 0xb7, 0x49, 0x97, 0x5e, 0x9c, 0x36, 0x4a, 0xb5, 0xa6, 0x64, 0x77, 0xc4, 0x2d, 0xe1, 0x6e, 0x5b, 0x05, 0x29, 0x81, 0x4f, 0x4c, 0xba, 0x95, 0x7c, 0xb3, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0x94, 0x59, 0x0e, 0xf5, 0x66, 0x10, 0xaa, 0x7f, 0xec, 0x46, 0x5b, 0x72, 0x32, 0xab, 0xa3, 0xf4, 0x7c, 0x94, 0x2b, 0x8e, 0x8d, 0xc8, 0x76, 0x82, 0x51, 0xd3, 0x70, 0x2c, 0x99, 0xb9, 0xef, 0xef, - ])), - Tmr(Midstate([ + ]), + Tmr([ 0xd0, 0x7f, 0x71, 0xb7, 0xcd, 0x48, 0xb8, 0x50, 0xad, 0x09, 0x06, 0x8b, 0x9b, 0x48, 0xe0, 0xf8, 0x5f, 0x84, 0xc0, 0x1b, 0x34, 0x58, 0x6a, 0x51, 0x34, 0x61, 0xfa, 0xf7, 0x6d, 0x54, 0x34, 0x64, - ])), + ]), ]; /// The TMR of the `Ctx8` type used by the SHA256 jets. #[rustfmt::skip] - pub const CTX8: Tmr = Tmr(Midstate([ + pub const CTX8: Tmr = Tmr([ 0x8d, 0x5c, 0xe5, 0xf6, 0xeb, 0xfc, 0x70, 0x61, 0xe7, 0x91, 0x16, 0xbd, 0xf6, 0x6f, 0xb4, 0xe4, 0xc6, 0xca, 0x32, 0xa0, 0x1c, 0xda, 0x48, 0xf6, 0xed, 0xc1, 0x0a, 0xee, 0xfd, 0x31, 0x0e, 0x8c, - ])); + ]); /// The TMR for the unit type - pub const fn unit() -> Tmr { - Self::UNIT_IV + pub fn unit() -> Tmr { + Self::UNIT_IV.into_merkle_root() } /// The TMR for the successor of a type. @@ -356,17 +356,19 @@ impl Tmr { /// Given a type `X`, we define its successor `S X` as `1 + X`. /// In Rust notation this would be `Option`. pub fn successor(self) -> Tmr { - Self::SUM_IV.update(Tmr::unit(), self) + Self::SUM_IV + .zz_update_2x32(Tmr::unit(), self) + .into_merkle_root() } /// The TMR for the sum of two types, whose TMRs are given pub fn sum(tmr1: Tmr, tmr2: Tmr) -> Tmr { - Self::SUM_IV.update(tmr1, tmr2) + Self::SUM_IV.zz_update_2x32(tmr1, tmr2).into_merkle_root() } /// The TMR for the product of two types, whose TMRs are given pub fn product(tmr1: Tmr, tmr2: Tmr) -> Tmr { - Self::PROD_IV.update(tmr1, tmr2) + Self::PROD_IV.zz_update_2x32(tmr1, tmr2).into_merkle_root() } } @@ -380,23 +382,23 @@ mod tests { #[test] fn ivs() { - fn check_iv(target: Tmr, s: &'static str) { + fn check_iv(target: Midstate, s: &'static str) { let name = s.trim_start_matches("Simplicity\x1fType\x1f"); // Uncomment this if the IVs ever change /* let target = Tmr(bip340_iv(s.as_bytes())); println!(" #[rustfmt::skip]"); - println!(" const {}_IV: Tmr = Tmr(Midstate([", name.to_ascii_uppercase()); + println!(" const {}_IV: Midstate = Midstate::new([", name.to_ascii_uppercase()); print!(" "); for ch in &target.0[0..8] { print!(" 0x{:02x},", ch); }; println!(); print!(" "); for ch in &target.0[8..16] { print!(" 0x{:02x},", ch); }; println!(); print!(" "); for ch in &target.0[16..24] { print!(" 0x{:02x},", ch); }; println!(); print!(" "); for ch in &target.0[24..32] { print!(" 0x{:02x},", ch); }; println!(); - println!(" ]));"); + println!(" ], 64);"); println!(); */ assert_eq!( target, - Tmr(bip340_iv(s.as_bytes())), + bip340_iv(s.as_bytes()), "mismatch on IV for {}", name ); @@ -414,12 +416,12 @@ mod tests { // Uncomment this if the IVs ever change /* let target = tmrs[i]; - println!(" Tmr(Midstate(["); + println!(" Tmr(["); print!(" "); for ch in &target.0[0..8] { print!(" 0x{:02x},", ch); }; println!(); print!(" "); for ch in &target.0[8..16] { print!(" 0x{:02x},", ch); }; println!(); print!(" "); for ch in &target.0[16..24] { print!(" 0x{:02x},", ch); }; println!(); print!(" "); for ch in &target.0[24..32] { print!(" 0x{:02x},", ch); }; println!(); - println!(" ])),"); + println!(" ]),"); */ assert_eq!(target, expected_tmrs[i], "mismatch on TMR for TWO^(2^{i})"); } diff --git a/src/node/construct.rs b/src/node/construct.rs index 85fdcf83..72568cc1 100644 --- a/src/node/construct.rs +++ b/src/node/construct.rs @@ -549,7 +549,7 @@ mod tests { let prog = finalized.to_vec_without_witness(); // In #286 we are encoding correctly... assert_eq!( - hex::DisplayHex::as_hex(&prog).to_string(), + hex::DisplayHex::as_hex(&prog[..]).to_string(), "dc920a28812b6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f243090e00b10e00680", ); diff --git a/src/node/redeem.rs b/src/node/redeem.rs index 4ce41dd2..1b67d106 100644 --- a/src/node/redeem.rs +++ b/src/node/redeem.rs @@ -544,14 +544,13 @@ impl RedeemNode { pub fn from_str(prog: &str, wit: &str) -> Result, crate::ParseError> { use crate::base64::engine::general_purpose; use crate::base64::Engine as _; - use crate::hex::FromHex as _; let v = general_purpose::STANDARD .decode(prog) .map_err(crate::ParseError::Base64)?; let prog_iter = crate::BitIter::new(v.into_iter()); - let v = Vec::from_hex(wit).map_err(crate::ParseError::Hex)?; + let v = hex::decode_to_vec(wit).map_err(crate::ParseError::Hex)?; let wit_iter = crate::BitIter::new(v.into_iter()); Self::decode::<_, _, J>(prog_iter, wit_iter).map_err(crate::ParseError::Decode) } diff --git a/src/policy/key.rs b/src/policy/key.rs index b11c9cb8..620f09ae 100644 --- a/src/policy/key.rs +++ b/src/policy/key.rs @@ -1,8 +1,8 @@ // SPDX-License-Identifier: CC0-1.0 +use bitcoin_miniscript::bitcoin::hashes::sha256; use bitcoin_miniscript::{MiniscriptKey, ToPublicKey}; use elements::bitcoin::key::XOnlyPublicKey; -use hashes::sha256; use std::fmt::{Debug, Display}; /// Public key which can be converted to a hash type. diff --git a/src/policy/satisfy.rs b/src/policy/satisfy.rs index 952ef9fc..c9f3710c 100644 --- a/src/policy/satisfy.rs +++ b/src/policy/satisfy.rs @@ -9,7 +9,6 @@ use crate::{Cmr, Policy, Value}; use elements::bitcoin; use elements::locktime::Height; use elements::taproot::TapLeafHash; -use hashes::Hash; use crate::jet::elements::ElementsEnv; use std::convert::TryFrom; @@ -153,6 +152,8 @@ impl Policy { } Policy::Trivial => super::serialize::trivial(inference_context), Policy::Key(ref key) => { + use elements::hashes::Hash as _; + let signature = satisfier .lookup_tap_leaf_script_sig(key, &TapLeafHash::all_zeros()) .map(|sig| sig.sig.serialize()) @@ -298,8 +299,8 @@ mod tests { use crate::policy::serialize; use crate::{BitMachine, FailEntropy, SimplicityKey}; use elements::bitcoin::key::{Keypair, XOnlyPublicKey}; + use elements::hashes::{sha256, Hash}; use elements::secp256k1_zkp; - use hashes::{sha256, Hash}; use std::collections::HashMap; use std::sync::Arc; diff --git a/src/policy/serialize.rs b/src/policy/serialize.rs index 5e85a5eb..ea623760 100644 --- a/src/policy/serialize.rs +++ b/src/policy/serialize.rs @@ -9,8 +9,6 @@ use crate::types; use crate::FailEntropy; use crate::{Cmr, ConstructNode, ToXOnlyPubkey}; -use hashes::Hash; - use crate::value::Word; use std::convert::TryFrom; use std::sync::Arc; @@ -134,6 +132,8 @@ where Pk: ToXOnlyPubkey, N: CoreConstructible<'brand> + JetConstructible<'brand> + WitnessConstructible<'brand, W>, { + use bitcoin_miniscript::bitcoin::hashes::Hash as _; + let hash_value = Word::u256(Pk::to_sha256(hash).to_byte_array()); let const_hash = N::const_word(inference_context, hash_value); let witness256 = N::witness(inference_context, witness); @@ -274,10 +274,10 @@ mod tests { use crate::node::SimpleFinalizer; use crate::policy::Policy; use crate::{BitMachine, CommitNode, FailEntropy, Value}; + use bitcoin_miniscript::bitcoin::hashes::{sha256, Hash}; use elements::bitcoin::key::XOnlyPublicKey; use elements::locktime::Height; use elements::secp256k1_zkp; - use hashes::{sha256, Hash}; use std::sync::Arc; fn compile(