From 8324193a5b94a1767a24d6e1bdfad92f55407ded Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 5 Aug 2026 20:22:02 +0000 Subject: [PATCH 1/7] policy: stop passing in a TapLeafHash to the satisfier's signature lookup It's not totally clear how we should be telling the satisfier what tapbranch it should be signing, but this isn't it. At the time that we call this method we pass in TapLeafHash::all_zeroes() which is incoherent and will go away in rust-elements 0.27.0. Then the only implementor simply ignores the value. For now just drop it. --- src/policy/satisfy.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/policy/satisfy.rs b/src/policy/satisfy.rs index c9f3710c..dc16c805 100644 --- a/src/policy/satisfy.rs +++ b/src/policy/satisfy.rs @@ -8,7 +8,6 @@ use crate::{Cmr, Policy, Value}; use elements::bitcoin; use elements::locktime::Height; -use elements::taproot::TapLeafHash; use crate::jet::elements::ElementsEnv; use std::convert::TryFrom; @@ -23,8 +22,9 @@ pub type Preimage32 = [u8; 32]; /// on every query. Users are expected to override the methods that they /// have data for. pub trait Satisfier<'brand, Pk: ToXOnlyPubkey> { - /// Given a public key, look up a Schnorr signature with that key. - fn lookup_tap_leaf_script_sig(&self, _: &Pk, _: &TapLeafHash) -> Option { + /// Given the external Taproot public key, look up a Schnorr signature (for the sighash_all + /// sighash mode for the program's Tapbranch) with that key. + fn lookup_signature(&self, _: &Pk) -> Option { None } @@ -152,10 +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()) + .lookup_signature(key) .map(|sig| sig.sig.serialize()) .map(Value::u512); ok_if( @@ -318,11 +316,7 @@ mod tests { &self.context } - fn lookup_tap_leaf_script_sig( - &self, - pk: &Pk, - _: &TapLeafHash, - ) -> Option { + fn lookup_signature(&self, pk: &Pk) -> Option { self.signatures.get(pk).copied() } From dedd8981c548b744aede10531085c32ecb9d63c6 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 4 Aug 2026 15:48:07 +0000 Subject: [PATCH 2/7] update elements to 0.27.0 --- Cargo-recent.lock | 60 +++++++++++++++---------------- Cargo.toml | 2 +- jets-bench/src/data_structures.rs | 9 ++--- jets-bench/src/env.rs | 22 ++++++------ jets-bench/src/input.rs | 4 +-- src/jet/elements/c_env.rs | 40 ++++++--------------- src/jet/elements/environment.rs | 3 +- src/jet/elements/tests.rs | 33 ++++++++--------- src/policy/satisfy.rs | 2 +- 9 files changed, 72 insertions(+), 103 deletions(-) diff --git a/Cargo-recent.lock b/Cargo-recent.lock index ae7b3d61..a146b8d8 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -25,12 +25,11 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "base58ck" -version = "0.1.0" +version = "0.1.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" +checksum = "365c0acd5b2e8dd0111a46c4faea83fb3cfb6e39a49a7c73a06e090db7b2eff0" dependencies = [ - "bitcoin-internals 0.3.0", - "bitcoin_hashes 0.14.1", + "bitcoin_hashes 0.14.101", ] [[package]] @@ -59,17 +58,17 @@ checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" [[package]] name = "bitcoin" -version = "0.32.8" +version = "0.32.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e499f9fc0407f50fe98af744ab44fa67d409f76b6772e1689ec8485eb0c0f66" +checksum = "bb0ce8bd5baaa0d303a19915a6d93afed161f528654e42da2a7a97d05c59499a" dependencies = [ "base58ck", "base64 0.21.7", "bech32", - "bitcoin-internals 0.3.0", + "bitcoin-consensus-encoding", "bitcoin-io", "bitcoin-units", - "bitcoin_hashes 0.14.1", + "bitcoin_hashes 0.14.101", "hex-conservative 0.2.2", "hex_lit", "secp256k1", @@ -82,15 +81,8 @@ 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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" -dependencies = [ + "bitcoin-internals", + "hex-conservative 1.2.0", "serde", ] @@ -102,9 +94,12 @@ checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" [[package]] name = "bitcoin-io" -version = "0.1.4" +version = "0.1.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" +checksum = "bb5de036369d1ac59d3c1819ebc4d850f89466f5401c571a285b6ed564a4cb78" +dependencies = [ + "bitcoin-consensus-encoding", +] [[package]] name = "bitcoin-private" @@ -114,19 +109,19 @@ checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" [[package]] name = "bitcoin-units" -version = "0.1.2" +version = "0.1.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" +checksum = "9cb95693f371d089a4b5b6fc41c6f3ea6e01ee8c15388335dfac8ea685173b51" dependencies = [ - "bitcoin-internals 0.3.0", + "bitcoin-consensus-encoding", "serde", ] [[package]] name = "bitcoin_hashes" -version = "0.14.1" +version = "0.14.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" +checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" dependencies = [ "bitcoin-io", "hex-conservative 0.2.2", @@ -140,8 +135,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a67800fcf7f3ca52f7796d4466948a424326b225950c79d1e76d0458760bb25d" dependencies = [ "bitcoin-consensus-encoding", - "bitcoin-internals 0.6.0", + "bitcoin-internals", "hex-conservative 1.2.0", + "serde", ] [[package]] @@ -176,12 +172,16 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "elements" -version = "0.25.3" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c63a04238f4b7f3564fe9705921452c2900efd00982545e5c634fac9024fe2" +checksum = "34e00f7ddcd826d2f4c9bd72d79a6b6991a0ac4286e7e2b2ff8eecd130549073" dependencies = [ "bech32", "bitcoin", + "bitcoin-consensus-encoding", + "bitcoin-internals", + "bitcoin_hashes 1.1.0", + "hex-conservative 1.2.0", "secp256k1-zkp", "serde", ] @@ -485,7 +485,7 @@ version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ - "bitcoin_hashes 0.14.1", + "bitcoin_hashes 0.14.101", "rand", "secp256k1-sys", "serde", @@ -601,7 +601,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75c8fb4a18e63fbce4cf16026c36a6c38066e4f4a09ce5e81be817d0e36d8f8" dependencies = [ - "bitcoin_hashes 0.14.1", + "bitcoin_hashes 0.14.101", "byteorder", "getrandom 0.2.16", "hex-conservative 0.1.2", @@ -634,7 +634,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd2cc5d458a8032d328ea85e824f54f61664ab84c3d42b3b7f8804fb9b81572" dependencies = [ - "bitcoin_hashes 0.14.1", + "bitcoin_hashes 0.14.101", "cc", ] diff --git a/Cargo.toml b/Cargo.toml index 8055aa8f..c6cb91b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ path = "src/lib.rs" bitcoin = { version = "0.32.0", optional = true } bitcoin-miniscript = { package = "miniscript", version = "12.0.0" } byteorder = "1.3" -elements = { version = "0.25.3", optional = true, default-features = false } +elements = { version = "0.27.0", optional = true, default-features = false } ghost-cell = { version = "0.2.6", default-features = false } hashes = { package = "bitcoin_hashes", version = "1.1.0" } hex = { package = "hex-conservative", version = "1.2.0" } diff --git a/jets-bench/src/data_structures.rs b/jets-bench/src/data_structures.rs index bcb2ad4a..2eacd32d 100644 --- a/jets-bench/src/data_structures.rs +++ b/jets-bench/src/data_structures.rs @@ -4,10 +4,7 @@ use bitcoin::secp256k1; use elements::Txid; use rand::{thread_rng, RngCore}; -use simplicity::{ - bitcoin, elements, elements::hashes::Hash as _, types::Final, BitIter, EarlyEndOfStreamError, - Value, -}; +use simplicity::{bitcoin, elements, types::Final, BitIter, EarlyEndOfStreamError, Value}; /// Engine to compute SHA256 hash function. /// We can't use hashes::sha256::HashEngine because it does not accept @@ -182,7 +179,7 @@ impl SimplicityEncode for elements::confidential::Asset { match self { elements::confidential::Asset::Explicit(a) => { let left = Final::product(Final::u1(), Final::u256()); - Value::right(left, Value::u256(a.into_inner().to_byte_array())) + Value::right(left, Value::u256(a.to_byte_array())) } elements::confidential::Asset::Confidential(gen) => { let ser = gen.serialize(); @@ -324,7 +321,7 @@ impl BenchSample for elements::confidential::Asset { let mut rng = rand::thread_rng(); let mut asset = [0u8; 32]; rng.fill_bytes(&mut asset); - let asset = elements::AssetId::from_slice(&asset).unwrap(); + let asset = elements::AssetId::from_byte_array(asset); elements::confidential::Asset::Explicit(asset) } else { let asset_str = "0abb0d5d5843b9378c7f245fd7329d6fcef6926554f0c95f7cf0316239178f743c"; diff --git a/jets-bench/src/env.rs b/jets-bench/src/env.rs index 21cf158c..b55c2ed6 100644 --- a/jets-bench/src/env.rs +++ b/jets-bench/src/env.rs @@ -1,7 +1,6 @@ use elements::locktime::LockTime; use elements::taproot::ControlBlock; use elements::{BlockHash, Transaction}; -use simplicity::elements::hashes::Hash; use simplicity::jet::elements::{ElementsEnv, ElementsUtxo}; use simplicity::Cmr; use simplicity::{bitcoin, elements}; @@ -190,7 +189,7 @@ fn env_with_spent_utxos( Cmr::from_byte_array([0xab; 32]), // dummy values ControlBlock::from_slice(&ctrl_blk).unwrap(), annex, - BlockHash::all_zeros(), // Dummy genesis hash + BlockHash::GENESIS_PREVIOUS_BLOCK_HASH, // Dummy genesis hash ) } @@ -206,7 +205,7 @@ fn null_env() -> ElementsEnv> { pub(super) mod txout_utils { use bitcoin::key::XOnlyPublicKey; - use elements::hex::FromHex; + use elements::hex; use elements::{confidential, encode::deserialize, schnorr::TapTweak, AssetId, Transaction}; use simplicity::{bitcoin, elements}; @@ -218,7 +217,7 @@ pub(super) mod txout_utils { let asset = rand::random::<[u8; 32]>(); elements::TxOut { value: confidential::Value::Explicit(v), - asset: confidential::Asset::Explicit(AssetId::from_slice(&asset).unwrap()), + asset: confidential::Asset::Explicit(AssetId::from_byte_array(asset)), nonce: confidential::Nonce::Null, script_pubkey: Default::default(), witness: Default::default(), @@ -233,13 +232,13 @@ pub(super) mod txout_utils { let v = rand::random::() % 21_000_000; let asset = rand::random::<[u8; 32]>(); let xpk = "21ddd128bfa066994cb4be6880f0589075996203d8a7e88f136eec662b8912b8"; - let xpk = Vec::::from_hex(xpk).unwrap(); + let xpk = hex::decode_to_vec(xpk).unwrap(); let output_key = XOnlyPublicKey::from_slice(&xpk) .unwrap() .dangerous_assume_tweaked(); elements::TxOut { value: confidential::Value::Explicit(v), - asset: confidential::Asset::Explicit(AssetId::from_slice(&asset).unwrap()), + asset: confidential::Asset::Explicit(AssetId::from_byte_array(asset)), nonce: confidential::Nonce::Null, script_pubkey: elements::Script::new_v1_p2tr_tweaked(output_key), witness: elements::TxOutWitness::default(), @@ -247,20 +246,19 @@ pub(super) mod txout_utils { } pub(super) fn confidential() -> elements::TxOut { - let tx_bytes = Vec::::from_hex(CONF_TX_BYTES).unwrap(); + let tx_bytes = hex::decode_to_vec(CONF_TX_BYTES).unwrap(); let mut tx: Transaction = deserialize(&tx_bytes).unwrap(); tx.output.swap_remove(0) } } pub(super) mod txin_utils { use elements::{ - confidential, encode::deserialize, hex::FromHex, AssetIssuance, OutPoint, Script, Sequence, + confidential, encode::deserialize, hex, AssetIssuance, OutPoint, Script, Sequence, TxInWitness, Txid, }; use simplicity::{elements, jet::elements::ElementsUtxo}; use super::txout_utils; - use super::Hash; const PEGIN_TX_BYTES: &str = "0200000001013fe9fcf1d5eae66a152efa45ad32baa5eed3cf11ab5e04edde650313b58ed8c90000004000ffffffff0201f80bb0038f482243202f0b2dcf88d9b4e7f930a48a3fcdc003af76b1f9d60e63010000000005f5c88c001976a914d7cc0ea6d5e53af78c7802101519cc100692668e88ac01f80bb0038f482243202f0b2dcf88d9b4e7f930a48a3fcdc003af76b1f9d60e63010000000000001874000000000000000002473044022048cf10f12a31cb0ec36ba3a6f79fad7e0dea3f1aa790a5aed02f8e8455c8cb1502201a2624089ce70c893dfd07a156ba91223edd5680cbd93d3336285ceefcb3dc1401210205914becd15ac5d2f72ad0aa42e84349c825a544d8c16e78ecc21534ef561fd4060800e1f5050000000020f80bb0038f482243202f0b2dcf88d9b4e7f930a48a3fcdc003af76b1f9d60e632006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f1600141ab7f5995cf0dfcb90cbb02b63397e5326eae6febe020000000113244fa59fcb407124038ff9121ed546f6dc217571cb366a50d3193f2c80298c0000000049483045022100d1e212715d2dcbc1c66d76f43d9f326f54ff339b565c68f046ed74040730433b02201d9ccbad57566100a06b4be47a4c777cbd7c99e0a08e17f7bf10458117426cd801feffffff0200e1f5050000000017a914774b87be1ef871d82a01edbb89a70bf4bb59310387a88c8b44000000001976a914b14b7395623921dbbce438f4fc1fc8f1a495affa88acf4010000b700000020a060086af92ac34dbbc8bd89bbbe03ef7e0016930f7fdc806ff15d163b5fda5e32105949c748222d3e1c5b6e0a4d47f8de45b25d63f145c4056682a7b15cc3da56a2815bffff7f20000000000300000003946c969d81a3b0ca473ab54c11fa665234d6ce1ad09e87a1dbc56eb6de4002b83fe9fcf1d5eae66a152efa45ad32baa5eed3cf11ab5e04edde650313b58ed8c9fccdc0d07eaf48f928fecfc07707b95769704d25f855529711ed6450cc9b3c95010b00000000"; const ISSUANCE_TX_BYTES: &str = "020000000101975891c8466c3eed75f817e16867e4004786afe652c833db38a6d8ff37626ea10000008000fdffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000846378d77e3e759fd90a42a99cf4b95e8819db99d424ce3fd0352ec16ca9115a5093b64687ef20c5c3da2d2695c42aecd775d700d2ddb9a903a3eac8d661b503070040aba8ea89e00fbd0e14bcbeec94b883ac4fba0dafd5e795658bc1d184ad80c625809da1bff65d7958239c186b5dd8ef23606903bf1f5961893b3e3d7dd44902e4ea302f8fceaa7a53a51a24cfb9666f2d798224a5a84b60d26483a9da385362b87300616001455c29f7cf389e353362116c2ee374857c013129e0a3a07091f4e203f46aaa44d17824ba311243ed38a70fb9b393bde312d5b4460c208fcaab83caf46df2ee16c162094809fb9dce4b642224da210702be1b04deb94c70381d9c59df43aa717e026743f9c54c2048756eddb6d5992a56627102194852f9b160014feff6610a74105b33e4e8d2cac73f3d05c944d270bcdadac9b7bf23a96bdbb9f488bf7909f76d7ff997a5551a38778a7e7aa5f8b540889d6c664ee5d6a106dadb07a987843fc2fb965f356821a59db1dc1f7e1aa839f03fbd0226d70bba5a6fc866d4c980b677cd9112e98aaadfc671ff7e39e49a748ab160014b6d0f377bd2d2dc48ebb374b7107a15d4395ff8101230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000000000251000065000000fd46104033a1497f00233f81f0687170526b203c1d6f34ef9c7d73bd2ccafc3d7de5fa4e69ccd7bf27571e7b49803282a8dd2fe98845efbfcd2d28de571a1d2c077228effcc0ce338457ab6f3d15ea7fbbdff8024ceefc72eb9345d272a3650b6ad1220a173cc7ad90c7b6d108bc5bf20e8613ee76b8f2280f55dccaf5e0adb7eb50753288693ea99417bc7588db0f4ff798a503885da8ab6ae3be8b628ba48c7a4c80a6067017705486e7c06ec509557f7d75e2e75db784a1293f847a5940e725d86a505f720ce43c27df97ea2fc05e003cc74c924fbe8513454ccf6fc54e73987a59f48297175e81fd8ccae89bcad6006501c6126bb94ae7bbea35f1e996107015eaf7bc4e046bbfff3d324afc1f8a764f91f0775aac6c328a9ee2c28c74673e62a0ed7ad630738329be8d586bda0e6224f42a9d7c4b4dbfe505ce513e9bd2d2f78a7c3977d74ffc5790dc50f913c80e8b1f23e6b236c790a9578e956b039ee8940bcb3f7d87c8da028dd60b078130c52d553d63ec02689a62c4d0f365dc53cf406b6ed3ee3eb60a280776452712635788f6583e875c598ab9ca6a96a2b68e99853abcbc1ba113bed2fbf05fb63c5fef34ab38e10f426309fc0891bbbadc44979409712a3ce7cf01a31985eeac0f4dbe00a57a20ef9f766025102b1428cd8adcfa63a7ffa25f89c2ba8e6b92cfe660135f1714179d455f014a9875005afa019a434d285927dae2e285bdf82721d65d2f095fdbe83cd861497e4875a69961cd44b6895485ce325c074f69ccb60c8831a7437ab08f9d6da84f40f65d5a63b409631d0a65eb5285c0f4837ff71f2a7391f51d10865731bcab4943bbfa3feec2433640329753a134a9035caeb9bca8287c5bd723252c8f6849b80e89f9355200de5548f081a36c5a1cc579be24b8b563aa4025c005cedfc1bb540fa8792749b971835edc741fbd211fb45ed6df5094714a8ba30f3895a8e2a4314d944d00791a19f4f811bd7d67a83df31614b43f13c59ef1d20e28d589a505a1acd37a4ff78e9af2d132063af9f5fd7d9fb3cd6bf9f3dc73cd4cb3479f7c36c23135d8c962fb531d16ed8fb4055151c6216cf53bbf1cf65ee63d9bf85bb74bfae527022619c4f0d9e8d3339bde33bb789bd25bee0241acd1527283f1cd58b7bec00b6f30931024e30c18469002e825bf6d10240b0163585cdff2845a077681996558fd63f1f7c373b54af1c2da61bb015ff70895e1072136d3b603d329c428871750858a70a222ad73560bc73e5ad24f541fc4a32f734a2dc722f24579b505158f39bee0cfc4c9890245c2defb368b6d6644a2579ff4a23082e686c94bcfc4097589efeda5810c363acd8290ee29ee76baecb178a228de614388b79283df07396195c4d2d3aadb7ac3f68cab163c8a1157e5ce4c3502cba9032918bc259434c4b12cbf77d08beb6cab6e19ac3c7d2d82a6b792a0c1de08540ada9c45dfab36b4c71419d54f25959d7c95e2c0301982caddd0fd0f9ac45dfb1cbf9a6cbb3111f7e5a9e8a29870c1029cc0d74df8f8a852dca511aeee22c471b09d8cf18eb85dddf9273c5024de0d0d617f3609d6e09a2d0f2951ae35d07f5f27cd47f217a939d56aae2c8ee2d95836bbdaa5c816082fe2bbb1f5c7a114e1b45ddd448c3ad19cdd7ed04a087ba00524ed8e16f295b4ec28f5ba5e0077d3d0ef30e718ce5a1d9c8430631baefcc9c26551d26f42ab64041129304eb4d76720d94adf9f2cece3627b2833335dce5f562ae2169ef44f12ca36a26d45195847b8eda8836177025bc66aa4f103d662ba1de526db5c7ad2899bd9e22245445022a156a2b47022853ca473588172efdcb8e61c8d20962a8c92d3a86e83050ed75d6a152915eb0e1051800b79d15b473899f4fbd3d47d6e7e72982d6c7adce00e51f0b1033bd1112091418ef979b4aa7f4c942b31ecc5bc9cbddeb3812906a172fbcfe085eafb901140710cea913779510648a79fab4917f96efc5e7f28b0fd2c813805760d657a089a015f6368f2b471539c8ca2b33c4800a6a201a21daf09a194e1962196716e0a645c72dd1168dead6b64b0ef16ef4b73dcb2b56d517c082f218db141059cc7d52d5e5e0832f13030be8e3b36605f9f9749fbb1b1b4805a5720763ae406b32498f1aab809088489a6b71e2274ec26c4964f2e3bb32063013868a030af901e8afb5f563e3a690726b7afd3761ab9cb3abaa4849355d0183dfc66d8e8bace12e739c9a9ae1efa0d9d8f58df3352e7f8c1e24e2a0f71c861ec09428bbe837145ec1b50426c24725bdd025da913b689ede4c8d57c8cda6a48a5b246b0ae57e4dce9cd2cfd0604b217f806650ac957aa01aa9f37e601bf1fbe36e0b3359a4b7477fb4e59c48481797694b5ef3b51ccb7a69910617988bf1b5dae18ff0686d722fcef097d55ec74932643a285242243a96288525dbaab523d55ab28aa0b6df74c4bb2d90042c952c5412d379357c81fa1618e3cbc855132e0e63242fcc2cdcd1d96663ab8f112424f7deb4aa6a01976d4d8c9e82303b3323bf1a77a9392b8141aba33df3a02f3423d50136a65aafa4b993a36ebe4e1ade2f3c8cb70bdd35b4787180cfe14ce82e81f60aea09c052f74a41045eee52844cb50e257dad50ca3e8f3631f82366252cd4dfef33e6106edee6764d25d0a0cf8c9d8dc0b126064d2efec032890724e40ceb350a4d5df729bb22a878fce0b4e698b16c251db70a49f1275ed28a7e678f2ffad0047a118d7f8202f417ba2436b285cbbd0f1c2710f08d73b31e54ad9e1164c6fc1ba1a5c9666b001f5d8b7d03e97a48d338890b41b2968c65859e18907a08e7f96da368f2143f63d09c0948e6e7a3b41210fe7ab7a48668339d0f5e4d4de1b8c0cef095b228dcaaa0ef8c6fc1d8d65cda7e9e81137aa8d0595b53c6bd9f831debee20784e790af3676039b7f4486e28ea28edb66dfdcaaa62f286099bbb62066023278de78030c30a7bc33f5ee2d2fee4705eeebf1768c7ee1b15dca01eb82f818589ba2362feee455c568f38c306d964987e29cd50408c73d171f93c4bb1e4937bbd786d567bd3dd488a440cf95fcc1a54d6e0b35173421daa01512f970e89738251fb75701c5c849720290e49f124fe3c4e2885b23b055b1cf6b7ea10a07614e9f2eba7bc953158e11130167fd2081a09381329449e2f17fe7403db46b0ce380fe846cda85e4c085fc0dfd8a86d4aaa4fd6c1b2804b13c70a4a858180ff1450a4c11aa576327f78e74c3dccbb27d178ef09575c4879e84d49b41caa3b3dd282f1b464d0df4bc01565123ead10bdb9273544b058032807482774fd9e705187bc6a4927dc29a23a8d6e5d7c7b54432546d5a2f341b2d90ab6866d9177f7f8d601083a53444b16b1e9ef4ff24954f4a3bc5fa25af61ce9a5c1ee2e507eb4f4022b4c0bea888491b1201477eeae5ae432a262b8c9da98283b895f484dd911a7c503a258e9b4ea01f07ec66fce68be4372d87b304d7840362064b7443ec227df0d25ad6e695f800be0bff81cb68d8f05c89318561209f6e36952cc30c0a2f3b66855b63d388d55a44013096f2bf8941804cb202685deb23ad0fdb92de3a780051fe5f644483db93c95fbff0a30e0d1b1a476f1161953835665c761b7190b1520b5880896e0c51ae6e3090fab2554ad56dcf0d8311b0256a5a4561460b01741e843d4b0c26abfbabc519dc2141d3aacb6ae7713cf2c95803904ace06f667d114193e655e1ff0ce724441cedfd21ca3268cd2f586eeb150c08c55540e3c621e77986c4fd07d5a586a787fdb976a1549d982c2620dbaff166949c99f5d52664fb350025976d1f78d00994442df6d552cbc567f0d9d7b4d391876bbcc3b9e61f2f764b2464066f3a491042f7376ce4f28e63e6d5d395e5ef6c529d6eb3a792ffd5ea1016fbae24188d96bac0a9c40a3c024a4432a27145d72d0f9c91f373ad0fafb7c4503d3668ebf8c090a805a98d272c6f24ca0f8c98f03f3c9d33f38c92200d4e9a3d8fe2034c195779c2fb261e57b1a067f177af54f2ad7fc49154582a70f7da33500a849212717756f06e53bdcecd41b75faf63afcad04118a3ef956d72643b7d9737107354a3dd3b24f456aa4ab45f00b4a8d2a7617a9bc9dc2afca3c2e478ebde4634a73d1b6b61c99a14bd4b962bb3e3fdd9a00ab928e1a8fc1dcdd0a4ac463ba693ecd9d1ba40199c8f423ecdce7f05f928f5aab1baebbf3c0fa17b114aa12bb3c6ebc7da0b113e3e2886dc092b787a738fa1906c63c815c2ae6d51e97bd44fed8f1b83f5c4f9141971e6947c88490f67637c03cd7e354f74fab3494f40c869a57f67b6525501cf19ee9ae93430b29cc680c8b31dc8e8950a06c43e821ab86f16d275a219e49f9cf94016b7c08c8ceb161c52e8bca8f8044669a96c160c1e38c7e65afa862264ec168dc6d4a6a9d70625341ea83daeb15b93fd20d449157271e01cd591f4ddb9ad8b4bed1e1402cf9ca6964653288c78d673803df2505c680c840ea6fb7411f1cf229184f1331b8c8ed82d1ec7d35a8c4de9fa04b6283eb67fc4a164c122506921e49f9b6726b97168b53b3dac93f302e79dd260723dd077716083060cf2e363361fbba6d8b86723c64382d59fe20920e9d5aa133021a6709901b535c63c4795845c78a7e647e39340dc6b74d3e7341c49bd8af57c9abb2261a328f33122026043e4f32b7ebecca9d9250f186e81906143f409ef36c8865577e797e7d57381a0fe633df73b59544c16ed8b8f852f3e033da0ade4412a4d1ce584b47abf8ac4ac604a90a0f316b621c87ef7200bc10df3221533e1e54a6bde13c549c95f8060b4149d351ee10e33217470125bbb4bf327bfd5d151458dc249aa4b57f14979fd53b09ec2d01b9358739fbe44e7ba4c54a9e6bd483801605cd33dfa84e7dee0bfc30d4b4e2b0c45355e13a92f9c588725829de2e9efc0adfd807c3f8a32b623548785d07d3520f0933e5d5a280efc6724e03dad47013bf54338ef4956c9be1b09681429f9cda18a11df04ac0ed66f28d2f3722aec5fc0f169055e9f5357d15fa6d53edc35dcc5dde72ca418c618994a5e8cac6c2a115e87b27d2d3af81654c23d476f49b606144814cb36ca892b0e0d4673a2091c4f948079eca42f66e000432255da0c99cae1542acf0836398e067b8d13b9e1e9a149bdda367bd65f88af269078ac8cdf79a2b303287ceaac470b050b6884a44c86ccf413e50fecedffd864c16b18bf3c59cc9c0e2c3a84a37f7dc1bb53729f306f40827d30eef367ad5217a5c5f987da8003b08d55b90014cd828fed7beb4c4afe6bca378aae72f8c4c3eeabe1c1b7edb44f1ce8abce27c16154ea58015fcf9ca99200005729abd67473540c4c9f20d1d0875697dcf01b2cfe61aa729a9f90475bbb720e413794df4079bf3199ab1a071ee448dbef39a9d1e2c7462cb6a56d558389244b4542f913325dcc0c2517dfaea6cb67a1e5ee8e9e7f4b4b343b33588c28f6cf82be5aca0c0b51dc7c461dd05ccb9766814a17d0e491a61b87bf134002441fc1c4f3b060df87b065f75f30121545755397015f0c4b8c7cf855defc20af97349281147ce557e40d292b36950d58692162dac1993c0ca4fa61fafdc1baa39e50302666546a8c3f74c2e0b53b3f0b9307dae1834a284aa6a2f811af925037a7a69a0585689d31777cccabb18b7e3f5fb5644339fd998d3a3886e3e1bd5f85fcc39f8e5d14005292c275076636179c93c8b2593661f7ece42551ccc1e43fd78a0331001b7b3b9a7dcdf40ff398a43e07be40ec2946f994fe8647f854b86f8c4fd735dea6cdbe7ee962fe76b788f3405392e747293043c0938545f1d78c00c1cb84991752936fc6f0a72e861f032253c6ba214fd46104033d2543400eebc89cff38ea74f8b07f174b6917951751737e2a917818fffe7172adda4808ad17522f3d76e16489d4b4098b049353c053a7eba7fc13f9129fe41c6c2301782bf9a1ad16f22b7308e97ed7a2f29f5f948a13be4117dcbdf069210d93d508fd03bbf6430f4f788a097f573a34f430304d3518c5e2b1669f23a3d7597a502da41a2f92aaef8922d8603cc0455691cc59d28a004ad2f9385253874c2073d0e2575c48a8c8ed8a273bc2de878e217be56d318821c4205c784b838c6b030ce821246ac7b8876376ac0131735b66d616bf6c89b1f183ba62d53913be941462d67adadbc9f546be9099e895b839875952e2845bf468326b8802d44931d7fa81a1b7a048da7065538c6700c68213f16060bdf5c0ee5cf25066e0a739f0816d551d404e00b632a1f76b254ff8e93e24f45e6ac9f7c62486cdab28131fc9f77aa440295460b38379d347718daa871929743a8b5f19237b6e58c1cfa5645559fe31b99ae317de28d8b7fb5e32aa9f163a8479635e5ff564280ca7dae3ae355fb2319c624f01e6ff1a3c5f6e98dcf5d58c943a49b49e2d10c6b6d7cf355397bcdf3cf9be84e7eb3353f7bfea193f1b6b22309838b081fe37609daf49ac30a8d0929c189ef58148d92cdc584bf9ae21b13e7998d7258811a22738f4cdb7de66967411dffbaeff7947ed330a20f9de0ff91cd2d282153fce0d29637c7c064416611b13ac77585d03cc8862b8a93ddf03c1efc52b49417ac9571312f6d216f74815800ed6827bcec56b0ee6f55ec8e4b8737ddcab9a8e966b7a64fc1adc3eb04e8aeb44ab5633f12f57dbad58d121997621c3fd3a750d78f4a9e581a0c468eed32f8e2d340c76f5604c2e4a9787a153c51588d518ae7f5f3b4d7b072b4a953c594a261870dfeecec4c3865f98a890312f3e6d0ebbceae695f415ae93e18eacb14616f40d69bde463bc2190bd6a73174c26f838b49ac8286544b2a31d8dadd6e8164b823436490bc1d6790de250e7b80de847a856a5bfcacd44c172672d52fb2c8e6e04972596105d3372ab4e9d68845843d457d4d54b21fe2f4e4d117f5fa2ec13d671d9cefaf00fc5d61c3e9b5b565ef84afa535008115f8e6d8b8fb8ad2744a337b967054c6c45da0a6f820bb02f07d51b7ff10820421db70ef08c24d9d913deca3ddd1aa2659cd1d349a9606b35de2735e058ce86d0fd2ccf147867aa9f9c236bb32d82c4bec7d8e77a159d5057650ebb161ef1940d8e64fe4b6fad3bff7621848d3e8401cd5ab25a910352aea58e06c4549d2b7907011e02b1ba94774a4a66b24466f641d376976db27bd23ce682fc4341f5d449d13b9d8fc2f9a67db302083a53bfc79ba2e607cede1c2cf95d1e529262cf48908c889944c4db1b5e8555d86effacb882d148993a1fe91f567b14b43424515aa75852a50f73fbc5751e11c2467e695bb3ccc95f77e11ee44d7c57927a255caf5a5d7365af8af604a097097855d4627b44855549566e00fb530f52e15c1365ba86aa7d60f1ef9929b2fd0ce8526bb7adbeadbba9ed7e9ee60a11cd3952b2b142b3c348d5fb7c1e9a1a7cfc8c75937563097ff18bcfc00cf7bafce608a7caef0f18210ea76d30be1c866697771de2027f4c5dcc15e68342c7b413438939db39677838867379bf6c85c684dc72d00e4a7e15ae21ab863f0901309a673d85563feccc8a5e489bd7ee96ff697f7308a97ca31d71e8db533feb347881d7891f9e82b15a2a0fd725e523b41d67fba6292d177599c7ef1f0860f341d7002dc4566c72e3862abe80711acacfcf8162fd3d6485063a53d73acd54588aca581f1cd846565b0216e871cb65fa6d77cfb46445dd843df7c7ae759951fefaeef9a4301223e296cdf5ec4a6730580a51ce69ae044abe974bad7273534f4b2f668a3a7c978508fe9b9058668e56f6dac062e78205229a831feb67327c12e1a0166e34dba6cef41a72af91ee00c270f1b1abd6aa80116d2f76e6861aae614e7d2a70bc0045dd5005acea24baecd3be3cb6b5fd4dc8e15e396568842ab82e638edc93438050fd7966217c5a523942d2f6bd52a9f414493748c0c26069b30c8b2ee1d2ed5748bd2b79071ec7c53789096ba2e78dc19d4a904bb73e85c18cba02139c429a6f634ca6688034655deacfc4881fe2ab2f9b2d192102ac72084a33cd1525f4e99e52e18e9a7a70fd139e6ffdbd18e9f9f69bd88ca6c51b5f8c164704cd5d5fe9ffd1da9f66ff0b956953f5443cf3b2673718b6e159961e8fd39ff5de5a5bccf92dbb5fc61f58a7e6ca4e5377771ba062faa584965e136af0395ceab6ee5a92b97ddc6935fb14e77f63226a473e7026f62956262d21328fd6a3540415efb91513938c4dd48fa8fd39aa5752591aeecf183c49a3802dbeec2b6acfe2f2b541dbd0957899eac632d053ead0bd0dd743a2f768afbb8e2498763544c6b84972a0a5c809c68db911228f4a5c7bee0ffe26f841e0357117940117299ada73759495898253da36357e0233ef46f36bbde01a0829acb0134653e653aa714708cea625d2b664818a34e96a90fc2c26b6891654ee3afa0156b18fbf336d28bc6f29c8075ac0327f20a9b263a6c821d292de100cb211678874f29aa7c94fe9aa33158d6408e370b23b8b24b5d952fc75a1f3d45624d11d07a262d09aedf64fec483c1663ffe6efbf45afb69b5cff2cbf373ea078cc268965c8754247042acda43d49ab34ac9d8d55023a76d42ae858731cceab3c324a935df0f226b1f4bd1b15843ad68ceaf81a630871d57663dfa60bda287e88a3e36906895213edc1a387c3306d139555679e40abe5ec9bb7af1e1eca841d1e58dbb84282aa4f79f2c7e27a5c8da392930be19ed9bc14abb2b032eca39f6a6b4dd63eea4917d8e3f10c1a24f02ee43b5e61bcda2351f5a9525e50e99244947c8a8b7e36b0ef6a9b5c81d7a463aa8f899f91286e59b5accfde4d60fa0dffdaeea610ebf420042210ea39e841c9369761633a2d91ecd2db4d9b5f6ba3c0234acf6482133ae1dedec1311f66e9b2c596ea7348d2a97f69d3c8f6ed24791a7099e7ca434561cac27bc923398a3791509002ca65e90a363cd24ca901171073fde0ca56cb5a3c30d79730f001f6742fc60d65fe92a9c0ac21c7316b048c8e912a8a15f1258c94b6f70b964e7761e5d66e3a197e36b1d9e7cf0199282cfd8ab6fb08fb6fc416bb5fdc27e90df8f19da219600b3c534eaf857dca7d81ba0e6d39fe200b79580a4d50d7049647c95293bf7da983507844bb0ae18970354124834f2a84fc7a3a6c0d73528465bde9f919ab23e7557e023c9576efd95cb547c0f8eaedbdf889c10c2fb006b1c283c26f7103189a0a7411f5ba4ac1bc1adb8a481c82c470af023cd0198258c02abe70f6a7d3d88ad1db0140f553855fd3ce0c20a1ba30cd2dc66fae3b1a2374f6b70a72b84224e03d3a7d368bd265009a94f49a0e9d4fe9008b2183bb51e3ea4b2ec2eac830f617606403226a88e4ca85ba83f634d5953b3507eb6c9c0085d13cd6deb27beab2e18f0568a506da251d4a2eb0e4e57eb3aa28ae0831ee55bd17814b0231e0d6642540e73f8e56f016f1e9465123f27ed192b1e6fcb84f8cf03dfc0cf107fe3bf68700173c614f5ec79c8a513fb332df753e14d9de20514002dc34d845d78c1d9facef86f9e19717d505b2d00aaf90da2ed5a60d032ea41b893208a838633f084e940bd228a96e6c4af51d320070e096541551149b3a764407ad92b9331eb82ee66e26e49da13e1d433483b5c4edf3fdfdb780f7b76a7bcb467fcdb5db6a9b0e63eb390c780a850c2c9f1d3683ef9ca949c03c6851f04602ace98ae57abcc2d99f330a369c4bd2b4adb13f70d0f421a8e386b572f33d16e70f46891f33c9f978280dcaf5b28a7e98b5022ff33517fc1ebe69fe7846f48fcb3d8c9c3790787d740c4cb42369f52b18c420f76c0046eb3da8b4999b28d6be977a33502608937036a0a770feecba941c08703eb5c3c6adc91e7c7cfb7475e3bff85075aa41f39dcb2813675e13516c0e2c13a5cc1ed685e00f7d8ebb6f0fd5a2d174df43f7d0601f39694df34d7287dbfef1de686277d6d0506c59f73a4792a15b49f4916f0b051f9da8a14ece65871f2dc653b7d5902ee0ba771985d9f0432db9fc22bf468567f4c2e19ac23a2692ad8e155d433f53aec7294c01c9cf7172715391b2c0b53986c6e06f77959780dc0ee0c4b6f2f6fa1753d8582f2bb14f260f34f33df6ddee30c8dc823f7be6c40d0df760590a9445f7ab0cf5d5a3f33e0967bb023e205f7737d390d0ed6c52d22726aded20d3d31928810463bd511b1ee3f6ae11337712fdb8a8869337b4b8819098b8c1b725127c8bc48646634f31ce0487bde029e6960dca8d27d67473a9308a47d1ef35519ac9fa56eb93d840bc5fbdef88f6017c38849fbec727ba4da247a3c560a08776efa9a3743ddd18e9a535d60d74aeb5c0092aa15728cae216fe4eddfc4b99024732eff574d7eec2199a2bdcf9843d61a01bbddcc283e9fd9283ed91e92af964d84e5102470ee5061f66d132602158481362fea94ab9c4dd19c1287d8a38d8722d6f9f5f0a0573bf0f623cc5f2992f881e5101e436b2724b1d671152fbfb38caaf67cf25d5c304cf72b14da11a15c47aa008969a616e67643f5491605971c9e3e2a4f179b7e59df1c0f031903d486e0fafe65e69a2f5c934bc8badad20619c13a1eb9214e989d3a34c562d654a5235fe87469f2400920fff5e0e4c8e5b3e3fa928289ad378bb674e03e9249b7632253278c9fa1a240a2a2e33efa823e538ad267e980fb453037d5e109184515d0486fd2e8474c8219d093f2ce316f5af0e04a8c63005d5f7dd86c152d77557c38c1ae1d168ea06f1d9514091072a917d966ea0d931a559208221ce4b0fac75980b87948f1ad561570cccd0daf0828cacddac0805ab0e5a13df399b61a5e9de6c49adf65dcbe20c2f81ebd4fa4039e80d0727a14e6e0b0d3b800c47ffe37468d21e84e3977dcd64d3cb41cc30007d0cc574f1d3e600acc0c9b8e1f9095e4889ba5905da0af2d97cb68f0c1143cdb3db961d11ddd6971812cc9204f801a7ff5a448c0c9e86a122026d4ea74d0b2def69b056757f7d7910981927ab0cdb3820edb735c170e7379146edde11bbb4964e27aaa72b9c977431a540784df74d9cc601d2a447ab6837d3798fb49497d5931b74aba2a563928daf05c4b95a4a1cf25c38e8a6d7e17bf36e0c621b9351bc8e009cb848c827b4526f0edfbdeadb3d734f78f00fc03570b83f7d5840fbaf905af3b54313055d95e4d697f53e9b61479eb8c428f87a10423fc862266cce54350e81a94cadab7d2f70c67225363a8e63cd002c92b769c4fd30737d444a3c1e897e367ee423b74841bfe50360a49efd0c256731975d9a38d04fa57454a25520ea4f56c1ed65683e6d012b8c8ebc3237c56c54556b3f8ca63f5c766364ed4794a2b15e8b70cdf67d552ed4fd8ef0ac159e3354efb440f323da3d1595707c49924dc0dcb06a9128a7e0524a5fd6b6510dfe539bea327f9745aef775da6ee55455505dd8ac5a2959ed411fda0daea56d25dbc9c7d3a67761cc3ea2e6c65e600e4db7890bc43b4cee6db84e3bdbe9b4c2d7c1e0af8abbb62f8fafacec372a5a30fc935a73904dba6a6e29e5582b1089af5cfe5e98a8591d2a282c4c5c8a52f2701e16278e659c00160ee1d0863905991dac6b290ea877cdf531db4fcb8fd7a2b0826e57601d65f9b4a895ba5869e45678e66dec8654049ab9743ddd9341138eaaf98713b4006b283134ec2a6cf4b12c624a4c620ac77561cead9a5c11a7d5b4b0ae3f92f72a4f3baf13eb4253498d6df7128cf935b075bec291800247304402200279ca59f49ab840062e5d5d3e1448ebbd381ca68ce2c0ce3cf0cdeb5357302302204c6b09eaafad7a019fdace6cab0335cfa1114f026b8ab91a9d11c8888b4e99b201210356f8a8c1cefde4ccee408bcfdf092f7f811da92e4f99d99231b3a5d8c58a490a00830300072589ea79c4dd0b6d3b2aa0c1ac0768a29a6b7a2e78127b2d8ca9c039506b28a3457584b47a73efb3375fe373e21e8395d4c1783d30193e571057dfc6ad94d7eeff5085548b88a7155b56e33b8fe545281765a07217094da53d1688992b1a98396a0451494e974da12ea68ad1a0a372ae160eae0f425ae227ecaa27aba4547f73fd4e10603300000000000000016fb32b0047f0087d30de68fc697678c7f4a95254a2004925e057185ff09d3b7f60b0d7f9af606b1846239a35843c6e6e221eaa9aefc0ff8b33a1b9a10855a59136d84c20cf675d6cffbb62999e452037baa143650e18e81733205817a8420440eea5490608b149366eeb4d1790e9199a04a39d0cae86ad4c5e250fac55f6953a489484938dc7521351a9c1775a658ead3404618ea8f724df382e94358c7ef3e669d9affbd0a2d6b79a6740464940eeba629a7bf7950a049366755b88dd9b2856a937ecb30b063238bf7e694ca78bf56e6e967bb820b340945b34c5d1bc55a60e5905d8c6ece19fab342cb977397a38d983b387ba6f1c846247b81e0d34e1589dc89617fb315eff0f4841898d28bd324db24056b6d5311d41bdff31ff881ac3b08d690a67481ee7bf77dc8f220d33dc1497b080b13a5c8660dd63951a76a1835bbf63505ca76d3d3c15ab4415097787b0571c0967db45556322a54aa0a9b8c5eb9007d92924c193796a3fed9debd71f67c6781a39e198ab227b7a060821dafc703f2abed6aae8103742128f087303107e1ddcedd96acf844b1b26cd795d13ef7cf0f184bc8cebaea710fc0bc0f258500df2c82249975e83a4f53dd74f4d1f692cfc49e5ff3ed5563110079df9f073db12fef044ba5fd1d76d4ed90f3c9f1901e4e57c63e6296c9f7aa36ea95941a6b01d7de20048714bb68964077e520f2d557246b92f3d567ffd4685187709e44d3f66a96cd05bec08f8667d6e29109a8240877b42031e56f213524ff6d881385da86e85765d20b2ad132ae191b5e8bec738df5c533c20fbcfbbeab8f0b5aebccaaab1a6df234a05bca892c048367f07e1747d4deef8536c19d2bf0c4574106bbc4bdedb30606f35559ddfff29ac65517477f69b75f6e6bfbb878129b684d25c3634e907cdc04afa579a815994ef6838348e155ef1dce5c4eae478bc2954e2b45ce5d63cdee731f398b98a8628f317fa1bb7602ce13506d84fc8e8852315df692bd0b6545a8d7884930a06d23be3d31b72878045d83018e7e6cdd437bc241dca6b53f4c9635eebe5bbc610da87d68d91eaab2c82f216049a49dc5e087fdea5c9f7890e05fb20f0f93ce8a008874d25bf377e52a346277069097046d27f8929c320ae1934cc5707eb0146fb73f98af7de49a234a17f371c14f778ba90308b7ace650cd667324b42ca790599d02579a38b64d5840aac48d0fe5b56a77e1fa6d034624da585a766a685c9fe2d1a4dc65d64588ee30d950643224acc1744b46a13687078d2354d68771825acf6f57317959b0c296af72499b1da75054f076135a59126d974ad6ef25764d122d35bd96ac0c4fe9cc47631eda9027bee307a74997b9bbe07a2da7c29b69de863149130be9deb5b9784cf603d0fea396fc5556a36b36a5b6951da1c289e6fd595943d258c8bc58541fdded69e7d97eb90eeab944804cb4262dd87fed48ee071c2fbf5a3df015d0fbe54a80dbc7d4d350aed97e91e2f000abfb8002feac203ec5ffcaa7033ba9b1a82a4edb37706db47199e3855f993ab02b6c8501e9a1215565bfd0194655fd98edd33282c7150a2f9369e46e31724c12b870223b0c9678f04a5f951860018ac96928b5d9c18ff1e06205e27e9bb15576a1a651ead62b7b8ab11057b352e1385c60478c369e34c320fa1b36e856a1f302f1da053d4b220fcbed9be7642319024bc712f082a5517d9f6dfa0c2d85af3d8660cd06e71e9de67053cda59926d9e0845d30939e7aecab1dd26f186239b8392500821d4c8dec5a8d0123cea75354a7dfbaa5ebcc9d75c9a307d633e4aae4ecca0a6216c1509dca5b052644dba73a0244e29f5e95ad2c09d313f607ca6ef424fb74481d4427ecd9b04afd563f46136cf2c6bc596a13ada2564c52450dd9b451da52cc1d82002ee68acb1caa38cdc2fd629e10264e908645eb859173b698918ca8dfa2e3a9350fc0545cae228890316c82f491c281d15bdecf2bcde89e9ac435239eaea24a8cddd08aac147633e822f2c57aae5f59ae901042cc15b045a58d78099da9031528e8182ee93433e4183c969cdef637d421c0fea042c1da85f5f338ea9be23c84f0f53456a695ae5db2d54d2e70e5579a6b81d1d3f5e63de0c746b0361d713d2d1e83118eacc7032bbd31c2ea26bb5d55fc47d7b56033a68c567cbfcf8856a7cfe0d37c59f660ba3226d409205a73b5a7b86a1bd6384e93e1486c33540f2e2e29551294be97204cd85f29d38ff11fb1d3dd34edb1bf7eb9e05c5b20318dab896a5134dadcdd197c74f94c0110a8349bf29fe246a0fd54063603de7a67333ea3830779a8d9bb6f41a82ede66103859a25c9116193c95d4e6a9029bf3e301f1b77c5f31d7c78ba43f34ea027ecd7063919ca1ae583d135c008edba60b020804571e33b9ada9b9072b339400d356a54fa3f4000ab7cbb8337400d2d9f130755d87a8b62a91e1fe95a0999f24b56d89a5ad291b0ee96aecc43b08aade1223cf1cc231bd2a18aa175dfc7064692bde34b0f3b6c55e2d27cbf58554484649f1dc52db19bdf284ba1dea6802a0808d87584a3ed0902988b5d1761234ae091ffd917385ffd419224d29f0d8393acfece765eeebc62406edc65c54ccc38bd118f7203e5bb12d2dd21024b34984731f2fccef7d9e292d18c6651d30b7fe47dec4c643c252c2186bc0ddb095c4f96a7ef7d7d73b0e3a55feab61ac088e03fc5d79b0cd44f538c0750aa592b4f9ec5f2f35446c6c4263c292a3ee1486ef7665569d36eaa9bf52b5ebf394facff338923e5f2a802272cf866a59d325f6893c913c55fa215533add8cedf52a1d79098e3c29d4a54929e762d6eb9f23d7dbb69187ba5c11f441575e603858f6729895ce534ac0261f5f88eefc4e3145a93b960030dfa4a5a8a5e287e932ed2375fb29b66d6b563432692d052a43f6caec6c9be33b6cad83e354225a232d056a614031923f67a0322d9c261a4a730a1427e0537749cf4fe20844674b456eee877070aeb10dc5d424f4f1b3d0fe42ef68ec48ed4ab6b2bd4eb52fa868d510badd443a7b22f5c370864fbf279774d26c6314903c2b3517276477db9a4db8f6514b38eb5e2bd5153b73288a0bdf17bafef3a9067216ce5939f5eedaa3dfe8f3a9a0f10b0933af5b37da4542e1323c22e00fcb9ca2f12b6a644cb6709cf491abfc86f8e842ca568de096e36714e2e043f10b039c62822714fdc65818293c44f03c984546f6c20177f212dd8485c20d07f809748a9f93b666daa2999f044016065afe45a9a8e865379489c1e7daeff03ffdb0b9cda86d913e764676112320cedf6455c2c9aea0df7b90c6462001f4cf02e4dba3afc85f283ea1fd69a8a73a9c85ff54ef7e12515f531a9ed0cfb10fa747cc4131737cd106ecfeda49ca336f9ba8636769f41922dc277b5ae95b01bbc15c36c2d93301e4d87279723c6ac79800f6a9a0533e99b3ce272694d4c6df941389bedf36fc7c8aee32ff954705f96b64ee524b7178af060d8113340b142325581839bf29d02144bbabf4cfaa1f6e178412098e2c175b5132dca9288ea1ceeb4517d78883e432e0cede03a59a6260abb55a1a2410b74f1adcb9730d377448cf8fb57d5c00de0b4000c54fe7ff516694e46e6c2b9b4efd5ad11862f368d29b05d0ad9a8b7891715259f00643060d5b348bd5c355fe294971590ff4c03d1513d4cc91fe5bc32b9531560ca50292cb340a1a3a5d7d5b6591a4e6db94b4807b4d33772304ef838912a142206491d7f3ef8c8a2932ef3c04ca2fc19e726a10ed8dcfcd0a2ec81a049afd6aa24ed7a0df80c809a6f5963e93da0e231b748d0099e12c2ce8e0754eb3d006cfcc381ee495234aec6568c978d7e8f44d6644c4638ce425ed3800c3f445ec7fa85adcb23cf78a18fbb5deaa20b47c12896377d032a67056ea192b187ec4a44c16cd7b728941a087a45f9915668a7ce2f120aff50660d66c8477591a4d2f9a79f39f40401342a5aae0ff8253ac62c9d46b09b458f20069f9170c1aa3ccaee649a3fb37f7de2cec2b7c82a07bdc707580a3371b2387c5e110eb9c945d7013cadd7580f3b0590777e7af620b7e0bbe389b70eee42cde184413cd3530083f612069796c9fb81fbcfcd69de104e8de56afb2fb4998b94100b53aad94f2cfd6bfa3f7d0158d549907c9a07b45a22b1a4a190bfd4c9b67132dfe5e5e5a00d37ddd80f96c1506da71baa95082c2b85e0d354ebff3a329868bc5840b17cb8fc4ddcbfd195e447c17d76632dfaaf6b02c5981f88572791f32810c8272408ca9c12ab24952fa50c5efe817fb21d204d59eeaa8d8282f787a92d1cd27ddc7abf8ccb530dd51f9337fcbef197823ef11a23c76e121079bdfa523eb6cfe78e2e8cec31846c5920dcf6700bb3875b0c9fbac31a6e6a1551205e539d39e0f333197131340d9d74c5b343450f4e0dc756d3dadf1b2d25c1d60f244ce0923682b96de22be8e1fbd6e762cb89bda68c597baf8c6c814f760050381041ddcde5f52dac92dfb3b229491712cb1ac7365dbd1bb62f91b6d124ba65fc8d83b532c8d98d96a2e131b8fb943335cf83a2de63e17888557be979c05c6f05550a49abe1a9b00cb77b756dd16a8edae167f38ae784f816c6d8b05edb9db093e5359963a927370b78f14bca2f472a2974b10a37ef0a78cb78754dcb5ec8f59d5e2f33523aab33abbeaea56a5c688e498fee42d202b0c1f08332077ff5360c189c5d30c4384cc8776bf30a67636c711a1396d7f139c57b2cc365d156898979f9a38a2c02b601ca5448e7a7c2981df170219f5a4550ded4ab499f4ae1e47da52b6c1ea441466b6824cde0e2a53bfd6fc636274760b102edf719473eeb4b68e70097d80e739f8f2e1e284228f731db44d327a52efe258f78795b2257dc3478e0a1732e741c0f68074a5520ce4cfaebcee260538bf4954e669cd17887bb25929330b4b2ceb750d64da0cd6ffa5171c4ffc104ceae609fe777b99908e4f46169144c07c5250fc4fdb051ba5335297357039e5957e77b177b974892b19e27f4b7a5e2522429451f84a2b5b2931fb4d5a47319e1df8434caf337784e6d33783a634d9d4a4ac27bdd96ab4684dbf29d0612a1419ce54db6e4af8209ca7da98b18a1eedfa2473653ffdfbf1472229fcf4c528a99995d48003c85511c87957a862b5308b9d2977ecc82b03ea381336fcae95e9c5ffb60b0b487f4f1c212f7f11265d928d095a16a638faa646ef3969c7cfc2d082ddeea9facd508f2773787508e45a969972fe54576120a834b2d553659efd9d9f605aee04c2a36c12b82a13a97fdc2af4211c87585b0378ec2c0cabd769479bf653bedd83c6796ad4c878114dae20affa06fa66daf9cc0e658dbe87cf51637bdb332e6e231ec30b371297d0c7a73d128f4f909175613243973298cb852b008e117ba7d41e71cdd1e24c3d4f6675a26ac8ae67fd8e7e5945853c11a153842196cebf9c791370556e5f3a72deb5685af7aa52ded66bb4760f3bcee98d1a1750426151d7346a9eff2dddb9b7b3736ac0bd0a590e7af4b25ed7fc3b52a5b52c915a3584e1b2383cdbfd8138dd545abc2734864fce88ef2cf12cabe5eea478095ffcc10fc512adeef7d9e8e27d26adef3e48de380e5b851d2b9cc1a8b2f08081308383f71cc61166efe9c9a139a662b94154ae757cc37225521d5f29d27347606fd2698ab7ff3f2497ef1c8efec0ad281f0932f36a8518ab40b946746c5661aeca2ac446064a7c823b5791f2687fbcca8fd2ce95e4994de25a8fba056ac2d5bb8e5a6f5b312c0893c8a7bcae58dcc967f3917dc3c97b3b7ff9653fdf087ed66b847731656e56f83829b0f20ae395d6b4a7587dc796603620553f7dca83030007025608a8da0159d01ce5cc0f7c8f453c62ef23ba5cac9e87d63d854abe7282c506e5eb6dd22d8168ed895e19432694f3f19f0ecce079a1cf1daa0b7e17a193c90d4fb0a155818e1d6fcfa08ed64e17bfb2573a3fb74a4a1b9210d3bd1034f7ba01079fe75b5448b77807a19f418cc560290c9f9ba4b670a9941d5134ece76c9afd4e10603300000000000000018a6bce01807c9bdd76cc5033fcdc479c5044a055e47db9d282b018dfa90f76d01b9fb00e4081f09465fa45160873ca33bdda257bfb2337278edcd9f6abaa88994ff9a932176975d5f971b6a699ecb55231d2e90f7f8a4b1c29fc8062c17de71f5e6d2489be1dd5b8ed07cbd8f5bdacebfe98569f88510cf266dc9fb9139d654f03efecc270ec323ae588076a64de344f1eb1def577664ba6dd07b1856b68deb5b32d4335e25ea784441dc70039e8fed8e56d9ccfc79c79614a79639f27ffa9497abede452de0cae56414662e511f0c01a938e99e88f5e0a7bce74530b4cfdd5b2b4994941cd7d83739f223ec0f18ed192a7d4730725b2c123682af22b9a1f0aee048fa8610fd813705757070838f826c37911f60a2f132f00910fea698e1f0ed735d7c75772c40a76d0af7f349df5974f95147fe8ea1cea847557889efdc3e836abaa04da71be14cba79ec7ed106c35622312994638863c263482e8291ec65a0d9e5c194c1780781c782684fa3f153823013df06f44ab05e0ea00f396053e3dd784819bd0a616eeb6e4ac8a11a5cab14ef40151fee4d7459dec2caa90e22dd2e2e01c082ababf8024a6553bf0e6d085a65948ab12598ffb1ac59c1df05df073fbd015337355db33701847c803318dd348356a657ed1a50344bada887b2559a6ac186883f5cfb2c28239ba699f9747a967b03e8866dabb72cfadfcb3fbb4c800bf3535c22b1d62a372de4113a2e2b90e85f7515ce84eb0152320c176679ce330c8fcaf17e90b62253a139871e1450822fae80af81fd1bd9dad1b7038db01aa6045a8db0102d7155a5ac6f72bb2afa06950d03ed94c79cb14594f6a01eb4f2b42d8b314306b1166a236f239ab47eac097e4c09126730476133b1d45e046ab098a8ae7a43e6014f284dbd83f1596db401452282471848937668b8392a38f961f88618d7aa9f39c07cece10a9bca1ca9749603a7f49791f553199dfa74e6796412c1e6a2b36f26c9030f513418171700ef7b8839c0381ab346950d5eba26a9d7b5ee4044bc594ec05aa8dc8158304fee3db3aaf0266f64d454e3fb0f5adce7d3501b743d85ffe4e889f0c33ffcaca5fab156e2d3c8f1b64f2c41c556d5a7842ce91c826592506c2d66522e9962bf9122262447105eab88e85cc2184ed0b829cdd9b3acdb9e3a59504a9d03afd81f96bd395861c6979f7e0a1fcb392b347599e34786f9a3998d28d945b23860011610a853237a0b6f249df2d99058b83e4defbc06d9df3bec802b41a2fec1b8510f912cfc67c5cc6c35150fa3d08348b5e4bc6da099fe70c737cd8b1c65ae44633e167c9005593081dc8d136799bcf64d416d0820b36b3c49ad61145585a4e3255a11ef4717e844ec52a40b6277499e5d4079de077985d5cab312e2d886fd2c44d7c0934e308e2b04e0c73fca15e764ee4fab3a24451328b5eada8f9cc79c8607d0831e6cf0d98e819ab89450f991a327d957ba2c01f5d9e5496fe09a173103db2ddbd46ac2368f6c1b37d8f5d6c5b0fb13e44a704f2b105a10bdcab372f47c7f316a3e3509a6cac2aa876c3ba96400ec9afe3e39fbb8cb04dec8cc586c62cf2f6f55fb373f5c59776ad1339f83e0d2d8c5c5ec5730c6258f5d7a3c475e295fc42d3cdb1ed8cee098fe576ad58b5777bafe0bbcac85b5a165589519e66c7306db586f23ca3a2fe5861b71303a3a7b27fac2863bb497f2e56e13478c027d8d0c4ee23b2114d0aab5e055361a06127780214c9ea4d7c5faf76a22e957eed5d9b23bdd9f433e05d6dd1ff19497040c7142b2a47b2427b844352ee0d16bb7f48441f13ed04e10a1f5573294b2dfaa0615c218b32da91d4c3ccbf6e2fb11123555d7c8ae790f0f3bbf219b077da42fba400bfa7c4700039cf7ab83ff4c8f2f78b118a8c8419468d46db65d3cba24ad78482366492a300057181d85997980d07de9607bdc78fdfbbd81bfd7fd38b384b001fba13fbb49bf0754620c781fdb2a7d9ecc949c0dfb31ad72d80669ba9eed13b650f7e9b24439a36fd01ced68f0b9e7786ecc285898cbd9c1d8e16a56a64f22b0fdc238e65d7868e382feeccebdd8ac0f3f07cf7c1498cc579ed7902fd69dba058376297773c853150110b557439a197d354e3cd619df5c58de5318795444356be6d647d56fc9b86a859cc7a759d54210f36c9baa0c2b71f1209d465cd44367ad10f1790de61c7cd0f4d1bc819d750324b15ee03d0d0ed62849796f1bf7e28c76bde2180482ea242a906ac21b87c8cd2a8482d8522cd2eccfe259b14cbeabfb814886effeffe3dc2fab36cf01d87759119dcb0617d18b4751f9e34456acbad90f090c799741189404b4e41568e63f3362f1806e31c5b46fab80e4c88b74fd812fce26fd8be6854568587d7ad4fa48ce23d30dde3aaee41dcd80a52a4aeccc5b831c79588c0640b2e72f6a16066f972bb4328771884b75615ac67752eec45cb09c00535a88fcd04a77ee26fadea9f468a1e3830a6d867236a0dfb3110a039d5367439fe4bdfef0ec654b74ffce087dab53a3c73dfd78de16a5b484aff9b0d87146fdefb708f2af7136ed72850377c6c62281201e6c8b3dd66fcb5ad08b5a1b7def79f9b57a069f44ea38f7512c34d19561e9fa787d2b556d2ff9d80ee4b48d13e350f567c162469fb55bc46b41326b5e2a881fcaa3e46bb1e25eda14e41042346c8c803104f4bf7cfc8b8d3066cda088b515deaab5117b280ed6dbb65b32c464f11c95bcdf33e5fdbc08e770037c45e5ecf66c5be861be4c57fb139588da85db47580d097bd3b566280092d1318c2cb9ef4ce36f9dc5209b51137008ea5d232be54f0dcdf37054e02b082d1f4bb6c3c70b2055465f3809566db0a7bf125c35744b600cc688cc20d2cbe891d91f9b94eec38194902910330009498fda15e39502917580e586223c165cd7fc3605b741aedf4bad4c4d3e6b775da1f14920ae9f1ca16be31eb3b98be97d8948fcd1acdd4d4d640d0e9ff1e25776df6442f85e0246a94c87f07c3e46f850c0010b0c441500393c738db192ad528fdf30738ade085f7fe1aa64ff04fb6d2abb661dc7358706a51e274e76effeaf590533d8871090393acfd805398f90272c9f009da27e9eb4b3f2320ea0a86316539fd39a8b94405f63dc74b47caab38f2a72737dddd45b63e608ce86b4e4e552714bef18b0407fd419f3d9ed7449300f44db5557587d862c16da8104d59d1f36e866bcf159e58f57dc4aae09790e16e4759c44d1817298aac924f78d2ff908e41be664cbf7388c1091dceff0b7451d416f6fc2c86eaaa7cda17f88ef8a3e0e38375a587b16b1bf94e554d9b377cff05f58f6b4c409ee659583e487a9a07a19a4d5f3ea66d44cbc6bda11d95c64c9d0bfb2683775da890d12a8101563ef4a0675f8b45963ea601cf4e0d8eb30ccdea3232d16ec4259b08f3f81624085d09f1e22f30bcfcd39e707d1bec0de5680ae6d62366e4615d462a5ec1bf3c5979dd141f29dccb581549f3c6da3d79e9f93b86dd0c0a00c0ae72a469921e851a87f7b09224e2bb7c76563eb5941b75c9b9ebaf997c0ff02db8f7ec630fe9b9d729a1eb13e625186d24ebe8fc8928d535222db7e86e33abcb8184d674aee60a2368753751deef6cfe58a65db1a782beee3e88390b4ac94c4085f6a27b3c6f4ec6000d4bec14d2ee68e58f409547e983e55fcf6f1b7dee0c03f8ded9abdfeee3a615fa9ff40e5ccc1f96a69b29f6c74d463a84b7326fb572adce7449c513b5570ced7c282d542be281f883878b677ac1a3c2ad30e22001cef97c8c30ab7c2e15d8e620cbb321f8bdf3c935a437981f9095f209e06859c15fb91ddb0c961a320e20b9427c767566fdb8b07b34178887ad189ef2637075c9eed26e3433e2eb93f138baae55224ff443109a873c2a92d2da937cc4ca46e2787027418e8df16a8ff164e1c527c4fb2abbbb53f96649ce18178b24a98f8c6f843ac7c1b9e651383eab7fc50af9534a5337ccf152fc26d1351b2d90ac78775ceb8cef457413c9916094f907e968ee54206c595104bae164f6e84ce9391d3f091d5da0adbfa4c32e705a5528dcfa57ed9e3e41525589385b799518caae2ec3c4b3b3adb8077df13e44b90d7dc47b9920555e7e6d419326b5fe1c806f0c174d0db1cf3cbae91fda2639996a965682eab241eb6e82412d7ff2debc030520859b474d65cc9d9ca7a7bdfbe64a0f288b866e42114cd62080b0b539de61a43b3ad0cb5c768b2fdc36757464af8f25bf2d5bfd8ce8b029b62f39434f80df63acebc7f3b3ed313d4face94be2386d4893cb672b35c9e346de162a4d11a13e3e711b1973ee43f57d82f86a043e9d5ab36024328593e67c8dba959e2c4c64d5be48bcdbc4b75d43ac919b6d768294ab3818fd2aea1cbb5f378c97a13f0b162cf6d7f3745baef6862f74d8631cb1f7181029df2b6c6ddff97fbd240a0ecd3593e8ff286c83221813ec4346915a8a9ba2025e7bb545a5372be950b59f4ce665b2e2188a5db21e52f33e89e197a5ca8d0f8fc1921f291650edf4561fecc0e0c802cb917a94d881b7b6b86f65634502b0f5893dd339ba1e2160b7ff2a28d68d25f53e2f1001e7331238b7b75f70322bd5debffb27a84823a57a1a8b36562b7726313ee770d21da76ac1d0731297cf0d73fbbd7a111a965f56446174d5118eb1f122b77ca04b910e268b0420b95b9dd43f083559cc608909ca31fad8ba978b211a7a583198465a344ceb5cccfc68c5cee065f69b692e49d01a9d91f29148fda8e767f605d8a361f27b6cd43f5d1c0ccace1fc84e510e75d580f3340a5296ad09d67ea96ff117283cf15e5ee7f5fc38ed9c390322718fcd6367a1c5bb3414492cd74acab5ea9dbfb168ad45860bb17c0f9a62cd0d7776934c2a4b26368fba0e668de85d6749c05bc061bb2d98d4f87f7adebb8b5045d572f70ec5baaa4b7b61abb42fe5d01cdba6c32e3599f9393011bdff7a3eb0e2f240b00f67422657f67ff5a74f0c6232d9092fa7da5429565649911081d7b0079835b6a11623559b3069a8f6e5c00ed39f9957b277c57f9354103fb2daff8363c8f5651c6f79c1b6b369ffacfb61fe71f8e866d10c3960b8ae7980312a84fecdb98ae9b6d2705292677a4f184294136095f6266f84ef5327565f349fb6109e8773b7054adfde3833dc994d674bb3b7c13867fcc3420000ef3c7be5ca8fd361384fb1aa04f2a08909ed6c20a86ce9743a38f3d90fe08f88b13dc3fa4b5a373f7951742cecb957e9bf9cedf23d13052222b9b577da222287b2e9712d91a6529e9c846682e4ed7a64be6ea235cb1688b20bafa35302d60a6122a57d735e85f8c483b938d6b933e0e29e52ae9da9dc4e9e4c33990c7cb3cb0eb1f876bd75447759921e99144559c511e51c7cac3bd2bd8e523d15e1b01a11c7f6fac154966e11632cd8bed9bfb9030ae5eb1784ccf20b3bf47c20fd4085d902db751a011928cf117463b7a1334000bd1ae68c2b467d5b998bf1be6f119454f07cc651f6264ff0843e18fd6c4e7c9875fcb4870dc1f11fd39e415b4893f81c180de2d09e4941d942c27cc7b17b1a33242a380b18cae7bd67ea49f7d71f28c7592bae562be8281a0e4bbc9e5f35e44db07c34ca1fc72d3c5cce645afd998edcf42d907b8e7cd140f7a83d4c5782a08cb2e37dd4c9f5cddcaadc2051f852ad8f2fef5b2d3161c2d24e709065db507e6d5f680b9075255a977cc092f3f893c70e39649a842c4ca491ad6a7b282470c28f43879b2204f2cf6e48c744759374657112ea1923289615d2e3e2df6f65578663d4196a59b72dfa98f306e23eaa86b786167dd701eee22a2068115002666e5d4b7686dd5b7951b3795c0b4c6c15290e083030007299c63a202890a17310328540685462ae2007e103086580efa3f1087466958e771ed3b44fd53012ede8c2380ac27ea211e392c2d42aee83aa3984550ced069f6cbf14bdd7410ee4211d61c94c156e7183370dccf7964552ec081c5d34fc304fb3c90a77933d0379b37c11292988866102e7c62c0ed6543d8ba47a13526ed6776fd4e1060330000000000000001944f1e003fe134b5ee3189d6eee87d81b57a1dd8d05d96d2659fc3b1d79d135b194870e79922d1a1c3956cc0d541bbcba70a80bd66818ad8da670562f7550f8acd03257acca9d4dea7d903039507c7a79e5bab415295938dcdd4abafa2c60c3f9b569c0f69d71c286da31da1732f2a5f07615456f83462bd5d52efaaaeeb7c9e4f569bc73df90782a0533d8832753856abee377a322a00c06942ee2ed98368afaa12ab2708cd6afed4aceb52bf68bb80053d9e9190d6ffc0ec355e715fd1c7616d806bd2d0c4319eac7cc33e3fbad9f49afc9c350d6f46ad8cd20cc99a7f304b5a9862b934dcc464b0167ce9424f22edeab320d7afd7a63591d079153df40cd63753e45babb4a14ad9610c79634280a4cf12310aca855beb9bab0691f7e3f3ea928af82fe79c61bf8f1fea647ae9215769e52f204aee66711ce76782aa929e8f5dffb2a3b053895b5c703a2741bd7a33940d6ea68034125e62a1ae0148a98ae2758b53d340756c01ed40b00225eb040dcb339689e7bd3c9f7b49f63035726ec276137f6f114162b9b7c777b7d76c33e74f360a77ad9e70dd77e68ffa7cd368a4e2a709b647f45d1352be8e866a4c10e57e7cd2c99dd1902cf69030aa67ef92ed13e6cc65fa448f348e6c1488d1a559de2daf1f44392771ac574b910595236e7d47c59187592f22ba4720552e47f708e6969c7f3930f6990300224f76416fc17c3a2b362526a2fdcab96d97de78e8a6a308283e467df0d8fca3ba7a097f8bb57eabc3185d70590a10ef8f22f0e98bdc2bec07d4cd4b32e836f14ff4ef94583c18b9d496c35c10ebbc948541eee1a2e574cc271606a3afce260bbc930d2dcb9dfcc0cdb0b11764302d770f3315b0cbb89d60c244ff442b20114432837943d29cc7b57d62927be6e180a2e9fcaad5e512b116f4810b2edf539bacc88d929d6e9e467b8929221d1d94f270d55b26f7d1ef3bde741941941d09f5368d295bd0b7bc26a5a17f7d05e8f111615cc9c9268e62c7d6afce346bd90e7df5d5c5e3c35e35ac58f2e4fbd27df087d2ea563d523229c35065c8fc2f45fe559f3ba015a2ad1e25dff624b3e276e3a26f1c79839a46db1eb568f3a7bd1cba3e70154d3f841f83f3c5311c30d84f0271c3cc6ac30b76d03fdc8a35219db36e610c521794921c21f2677c31c7f62441a3ff4c3ae51f84bf8751896b0ff2ba358a5b682f6b7c82427cc8f237476cf2f33ec253ca282f0e76813b5c39d4d39daf39470a2fe5d7536597ed1f3e5eacccd12d026c6eddae22cc2f824463e409fec56fd2e8a37a45bf3dd5925522cdb5b2be245b329bb906a327a7af9f76ba39abf2d968946cd0811f79492f1137596307afefb7f2e236260f94e3af0a0ee3a2d70b4d4fdf4e43f0cf9db48547bd3c1f0025e2ef7df60bb7106a4f6f7d6a0918ed7c205c9af79dcdfa7604eebbc1c4dd4027fd591afb34ee90796daca1d31f3300ba3d9afa3425ea51003e1b807a6b87533772e83ea6da9f6af8959cd87d1706c6afd98bd287a5b15ec3adb39edee929094a8e8d1193f4c857c023082925019adb6ee174066db2efe400b6d2dcce7ed95f277358fb7a5147408cf1b5e57b3b70c0ce0aeab5f1dc076dda4e3506d74270e6e177614c925d16c9987338da03c8eee837370120d5c3b9db22be11fb6629ee3a3707d9beb266440d093b016f431e12f8e538ec150624389a2445df96e8a555ecf74d6539f094de7297323c0923deecab854201d2199074442b8f13f56f16bed0927cd6f7bb0377ded7a9af040f7b9751c92d631073d659526c72188adf502d7802cb0182c4eb0f3bc0d85f0dc888a1ec4db0ab1d92f5d32b1206106bbe73bdb1c6aea7b0982b03df95cb3392ca24e046aef46afa0eeac40c8d333bb6eab09360a7d0a3c4d9b5e14ed06cb4bbaaf2918ea03b6dec268b4a326ed0063189196e08eeb5ef3013d64faacd12b5244bb927f5fd597565cfc0ea7882b03204b29e7457774745b83f29c4251859215af276aae1e6359659748df526840216efb41ca896491585a430c565fd86b1af9fdac03a7f5d91c9f2ec7b7d8788c416655a3ee3dc7c94fc1624f49e101ec6106b19a6552d9b0a69993aa9b22f550b0ca684ee54c5067c5e5e8e66ec76ce11776966a4857a4a4481ea153ef69c0006393395615d279867620c2a83941bc7117b3a0bbdf9c59aec020ad8472eea1ec2adb957d8f6d06128039846e7987d60b520f95ca54ac3a14114dd8c553aab25e04597ce8aca711cb9917748d0e58497fe3d248d15852da7a532b103d258d91ad24768d7f99f5d4914e5eb08c5c69ec1f0708376ad457734d22d23f2197f2c4bb7326a2af2cc1be87ed78f6aea444638ece8e4371fa6b3ca26d48082c0296dde972d9b613ead8d4a7f13916a093bd99bad57ec024004373232c191a36cbf24145c3d07e3155ef18aee937c0eb2893bff4132ebcf219de041cf0487ad3343b6fdb69bb4a53b96a6ebc6e30a16c95d7f33aff690882031877f7b0b856198d11965b3b1741b4f77dd80f11edc65c495ab032fd8a71566d5d2e5ff9fcb6fff5c82e695223ed373de3783bfd72f833ec2589e32fdb94ed7705b072d2c06bcbd6b6c26f39cec98d851fa4cf2182559699c652dad7ecfa9f92cdcbe3a2ef28b0a45573cbb0a23d983581f6ec4755dd6c6aef0ac2df82c3dd90eb29d14bbfcc7a70743a5d4f8ca94b848084933767f943a97df3ab7514e12c90ce2de7502a9687577e4fed24f252353836f98a3e7b25378f8d2d64089b7224f320817d0ca455e9a025e4b1c8953951346ded61f8b010c6fcef02ca767a116bb8cd17df58a69e9554051324c66699f3dc51895ac262cf9f40e3db2ce425374b87e262210f1945c8ab6aeb774c6d29fb299e91221b6086e74bd5064e7d4f6edde94e52f789ac9252943dd26a470c8911ba10e00faca77a707ad00b17c865fabb4ac1292c767d7862b0f88fd10a706fb7f25273dd23a47190f7583aac30f56903a6af2a53c1147d3d25c567e6cd8d1b7e66f6c79c2213530d78a3a580864a9a88b9f0639cf3f7fbeaea83ca5f5620c37ecdec12c48a5cf453a618960a458e460b3cec42612f891526dc8ccdd2126c3d3361a440e630f1e28e29911de43a516e2ff68013469ba8da4987e75510238a57c4fe2dba2d27baca30167b4004f7d16ae51e892b786d1d1ce49004b86a03ad1642f880a40ac723388f8cd6a7fafa5d24ae8f890c36e2b3090923a135bcd1a63e71933579278b51d17b2032f9b6064538029c14de6e371c5078caa81b1d4bb1264a24ccdea14963c755f6f59894e476b105cd1503c30c42f7439bc38f0a942d4d548eeed23f3f6b0437a3549e8c037cf8b02a2bae9a17e0a625c8ee15e662c8ff2471fe6a096093566b0b6070eef04fae4debf0cbcba425620b29988ffc99403b633bbabf642ab6ba50b4839682e0aca599bcfe6b9d8ab25ffe176f1d03cc3f1c019656a860764131d2b856ebf9c147b57a2f639a387817ea3fd26bed54d26131c68cb78c1bf013ee974d2a98e21a5d17b52ab3662043d268df9fc3df15f1d310bfe4b1c6e6cdb8cad182c5bdaeb130301a47a3405b3d68067821817668d6c31247a2ef709e10b0c650390a029fcf6f49f6f61435adade4a23e067fbab9fee9b41e844f93369ef4bf125a2612ecece8337d5c367a727bb393f56629b1f1835ab8c82197de24b4442697d43013fd056b12d7f798e567711134dd17010afa691b76a382f030491622a8ec99108bf1a63a4d16cdb051ffd304b4841e32aca8cf788a2a228c2332d62d70fa5648db427cd46ff6b25c9eeb4b6cec66ceb8e1f6c1014be5b8b4cb995288e4ef833edfc65d0e921831e6a7116318b644a67e406c823bd4bf565842ec98c65846cb79fd4e3f641a56a3f1607da4a5369fa8665bac3dbf60c60db1e82f15fd6de7333d0c1e6ec39d18ea197ccfefbb465c9576e7a621e9de1005c7d057c0054f5235c2a9f9b861e66a852edde3051bb2e3d9a9bd94f5cee2586ef178ff8e6453766f13e4189a953d2a07fad8ce2798a5e4804cde19729835d458b79632db9ef793b19264167cdc8a45771b24ee882a07531e59f529ac965fddff28548a5616beee0fe568c60f0154e30ba6dba2f4ffdacf3c7e865c38eb8128a1d0a33d3db35d1dbd4abb2a6137f4ff5146ef8743e974b8e39ea2af18e59e79f7d8f19d9fd55f207ae08f634103c7ba3d0e51e815870c4f2201c48ee932b855b6ec5d0620670b35d933886d7c5bd340a8be97da01155ce4f9f9cc3a3348d6069e76f86a6b24293c47f0d7a5103ecb5046c2ac806b66142e330856bbf0b2f13b6c5fc61919df8af8bf4cefeda6575b98aa1258e38768110fc8c70df44c1af37c8a37a25a349836e0b03f0b44b5e7351aef29eaae8046a3053acdeea934e4c9c19c23b72d40b8ef2a5e1145fe30437aa98075036d8789f1d118278822ad95463f310d181a1ad307a5574a2755ed1f897ae80034374d621c3239fe99567f33e9276401a60be33b25fbc9b567f0d4dda1b5a1895f6d7da6298612693ccfd8a5c6fd70605571d07080ad946c6cc4a23fbb951b572ca4495dc341f02d34e4c65cd55e91e20cb9e9236cb291d31bf065edf734761666ed1635d9a3a966c05ef84187bc48c78e4cf3ccf25815028e80f5dc4837e52fdf8b7bb508e1c89701e49ab92a2a7bf296e63f3ba1cd254c9fd0d46f03b3ee0e7d8474bd1bede4047080f704e6a97c90714fd7cf8c2b26031f83bf712a42fbb4fbece25e068fdaf9d9884481bf52e36bd0354b01e9d2c42bcc1f4cf3714b3e10ffacb25d179bd85abb48b4ec52903dab11cf70a493d5110bf9d8b1732d3112b0e3de13b3f2c99a484b7c1d6e889537e96d3528dd3ed5315fafceed1e9cd7cfe145ac07c2a09258cebbb81e4462e9edc53e3ddfd411b95c70cf7ebbe1e09598d2b18f573c3dfad52731b663aaeab850bee6ff9a874f00429779a87049a2845b68106db1d1f0ad17747661ffa8f9c4dc8144ba641c3196312538418f53ca44594afd0c6341c407e4049a6c14c8fac8b3a952c7eb1508288d7e3bbc37ef9b3dc8ab9b856ec80c8a06a3f1da9f8190f8b1d64f628305f9365962690e74162e6e50d916c6792b0f4a6423a05efcc3ad63e3e6acc389859322313c7268bbeac37ea629b6b4353aa48f7ea52055ab45ee5953194ce2b0ddb0054943cb5d02fb7c2ca1740f8101539be45eadad4c048dff9aac92ecff51d5a2f9e853527e000646e4d282df506f96c39b3cfa5ee49bd15efad63e7c20e84e55a3f108626f7a40546fd00dfc84b927b9893717e12c4af7c018c2fed42c9b75d3c2aa8fe381a45cfc7c7ac9291d748ea47019b7e0413dee6bda9e6ee79d045b598484c4e3fcae27242e4d4dc05376b73b0f762566b5f6f82aaf787f2e5a5bedf498f0bc1cb358fe1e2e2792e66c1c45e1b0d246ebfe1dd2d828409e1ecd1655d3d67ec2743e83e3d03f25c98a94d6f4c2c35c3c27a11b23847d508782b66738cb7a2bae10be75bb5baee38f9735a6f35a900e10f047b87b288b10e0ed365a736cf14db7fecfcb2ae3328066f38c9c3ce3666cea772c7dfc3f8f809482b8db6f9f6f890530d5be63a8cb0a666fe9170cb8cf5135ecd99b96fcc9066a6ce63bb68eb15cfcd105b30251b09dca1f6f515d322c0055b1087c21d1628368a6eecb5eefccd57a851629e47a4817b18f5af59f8050e91751b6c9d114b668fd69ad29ac8e465c40810bb5204d7cb3ac5641b9acf7741ba6d321c6934b71d37a454698b3b6652e3f5d3d2b5c2be02c38d44150ac2714d9631dc1f32a6dffe0757c51c1ec009b1aba29f0375eceffae3f5fd585e18d7e332b45e0f627864160000"; @@ -304,13 +302,13 @@ pub(super) mod txin_utils { /// native bitcoin asset. /// Returns the txin and the spending utxo pub(super) fn pegin() -> (elements::TxIn, ElementsUtxo) { - let tx_bytes = Vec::::from_hex(PEGIN_TX_BYTES).unwrap(); + let tx_bytes = hex::decode_to_vec(PEGIN_TX_BYTES).unwrap(); let mut tx: elements::Transaction = deserialize(&tx_bytes).unwrap(); let txin = tx.input.swap_remove(0); assert!(txin.is_pegin); let pegin_data = txin.pegin_data().expect("Is pegin"); let bitcoin_utxo = ElementsUtxo { - asset: confidential::Asset::Explicit(pegin_data.asset), + asset: confidential::Asset::Explicit(pegin_data.asset_id), value: confidential::Value::Explicit(pegin_data.value), script_pubkey: Script::new(), // Actually generated from a tweak of claim script and signing scripts }; @@ -319,7 +317,7 @@ pub(super) mod txin_utils { /// Input with issuance as well as re-issuance pub(super) fn issuance() -> (elements::TxIn, ElementsUtxo) { - let tx_bytes = Vec::::from_hex(ISSUANCE_TX_BYTES).unwrap(); + let tx_bytes = hex::decode_to_vec(ISSUANCE_TX_BYTES).unwrap(); let mut tx: elements::Transaction = deserialize(&tx_bytes).unwrap(); let txin = tx.input.swap_remove(0); assert!(txin.has_issuance()); diff --git a/jets-bench/src/input.rs b/jets-bench/src/input.rs index 2226706c..184ab09f 100644 --- a/jets-bench/src/input.rs +++ b/jets-bench/src/input.rs @@ -306,8 +306,6 @@ impl FlatValue { ); unsafe { - use simplicity::elements::hashes::Hash as _; - let mut dst_inner = [UWORD::from(0u8); MAX_VALUE_BYTES / mem::size_of::()]; let mut src_inner = [UWORD::from(0u8); MAX_VALUE_BYTES / mem::size_of::()]; @@ -347,7 +345,7 @@ impl FlatValue { simplicity::Cmr::unit(), simplicity::elements::taproot::ControlBlock::from_slice(&ctrl_blk).unwrap(), None, - simplicity::elements::BlockHash::all_zeros(), + simplicity::elements::BlockHash::GENESIS_PREVIOUS_BLOCK_HASH, ); // We can assert this because in our sampling code jets should never diff --git a/src/jet/elements/c_env.rs b/src/jet/elements/c_env.rs index fc1b534c..25483f60 100644 --- a/src/jet/elements/c_env.rs +++ b/src/jet/elements/c_env.rs @@ -4,15 +4,9 @@ //! use bitcoin::taproot::TAPROOT_ANNEX_PREFIX; -use elements::hashes::Hash as _; use std::os::raw::c_uchar; -use elements::{ - confidential, - encode::serialize, - secp256k1_zkp::{RangeProof, SurjectionProof}, - taproot::ControlBlock, -}; +use elements::{confidential, encode::serialize, taproot::ControlBlock}; use simplicity_sys::c_jets::c_env::elements as c_elements; use crate::merkle::cmr::Cmr; @@ -81,8 +75,8 @@ fn new_raw_input<'raw>( pegin: inp_data.genesis_hash.as_ref(), issuance: if inp.has_issuance() { c_elements::CRawInputIssuance { - blinding_nonce: Some(inp.asset_issuance.asset_blinding_nonce.as_ref()), - asset_entropy: Some(&inp.asset_issuance.asset_entropy), + blinding_nonce: Some(inp.asset_issuance.asset_blinding_nonce.as_byte_array()), + asset_entropy: Some(inp.asset_issuance.asset_entropy.as_byte_array()), amount: value_ptr(inp.asset_issuance.amount, &inp_data.issuance_amount), inflation_keys: value_ptr( inp.asset_issuance.inflation_keys, @@ -108,6 +102,8 @@ fn new_raw_input<'raw>( } fn new_tx_data(tx: &elements::Transaction, in_utxos: &[ElementsUtxo]) -> RawTransactionData { + use bitcoin::hashes::Hash as _; + let mut tx_data = RawTransactionData { inputs: Vec::with_capacity(tx.input.len()), outputs: Vec::with_capacity(tx.output.len()), @@ -120,10 +116,8 @@ fn new_tx_data(tx: &elements::Transaction, in_utxos: &[ElementsUtxo]) -> RawTran .map(|x| x.genesis_hash.to_raw_hash().to_byte_array()), issuance_amount: serialize(&inp.asset_issuance.amount), issuance_inflation_keys: serialize(&inp.asset_issuance.inflation_keys), - amount_range_proof: serialize_rangeproof(&inp.witness.amount_rangeproof), - inflation_keys_range_proof: serialize_rangeproof( - &inp.witness.inflation_keys_rangeproof, - ), + amount_range_proof: inp.witness.amount_rangeproof.to_vec(), + inflation_keys_range_proof: inp.witness.inflation_keys_rangeproof.to_vec(), asset: asset_array(&in_utxo.asset), value: serialize(&in_utxo.value), }; @@ -134,8 +128,8 @@ fn new_tx_data(tx: &elements::Transaction, in_utxos: &[ElementsUtxo]) -> RawTran asset: asset_array(&out.asset), value: serialize(&out.value), nonce: nonce_array(&out.nonce), - surjection_proof: serialize_surjection_proof(&out.witness.surjection_proof), - range_proof: serialize_rangeproof(&out.witness.rangeproof), + surjection_proof: out.witness.surjection_proof.to_vec(), + range_proof: out.witness.rangeproof.to_vec(), }; tx_data.outputs.push(out_data); } @@ -177,7 +171,7 @@ pub(super) fn new_tx( } let c_raw_tx = c_elements::CRawTransaction { - txid: txid.as_raw_hash().as_byte_array(), + txid: txid.as_byte_array(), inputs: raw_inputs.as_ptr(), outputs: raw_outputs.as_ptr(), n_inputs: raw_inputs.len().try_into().expect("sane length"), @@ -262,20 +256,6 @@ fn value_ptr(value: confidential::Value, data: &[u8]) -> *const c_uchar { } } -fn serialize_rangeproof(rangeproof: &Option>) -> Vec { - rangeproof - .as_ref() - .map(|x| x.serialize()) - .unwrap_or_default() -} - -fn serialize_surjection_proof(surjection_proof: &Option>) -> Vec { - surjection_proof - .as_ref() - .map(|x| x.serialize()) - .unwrap_or_default() -} - /// If the last item in the witness stack is an annex, return the data following the 0x50 byte. fn get_annex(in_witness: &elements::TxInWitness) -> Option<&[u8]> { let last_item = in_witness.script_witness.last()?; diff --git a/src/jet/elements/environment.rs b/src/jet/elements/environment.rs index fb5db4ca..202582c4 100644 --- a/src/jet/elements/environment.rs +++ b/src/jet/elements/environment.rs @@ -118,7 +118,6 @@ 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; let ctrl_blk: [u8; 33] = [ @@ -151,7 +150,7 @@ impl ElementsEnv> { Cmr::from_byte_array([0; 32]), ControlBlock::from_slice(&ctrl_blk).unwrap(), None, - elements::BlockHash::all_zeros(), + elements::BlockHash::GENESIS_PREVIOUS_BLOCK_HASH, ) } } diff --git a/src/jet/elements/tests.rs b/src/jet/elements/tests.rs index 936f3084..f6373f51 100644 --- a/src/jet/elements/tests.rs +++ b/src/jet/elements/tests.rs @@ -7,13 +7,11 @@ 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, + confidential, AssetBlindingNonce, AssetEntropy, AssetId, AssetIssuance, BlockHash, OutPoint, + PeginWitness, RangeProof, Sequence, SurjectionProof, Transaction, TxIn, TxInWitness, TxOut, + TxOutWitness, Witness, }; #[test] @@ -33,8 +31,7 @@ fn test_ffi_env() { 0x33, 0x2f, 0xb7, 0x1d, 0xda, 0x90, 0xff, 0x4b, 0xef, 0x53, 0x70, 0xf2, 0x52, 0x26, 0xd3, 0xbc, 0x09, 0xfc, ]; - let asset = - confidential::Asset::Explicit(AssetId::from_inner(Midstate::from_byte_array(asset))); + let asset = confidential::Asset::Explicit(AssetId::from_byte_array(asset)); let tx = Transaction { version: 2, lock_time: elements::LockTime::from_consensus(100), @@ -47,17 +44,17 @@ fn test_ffi_env() { is_pegin: false, // perhaps make this an option in elements upstream? asset_issuance: AssetIssuance { - asset_blinding_nonce: Tweak::from_inner([0; 32]).expect("tweak from inner"), - asset_entropy: [0; 32], + asset_blinding_nonce: AssetBlindingNonce::NEW_ISSUANCE, + asset_entropy: AssetEntropy::NEW_ISSUANCE, amount: confidential::Value::Null, inflation_keys: confidential::Value::Null, }, script_sig: elements::Script::new(), witness: TxInWitness { - amount_rangeproof: None, - inflation_keys_rangeproof: None, - script_witness: vec![ctrl_blk.to_vec()], - pegin_witness: vec![], + amount_rangeproof: RangeProof::EMPTY, + inflation_keys_rangeproof: RangeProof::EMPTY, + script_witness: Witness::from(vec![ctrl_blk.to_vec()]), + pegin_witness: PeginWitness::EMPTY, }, }], output: vec![ @@ -67,8 +64,8 @@ fn test_ffi_env() { nonce: confidential::Nonce::Null, script_pubkey: hex_script("1976a91448633e2c0ee9495dd3f9c43732c47f4702a362c888ac"), witness: TxOutWitness { - surjection_proof: None, - rangeproof: None, + surjection_proof: SurjectionProof::EMPTY, + rangeproof: RangeProof::EMPTY, }, }, TxOut { @@ -77,8 +74,8 @@ fn test_ffi_env() { nonce: confidential::Nonce::Null, script_pubkey: elements::Script::new(), witness: TxOutWitness { - surjection_proof: None, - rangeproof: None, + surjection_proof: SurjectionProof::EMPTY, + rangeproof: RangeProof::EMPTY, }, }, ], @@ -97,7 +94,7 @@ fn test_ffi_env() { script_cmr, ctrl_block, None, - BlockHash::all_zeros(), + BlockHash::GENESIS_PREVIOUS_BLOCK_HASH, ); types::Context::with_context(|ctx| { diff --git a/src/policy/satisfy.rs b/src/policy/satisfy.rs index dc16c805..2c9aa286 100644 --- a/src/policy/satisfy.rs +++ b/src/policy/satisfy.rs @@ -296,8 +296,8 @@ mod tests { use crate::node::{CoreConstructible, JetConstructible, SimpleFinalizer, WitnessConstructible}; use crate::policy::serialize; use crate::{BitMachine, FailEntropy, SimplicityKey}; + use elements::bitcoin::hashes::{sha256, Hash}; use elements::bitcoin::key::{Keypair, XOnlyPublicKey}; - use elements::hashes::{sha256, Hash}; use elements::secp256k1_zkp; use std::collections::HashMap; use std::sync::Arc; From db4ade08ef91606c179029e3ad8bb2c84974a0e4 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 30 Jul 2026 13:27:25 +0000 Subject: [PATCH 3/7] update bitcoin_hashes to 1.2.0. This will get us constant-time midstate manipulation functions. --- Cargo-recent.lock | 14 +++++++------- Cargo.toml | 2 +- simplicity-sys/Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo-recent.lock b/Cargo-recent.lock index a146b8d8..e00cf714 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -77,9 +77,9 @@ dependencies = [ [[package]] name = "bitcoin-consensus-encoding" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "207311705279250ba465076a1bac4b1ac982855fff73fc5f67e22158ac58cdc9" +checksum = "6712f9c6fd6785b3b270884e57c441c403dc5d7e19ca45368c97c7a1de3000ec" dependencies = [ "bitcoin-internals", "hex-conservative 1.2.0", @@ -130,9 +130,9 @@ dependencies = [ [[package]] name = "bitcoin_hashes" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a67800fcf7f3ca52f7796d4466948a424326b225950c79d1e76d0458760bb25d" +checksum = "5304e53726dbe5f93141535e102ed97b5bf4714fbecefdda8f9fb98d7fdaff0e" dependencies = [ "bitcoin-consensus-encoding", "bitcoin-internals", @@ -180,7 +180,7 @@ dependencies = [ "bitcoin", "bitcoin-consensus-encoding", "bitcoin-internals", - "bitcoin_hashes 1.1.0", + "bitcoin_hashes 1.2.0", "hex-conservative 1.2.0", "secp256k1-zkp", "serde", @@ -615,7 +615,7 @@ name = "simplicity-lang" version = "0.8.0" dependencies = [ "bitcoin", - "bitcoin_hashes 1.1.0", + "bitcoin_hashes 1.2.0", "byteorder", "elements", "getrandom 0.2.16", @@ -642,7 +642,7 @@ dependencies = [ name = "simplicity-sys" version = "0.7.0" dependencies = [ - "bitcoin_hashes 1.1.0", + "bitcoin_hashes 1.2.0", "cc", ] diff --git a/Cargo.toml b/Cargo.toml index c6cb91b3..54a1061f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ bitcoin-miniscript = { package = "miniscript", version = "12.0.0" } byteorder = "1.3" elements = { version = "0.27.0", optional = true, default-features = false } ghost-cell = { version = "0.2.6", default-features = false } -hashes = { package = "bitcoin_hashes", version = "1.1.0" } +hashes = { package = "bitcoin_hashes", version = "1.2.0" } hex = { package = "hex-conservative", version = "1.2.0" } logos = { version = "0.15", optional = true } simplicity-sys = { version = "0.7.0", path = "./simplicity-sys" } diff --git a/simplicity-sys/Cargo.toml b/simplicity-sys/Cargo.toml index 6b49d5bd..96c3bbf4 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 = "1.1.0" } +hashes = { package = "bitcoin_hashes", version = "1.2.0" } [features] test-utils = [] From b4fa5e9bde49449d0ccb707f731a621b7a10a7aa Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 30 Jul 2026 13:33:06 +0000 Subject: [PATCH 4/7] tmr: add const versions of all TMR constructors For the other Merkle roots it's unclear how far we can go with this -- in particular it seems unlikely that we'll be able to compute a jet CMR in a constfn because jets are generic, without serious refactorings at least. (And const words, and some others, will require minor refactors.) But TMRs have just 3 constructors, unit sum and product, and we can make them all const now. --- src/merkle/mod.rs | 12 ++++++++++++ src/merkle/tmr.rs | 44 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/merkle/mod.rs b/src/merkle/mod.rs index 5481fded..5a87bdcf 100644 --- a/src/merkle/mod.rs +++ b/src/merkle/mod.rs @@ -101,6 +101,18 @@ fn compact_value(value: &Value) -> [u8; 32] { .0 } +/// Helper function to concatenate two 32-byte arrays into one 64-byte one. +#[inline] +fn concat>(mr1: MR, mr2: MR) -> [u8; 64] { + core::array::from_fn(|i| { + if i < 32 { + mr1.as_ref()[i] + } else { + mr2.as_ref()[i - 32] + } + }) +} + const fn bip340_iv(tag: &[u8]) -> sha256::Midstate { sha256::Midstate::hash_tag(tag) } diff --git a/src/merkle/tmr.rs b/src/merkle/tmr.rs index 28e40a76..481f2e90 100644 --- a/src/merkle/tmr.rs +++ b/src/merkle/tmr.rs @@ -363,13 +363,41 @@ impl Tmr { /// The TMR for the sum of two types, whose TMRs are given pub fn sum(tmr1: Tmr, tmr2: Tmr) -> Tmr { - Self::SUM_IV.zz_update_2x32(tmr1, tmr2).into_merkle_root() + Self::SUM_IV + .update_64(&super::concat(tmr1, tmr2)) + .into_merkle_root() + } + + /// The TMR for the sum of two types, whose TMRs are given + /// + /// This function is slow but can be used in a const context. If you do not + /// need to run in a const context, use [`Self::sum`] instead. + pub const fn sum_unoptimized(tmr1: Tmr, tmr2: Tmr) -> Tmr { + Self::from_byte_array( + Self::SUM_IV + .update_2x32_unoptimized(tmr1.as_byte_array(), tmr2.as_byte_array()) + .to_parts() + .0, + ) } /// The TMR for the product of two types, whose TMRs are given pub fn product(tmr1: Tmr, tmr2: Tmr) -> Tmr { Self::PROD_IV.zz_update_2x32(tmr1, tmr2).into_merkle_root() } + + /// The TMR for the product of two types, whose TMRs are given + /// + /// This function is slow but can be used in a const context. If you do not + /// need to run in a const context, use [`Self::product`] instead. + pub const fn product_unoptimized(tmr1: Tmr, tmr2: Tmr) -> Tmr { + Self::from_byte_array( + Self::PROD_IV + .update_2x32_unoptimized(tmr1.as_byte_array(), tmr2.as_byte_array()) + .to_parts() + .0, + ) + } } #[cfg(test)] @@ -439,6 +467,20 @@ mod tests { }); } + #[test] + fn direct_constructors() { + let unit = Tmr::unit(); + let bit1 = Tmr::sum(unit, unit); + let bit2 = Tmr::sum_unoptimized(unit, unit); + assert_eq!(bit1, bit2); + assert_eq!(bit1, Tmr::TWO_TWO_N[0]); + + let dbit1 = Tmr::product(bit1, bit1); + let dbit2 = Tmr::product_unoptimized(bit1, bit1); + assert_eq!(dbit1, dbit2); + assert_eq!(dbit1, Tmr::TWO_TWO_N[1]); + } + #[test] fn const_buffer8() { use std::sync::Arc; From 03489a36f205106f21ce76449237a7b4b6152a95 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 28 Jul 2026 17:19:26 +0000 Subject: [PATCH 5/7] fold JetConstructible into CoreConstructible Since we stopped using compile-time generic jets, there is no longer a reason to have a separate JetConstructible trait. The `jet` method has all the same generics as the other CoreConstructible methods. So it should be folded in. This will be a mildly annoying change for downstream implementors of the *Constructible traits. They will have to delete their JetConstructible impl and move the `fn jet` block into their CoreConstructible impl. The next commit will have an even more annoying change to the CoreConstructible trait, and I figure if we're making implementors change stuff we might as well do it all at once. --- fuzz/fuzz_lib/program.rs | 3 +-- src/bit_encoding/decode.rs | 3 +-- src/bit_machine/mod.rs | 2 +- src/jet/elements/tests.rs | 2 +- src/jet/mod.rs | 2 +- src/merkle/amr.rs | 2 +- src/merkle/cmr.rs | 20 ++++++++----------- src/node/construct.rs | 16 +++++++--------- src/node/hiding.rs | 17 +++++------------ src/node/mod.rs | 39 ++++++++++++++++++-------------------- src/policy/ast.rs | 3 +-- src/policy/satisfy.rs | 2 +- src/policy/serialize.rs | 20 +++++++++---------- src/types/arrow.rs | 27 +++++++++++--------------- src/types/mod.rs | 3 +-- 15 files changed, 68 insertions(+), 93 deletions(-) diff --git a/fuzz/fuzz_lib/program.rs b/fuzz/fuzz_lib/program.rs index ae39baf9..6745baf4 100644 --- a/fuzz/fuzz_lib/program.rs +++ b/fuzz/fuzz_lib/program.rs @@ -4,8 +4,7 @@ use std::sync::Arc; use super::Extractor; use simplicity::node::{ - CoreConstructible as _, DisconnectConstructible as _, JetConstructible as _, - WitnessConstructible as _, + CoreConstructible as _, DisconnectConstructible as _, WitnessConstructible as _, }; use simplicity::types; use simplicity::{jet::Core, Cmr, ConstructNode, FailEntropy}; diff --git a/src/bit_encoding/decode.rs b/src/bit_encoding/decode.rs index 92f63afe..2d57c085 100644 --- a/src/bit_encoding/decode.rs +++ b/src/bit_encoding/decode.rs @@ -9,8 +9,7 @@ use crate::dag::{Dag, DagLike, InternalSharing}; use crate::jet::Jet; use crate::merkle::cmr::Cmr; use crate::node::{ - ConstructNode, CoreConstructible, DisconnectConstructible, JetConstructible, - WitnessConstructible, + ConstructNode, CoreConstructible, DisconnectConstructible, WitnessConstructible, }; use crate::types; use crate::value::Word; diff --git a/src/bit_machine/mod.rs b/src/bit_machine/mod.rs index 06d62e4d..d2f8751c 100644 --- a/src/bit_machine/mod.rs +++ b/src/bit_machine/mod.rs @@ -731,7 +731,7 @@ mod tests { #[test] fn crash_regression2() { - use crate::node::{CoreConstructible as _, JetConstructible as _}; + use crate::node::CoreConstructible as _; type Node<'brand> = Arc>; diff --git a/src/jet/elements/tests.rs b/src/jet/elements/tests.rs index f6373f51..5430733b 100644 --- a/src/jet/elements/tests.rs +++ b/src/jet/elements/tests.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use crate::jet::elements::{ElementsEnv, ElementsUtxo}; use crate::jet::Elements; -use crate::node::{ConstructNode, JetConstructible}; +use crate::node::{ConstructNode, CoreConstructible as _}; use crate::types; use crate::{BitMachine, Cmr, Value}; use elements::taproot::ControlBlock; diff --git a/src/jet/mod.rs b/src/jet/mod.rs index a412aaf8..93732d69 100644 --- a/src/jet/mod.rs +++ b/src/jet/mod.rs @@ -181,7 +181,7 @@ impl Hash for Box { #[cfg(test)] mod tests { use crate::jet::{Core, CoreEnv}; - use crate::node::{ConstructNode, CoreConstructible, JetConstructible}; + use crate::node::{ConstructNode, CoreConstructible as _}; use crate::types; use crate::value::Word; use crate::{BitMachine, Value}; diff --git a/src/merkle/amr.rs b/src/merkle/amr.rs index 2d07538d..21dbeb15 100644 --- a/src/merkle/amr.rs +++ b/src/merkle/amr.rs @@ -301,7 +301,7 @@ mod tests { use super::*; use crate::jet::Core; - use crate::node::{ConstructNode, JetConstructible}; + use crate::node::{ConstructNode, CoreConstructible as _}; use crate::types; use std::sync::Arc; diff --git a/src/merkle/cmr.rs b/src/merkle/cmr.rs index 08f08f12..653940ed 100644 --- a/src/merkle/cmr.rs +++ b/src/merkle/cmr.rs @@ -1,9 +1,7 @@ // SPDX-License-Identifier: CC0-1.0 use crate::jet::Jet; -use crate::node::{ - CoreConstructible, DisconnectConstructible, JetConstructible, WitnessConstructible, -}; +use crate::node::{CoreConstructible, DisconnectConstructible, WitnessConstructible}; use crate::types::{self, Error}; use crate::value::Word; use crate::{FailEntropy, Tmr}; @@ -356,6 +354,13 @@ impl<'brand> CoreConstructible<'brand> for ConstructibleCmr<'brand> { } } + fn jet(inference_context: &types::Context<'brand>, jet: &dyn Jet) -> Self { + ConstructibleCmr { + cmr: jet.cmr(), + inference_context: inference_context.shallow_clone(), + } + } + fn inference_context(&self) -> &types::Context<'brand> { &self.inference_context } @@ -383,15 +388,6 @@ impl<'brand, W> WitnessConstructible<'brand, W> for ConstructibleCmr<'brand> { } } -impl<'brand> JetConstructible<'brand> for ConstructibleCmr<'brand> { - fn jet(inference_context: &types::Context<'brand>, jet: &dyn Jet) -> Self { - ConstructibleCmr { - cmr: jet.cmr(), - inference_context: inference_context.shallow_clone(), - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/node/construct.rs b/src/node/construct.rs index 72568cc1..ad3eff45 100644 --- a/src/node/construct.rs +++ b/src/node/construct.rs @@ -13,7 +13,7 @@ use super::{ Commit, CommitData, CommitNode, Converter, Inner, Marker, NoDisconnect, NoWitness, Node, Redeem, RedeemData, }; -use super::{CoreConstructible, DisconnectConstructible, JetConstructible, WitnessConstructible}; +use super::{CoreConstructible, DisconnectConstructible, WitnessConstructible}; /// ID used to share [`ConstructNode`]s. /// @@ -360,6 +360,12 @@ impl<'brand> CoreConstructible<'brand> for ConstructData<'brand> { } } + fn jet(inference_context: &types::Context<'brand>, jet: &dyn Jet) -> Self { + ConstructData { + arrow: Arrow::jet(inference_context, jet), + } + } + fn inference_context(&self) -> &types::Context<'brand> { self.arrow.inference_context() } @@ -387,14 +393,6 @@ impl<'brand> WitnessConstructible<'brand, Option> for ConstructData<'bran } } -impl<'brand> JetConstructible<'brand> for ConstructData<'brand> { - fn jet(inference_context: &types::Context<'brand>, jet: &dyn Jet) -> Self { - ConstructData { - arrow: Arrow::jet(inference_context, jet), - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/node/hiding.rs b/src/node/hiding.rs index a891824e..2e7bc57c 100644 --- a/src/node/hiding.rs +++ b/src/node/hiding.rs @@ -1,7 +1,5 @@ use crate::jet::Jet; -use crate::node::{ - CoreConstructible, DisconnectConstructible, JetConstructible, WitnessConstructible, -}; +use crate::node::{CoreConstructible, DisconnectConstructible, WitnessConstructible}; use crate::types::{Context, Error}; use crate::{Cmr, FailEntropy, HasCmr, Word}; @@ -212,18 +210,13 @@ impl<'brand, N: CoreConstructible<'brand> + HasCmr> CoreConstructible<'brand> N::const_word(inference_context, word).into() } - fn inference_context(&self) -> &Context<'brand> { - &self.ctx - } -} - -impl<'brand, N> JetConstructible<'brand> for Hiding<'brand, N> -where - N: JetConstructible<'brand> + CoreConstructible<'brand>, -{ fn jet(inference_context: &Context<'brand>, jet: &dyn Jet) -> Self { N::jet(inference_context, jet).into() } + + fn inference_context(&self) -> &Context<'brand> { + &self.ctx + } } impl<'brand, X, N> DisconnectConstructible<'brand, Option> for Hiding<'brand, N> diff --git a/src/node/mod.rs b/src/node/mod.rs index 3f3c91bc..7f6ac600 100644 --- a/src/node/mod.rs +++ b/src/node/mod.rs @@ -90,6 +90,14 @@ pub use hiding::Hiding; pub use inner::Inner; pub use redeem::{Redeem, RedeemData, RedeemNode}; +/// Lots of existing code imports [`JetConstructible`] just to get the `jet` method. +/// Almost all such code also imports [`CoreConstructible`], which now gives them +/// this method, so this code will continue to work as long as there is something +/// called [`JetConstructible`] to import. +#[doc(hidden)] +#[deprecated(note = "subsumed by CoreConstructible")] +pub enum JetConstructible {} + // This trait should only be implemented on empty types, so we can demand // every trait bound under the sun. Doing so will make #[derive]s easier // for downstream users. @@ -129,8 +137,7 @@ impl From for Option { } pub trait Constructible<'brand, X, W>: - JetConstructible<'brand> - + DisconnectConstructible<'brand, X> + DisconnectConstructible<'brand, X> + WitnessConstructible<'brand, W> + CoreConstructible<'brand> + Sized @@ -162,7 +169,6 @@ pub trait Constructible<'brand, X, W>: impl<'brand, X, W, T> Constructible<'brand, X, W> for T where T: DisconnectConstructible<'brand, X> - + JetConstructible<'brand> + WitnessConstructible<'brand, W> + CoreConstructible<'brand> + Sized @@ -183,6 +189,7 @@ pub trait CoreConstructible<'brand>: Sized { fn pair(left: &Self, right: &Self) -> Result; fn fail(inference_context: &types::Context<'brand>, entropy: FailEntropy) -> Self; fn const_word(inference_context: &types::Context<'brand>, word: Word) -> Self; + fn jet(inference_context: &types::Context<'brand>, jet: &dyn Jet) -> Self; /// Accessor for the type inference context used to create the object. fn inference_context(&self) -> &types::Context<'brand>; @@ -344,10 +351,6 @@ pub trait DisconnectConstructible<'brand, X>: Sized { fn disconnect(left: &Self, right: &X) -> Result; } -pub trait JetConstructible<'brand>: Sized { - fn jet(inference_context: &types::Context<'brand>, jet: &dyn Jet) -> Self; -} - pub trait WitnessConstructible<'brand, W>: Sized { fn witness(inference_context: &types::Context<'brand>, witness: W) -> Self; } @@ -539,6 +542,14 @@ where }) } + fn jet(inference_context: &types::Context<'brand>, jet: &dyn Jet) -> Self { + Arc::new(Node { + cmr: Cmr::jet(jet), + data: N::CachedData::jet(inference_context, jet), + inner: Inner::Jet(jet.dyn_clone()), + }) + } + fn inference_context(&self) -> &types::Context<'brand> { self.data.inference_context() } @@ -572,20 +583,6 @@ where } } -impl<'brand, N> JetConstructible<'brand> for Arc> -where - N: Marker, - N::CachedData: JetConstructible<'brand>, -{ - fn jet(inference_context: &types::Context<'brand>, jet: &dyn Jet) -> Self { - Arc::new(Node { - cmr: Cmr::jet(jet), - data: N::CachedData::jet(inference_context, jet), - inner: Inner::Jet(jet.dyn_clone()), - }) - } -} - impl Node { /// Accessor for the node's "inner value", i.e. its combinator pub fn inner(&self) -> &Inner>, N::Disconnect, N::Witness> { diff --git a/src/policy/ast.rs b/src/policy/ast.rs index 846ebc92..572d1ac2 100644 --- a/src/policy/ast.rs +++ b/src/policy/ast.rs @@ -12,7 +12,7 @@ use std::convert::TryFrom; use std::sync::Arc; use std::{fmt, iter, mem}; -use crate::node::{ConstructNode, CoreConstructible, JetConstructible, WitnessConstructible}; +use crate::node::{ConstructNode, CoreConstructible, WitnessConstructible}; use crate::policy::serialize::{self, AssemblyConstructible}; use crate::{types, Value}; use crate::{Cmr, CommitNode, FailEntropy}; @@ -62,7 +62,6 @@ impl Policy { ) -> Option where N: CoreConstructible<'brand> - + JetConstructible<'brand> + WitnessConstructible<'brand, Option> + AssemblyConstructible<'brand>, { diff --git a/src/policy/satisfy.rs b/src/policy/satisfy.rs index 2c9aa286..929fe3a1 100644 --- a/src/policy/satisfy.rs +++ b/src/policy/satisfy.rs @@ -293,7 +293,7 @@ mod tests { use crate::dag::{DagLike, NoSharing}; use crate::jet::elements::ElementsEnv; use crate::jet::Elements; - use crate::node::{CoreConstructible, JetConstructible, SimpleFinalizer, WitnessConstructible}; + use crate::node::{CoreConstructible as _, SimpleFinalizer, WitnessConstructible}; use crate::policy::serialize; use crate::{BitMachine, FailEntropy, SimplicityKey}; use elements::bitcoin::hashes::{sha256, Hash}; diff --git a/src/policy/serialize.rs b/src/policy/serialize.rs index ea623760..ace99b9c 100644 --- a/src/policy/serialize.rs +++ b/src/policy/serialize.rs @@ -4,7 +4,7 @@ use crate::jet::Elements; use crate::merkle::cmr::ConstructibleCmr; -use crate::node::{CoreConstructible, JetConstructible, WitnessConstructible}; +use crate::node::{CoreConstructible, WitnessConstructible}; use crate::types; use crate::FailEntropy; use crate::{Cmr, ConstructNode, ToXOnlyPubkey}; @@ -56,7 +56,7 @@ where pub fn key<'brand, Pk, N, W>(inference_context: &types::Context<'brand>, key: &Pk, witness: W) -> N where Pk: ToXOnlyPubkey, - N: CoreConstructible<'brand> + JetConstructible<'brand> + WitnessConstructible<'brand, W>, + N: CoreConstructible<'brand> + WitnessConstructible<'brand, W>, { let key_value = Word::u256(key.to_x_only_pubkey().serialize()); let const_key = N::const_word(inference_context, key_value); @@ -71,7 +71,7 @@ where pub fn after<'brand, N>(inference_context: &types::Context<'brand>, n: u32) -> N where - N: CoreConstructible<'brand> + JetConstructible<'brand>, + N: CoreConstructible<'brand>, { let n_value = Word::u32(n); let const_n = N::const_word(inference_context, n_value); @@ -82,7 +82,7 @@ where pub fn older<'brand, N>(inference_context: &types::Context<'brand>, n: u16) -> N where - N: CoreConstructible<'brand> + JetConstructible<'brand>, + N: CoreConstructible<'brand>, { let n_value = Word::u16(n); let const_n = N::const_word(inference_context, n_value); @@ -96,7 +96,7 @@ where pub fn compute_sha256<'brand, N>(witness256: &N) -> N where - N: CoreConstructible<'brand> + JetConstructible<'brand>, + N: CoreConstructible<'brand>, { let ctx = N::jet(witness256.inference_context(), &Elements::Sha256Ctx8Init); let pair_ctx_witness = N::pair(&ctx, witness256).expect("consistent types"); @@ -111,7 +111,7 @@ where pub fn verify_bexp<'brand, N>(input: &N, bexp: &N) -> N where - N: CoreConstructible<'brand> + JetConstructible<'brand>, + N: CoreConstructible<'brand>, { assert_eq!( input.inference_context(), @@ -130,7 +130,7 @@ pub fn sha256<'brand, Pk, N, W>( ) -> N where Pk: ToXOnlyPubkey, - N: CoreConstructible<'brand> + JetConstructible<'brand> + WitnessConstructible<'brand, W>, + N: CoreConstructible<'brand> + WitnessConstructible<'brand, W>, { use bitcoin_miniscript::bitcoin::hashes::Hash as _; @@ -210,7 +210,7 @@ where /// add(sum, summand): 1 → 2^32 pub fn thresh_add<'brand, N>(sum: &N, summand: &N) -> N where - N: CoreConstructible<'brand> + JetConstructible<'brand>, + N: CoreConstructible<'brand>, { assert_eq!( sum.inference_context(), @@ -237,7 +237,7 @@ where /// verify(sum): 1 → 1 pub fn thresh_verify<'brand, N>(sum: &N, k: u32) -> N where - N: CoreConstructible<'brand> + JetConstructible<'brand>, + N: CoreConstructible<'brand>, { // 1 → 2^32 let const_k = N::const_word(sum.inference_context(), Word::u32(k)); @@ -252,7 +252,7 @@ where pub fn threshold<'brand, N, W>(k: u32, subs: &[N], witness_bits: &[W]) -> N where - N: CoreConstructible<'brand> + JetConstructible<'brand> + WitnessConstructible<'brand, W>, + N: CoreConstructible<'brand> + WitnessConstructible<'brand, W>, W: Clone, { let n = u32::try_from(subs.len()).expect("can have at most 2^32 children in a threshold"); diff --git a/src/types/arrow.rs b/src/types/arrow.rs index 09005e22..a9df5c42 100644 --- a/src/types/arrow.rs +++ b/src/types/arrow.rs @@ -15,10 +15,7 @@ use std::fmt; use std::sync::Arc; use crate::jet::Jet; -use crate::node::{ - CoreConstructible, DisconnectConstructible, JetConstructible, NoDisconnect, - WitnessConstructible, -}; +use crate::node::{CoreConstructible, DisconnectConstructible, NoDisconnect, WitnessConstructible}; use crate::types::{Context, Error, Final, Type}; use crate::value::Word; @@ -319,6 +316,16 @@ impl<'brand> CoreConstructible<'brand> for Arrow<'brand> { } } + fn jet(inference_context: &Context<'brand>, jet: &dyn Jet) -> Self { + inference_context.check_jet(jet); + + Arrow { + source: jet.source_ty().to_type(inference_context), + target: jet.target_ty().to_type(inference_context), + inference_context: inference_context.shallow_clone(), + } + } + fn inference_context(&self) -> &Context<'brand> { &self.inference_context } @@ -354,18 +361,6 @@ impl<'brand> DisconnectConstructible<'brand, Option<&Arrow<'brand>>> for Arrow<' } } -impl<'brand> JetConstructible<'brand> for Arrow<'brand> { - fn jet(inference_context: &Context<'brand>, jet: &dyn Jet) -> Self { - inference_context.check_jet(jet); - - Arrow { - source: jet.source_ty().to_type(inference_context), - target: jet.target_ty().to_type(inference_context), - inference_context: inference_context.shallow_clone(), - } - } -} - impl<'brand, W> WitnessConstructible<'brand, W> for Arrow<'brand> { fn witness(inference_context: &Context<'brand>, _: W) -> Self { Arrow { diff --git a/src/types/mod.rs b/src/types/mod.rs index 5b1bd299..bef9550e 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -416,8 +416,7 @@ mod tests { use super::*; use crate::jet::Core; - use crate::node::JetConstructible; - use crate::node::{ConstructNode, CoreConstructible}; + use crate::node::{ConstructNode, CoreConstructible as _}; #[test] fn inference_failure() { From 8973277c0c90af822850a143072eef84fc9d95ff Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 10 Aug 2026 22:50:31 +0000 Subject: [PATCH 6/7] policy: remove Assembly fragment The 'assembly' fragment of the policy language is a very half-baked feature. It provides no way to serialize (the `serialize_no_witness` method accepts two node types: ConstructibleCmr, where it will compute a CMR and nothing else, and Arc>, where it will always fail). We have two unit tests which demonstrate "satisfying" an asm fragment, but none which attempt to serialize the program, because this is impossible. In other words this feature literally cannot be used other than for in-memory unit tests. Also one of the tests was marked #[ignore] anyway so maybe you couldn't even do that.. The next commit will make the `serialize_no_witness` method infallible. It's only fallible because of the possibility that you might use the asm fragment. This will remove a gazillion .expect()s from unrelated unit tests. Anyway the motivation for this is not to clean up Policy (if I wanted to do this I would also eliminate some recursion in this module) but to update the CoreConstructible trait to always carry a type arrow. Rather than deciding what dummy arrow to put into this dumb fragment, let's just preemptively delete it. --- src/policy/ast.rs | 11 +---- src/policy/satisfy.rs | 103 +--------------------------------------- src/policy/serialize.rs | 27 +---------- 3 files changed, 4 insertions(+), 137 deletions(-) diff --git a/src/policy/ast.rs b/src/policy/ast.rs index 572d1ac2..cac72133 100644 --- a/src/policy/ast.rs +++ b/src/policy/ast.rs @@ -13,7 +13,7 @@ use std::sync::Arc; use std::{fmt, iter, mem}; use crate::node::{ConstructNode, CoreConstructible, WitnessConstructible}; -use crate::policy::serialize::{self, AssemblyConstructible}; +use crate::policy::serialize; use crate::{types, Value}; use crate::{Cmr, CommitNode, FailEntropy}; use crate::{SimplicityKey, ToXOnlyPubkey, Translator}; @@ -50,8 +50,6 @@ pub enum Policy { }, /// Satisfy exactly `k` of the given sub-policies Threshold(usize, Vec>), - /// Satisfy the program with the given CMR - Assembly(Cmr), } impl Policy { @@ -61,9 +59,7 @@ impl Policy { inference_context: &types::Context<'brand>, ) -> Option where - N: CoreConstructible<'brand> - + WitnessConstructible<'brand, Option> - + AssemblyConstructible<'brand>, + N: CoreConstructible<'brand> + WitnessConstructible<'brand, Option>, { match *self { Policy::Unsatisfiable(entropy) => { @@ -101,7 +97,6 @@ impl Policy { let wits = iter::repeat(None).take(subs.len()).collect::>(); Some(serialize::threshold(k, &subs, &wits)) } - Policy::Assembly(cmr) => N::assembly(inference_context, cmr), } } @@ -158,7 +153,6 @@ impl Policy { left: Arc::new(left.translate(translator)?), right: Arc::new(right.translate(translator)?), }), - Policy::Assembly(cmr) => Ok(Policy::Assembly(cmr)), } } @@ -267,7 +261,6 @@ impl fmt::Debug for Policy { } f.write_str(")") } - Policy::Assembly(cmr) => write!(f, "asm({})", cmr), } } } diff --git a/src/policy/satisfy.rs b/src/policy/satisfy.rs index 929fe3a1..a685d212 100644 --- a/src/policy/satisfy.rs +++ b/src/policy/satisfy.rs @@ -257,10 +257,6 @@ impl Policy { super::serialize::threshold(k, &subs_res, &witness_bits), ) } - Policy::Assembly(cmr) => match satisfier.lookup_asm_program(cmr) { - Some(program) => Hiding::from(program), - None => Hiding::hidden(cmr, inference_context.shallow_clone()), - }, }; Ok(node) } @@ -292,9 +288,7 @@ mod tests { use crate::bit_encoding::BitCollector; use crate::dag::{DagLike, NoSharing}; use crate::jet::elements::ElementsEnv; - use crate::jet::Elements; - use crate::node::{CoreConstructible as _, SimpleFinalizer, WitnessConstructible}; - use crate::policy::serialize; + use crate::node::SimpleFinalizer; use crate::{BitMachine, FailEntropy, SimplicityKey}; use elements::bitcoin::hashes::{sha256, Hash}; use elements::bitcoin::key::{Keypair, XOnlyPublicKey}; @@ -721,99 +715,4 @@ mod tests { } }); } - - #[test] - fn satisfy_asm() { - types::Context::with_context(|ctx| { - let env = ElementsEnv::dummy(); - let mut satisfier = get_satisfier(ctx, &env); - - let mut assert_branch = |witness0: Value, witness1: Value| { - let ctx = &satisfier.context; - let asm_program = serialize::verify_bexp( - &Arc::::pair( - &Arc::::witness(ctx, Some(witness0.clone())), - &Arc::::witness(ctx, Some(witness1.clone())), - ) - .expect("sound types"), - &Arc::::jet(ctx, &Elements::Eq8), - ); - let cmr = asm_program.cmr(); - satisfier.assembly.insert(cmr, asm_program); - - let policy = Policy::Assembly(cmr); - let result = policy.satisfy(&satisfier, &env); - - if witness0 == witness1 { - let program = result.expect("policy should be satisfiable"); - let witness = to_witness(&program); - - assert_eq!(2, witness.len()); - assert_eq!(&witness0, witness[0]); - assert_eq!(&witness1, witness[1]); - - execute_successful(program, &env); - } else { - assert!(matches!(result, Err(SatisfierError::AssemblyFailed(..)))); - } - }; - - for a in 0..2 { - for b in 0..2 { - assert_branch(Value::u8(a), Value::u8(b)) - } - } - }); - } - - #[test] - #[ignore] - fn satisfy_asm_and_older() { - types::Context::with_context(|ctx| { - let env = ElementsEnv::dummy_with( - elements::LockTime::ZERO, - elements::Sequence::from_consensus(42), - ); - let mut satisfier = get_satisfier(ctx, &env); - - let mut assert_branch = |witness0: Value, witness1: Value| { - let ctx = &satisfier.context; - let asm_program = serialize::verify_bexp( - &Arc::::pair( - &Arc::::witness(ctx, Some(witness0.clone())), - &Arc::::witness(ctx, Some(witness1.clone())), - ) - .expect("sound types"), - &Arc::::jet(ctx, &Elements::Eq8), - ); - let cmr = asm_program.cmr(); - satisfier.assembly.insert(cmr, asm_program); - - let policy = Policy::And { - left: Arc::new(Policy::Assembly(cmr)), - right: Arc::new(Policy::Older(41)), - }; - let result = policy.satisfy(&satisfier, &env); - - if witness0 == witness1 { - let program = result.expect("policy should be satisfiable"); - let witness = to_witness(&program); - - assert_eq!(2, witness.len()); - assert_eq!(&witness0, witness[0]); - assert_eq!(&witness1, witness[1]); - - execute_successful(program, &env); - } else { - assert!(matches!(result, Err(SatisfierError::AssemblyFailed(..)))); - } - }; - - for a in 0..2 { - for b in 0..2 { - assert_branch(Value::u8(a), Value::u8(b)) - } - } - }); - } } diff --git a/src/policy/serialize.rs b/src/policy/serialize.rs index ace99b9c..a2c748f7 100644 --- a/src/policy/serialize.rs +++ b/src/policy/serialize.rs @@ -3,38 +3,13 @@ //! Serialization of Policy as Simplicity use crate::jet::Elements; -use crate::merkle::cmr::ConstructibleCmr; use crate::node::{CoreConstructible, WitnessConstructible}; use crate::types; use crate::FailEntropy; -use crate::{Cmr, ConstructNode, ToXOnlyPubkey}; +use crate::ToXOnlyPubkey; use crate::value::Word; use std::convert::TryFrom; -use std::sync::Arc; - -/// Constructors for the assembly fragment. -pub trait AssemblyConstructible<'brand>: Sized { - /// Construct the assembly fragment with the given CMR. - /// - /// The construction fails if the CMR alone is not enough information to construct the object. - fn assembly(inference_context: &types::Context<'brand>, cmr: Cmr) -> Option; -} - -impl<'brand> AssemblyConstructible<'brand> for ConstructibleCmr<'brand> { - fn assembly(inference_context: &types::Context<'brand>, cmr: Cmr) -> Option { - Some(ConstructibleCmr { - cmr, - inference_context: inference_context.shallow_clone(), - }) - } -} - -impl<'brand> AssemblyConstructible<'brand> for Arc> { - fn assembly(_: &types::Context, _cmr: Cmr) -> Option { - None - } -} pub fn unsatisfiable<'brand, N>( inference_context: &types::Context<'brand>, From 3682ed65c664aac54a3d722f4b360acf838a4a6a Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 10 Aug 2026 22:55:20 +0000 Subject: [PATCH 7/7] policy: make serialize_no_witness infallible --- src/policy/ast.rs | 43 +++++++++++++++++------------------------ src/policy/satisfy.rs | 2 +- src/policy/serialize.rs | 28 ++++++++------------------- 3 files changed, 27 insertions(+), 46 deletions(-) diff --git a/src/policy/ast.rs b/src/policy/ast.rs index cac72133..31de9c36 100644 --- a/src/policy/ast.rs +++ b/src/policy/ast.rs @@ -54,58 +54,52 @@ pub enum Policy { impl Policy { /// Serializes the policy as a Simplicity fragment, with all witness nodes unpopulated. - fn serialize_no_witness<'brand, N>( - &self, - inference_context: &types::Context<'brand>, - ) -> Option + fn serialize_no_witness<'brand, N>(&self, inference_context: &types::Context<'brand>) -> N where N: CoreConstructible<'brand> + WitnessConstructible<'brand, Option>, { match *self { - Policy::Unsatisfiable(entropy) => { - Some(serialize::unsatisfiable(inference_context, entropy)) - } - Policy::Trivial => Some(serialize::trivial(inference_context)), - Policy::After(n) => Some(serialize::after(inference_context, n)), - Policy::Older(n) => Some(serialize::older(inference_context, n)), - Policy::Key(ref key) => Some(serialize::key(inference_context, key, None)), + Policy::Unsatisfiable(entropy) => serialize::unsatisfiable(inference_context, entropy), + Policy::Trivial => serialize::trivial(inference_context), + Policy::After(n) => serialize::after(inference_context, n), + Policy::Older(n) => serialize::older(inference_context, n), + Policy::Key(ref key) => serialize::key(inference_context, key, None), Policy::Sha256(ref hash) => { - Some(serialize::sha256::(inference_context, hash, None)) + serialize::sha256::(inference_context, hash, None) } Policy::And { ref left, ref right, } => { - let left = left.serialize_no_witness(inference_context)?; - let right = right.serialize_no_witness(inference_context)?; - Some(serialize::and(&left, &right)) + let left = left.serialize_no_witness(inference_context); + let right = right.serialize_no_witness(inference_context); + serialize::and(&left, &right) } Policy::Or { ref left, ref right, } => { - let left = left.serialize_no_witness(inference_context)?; - let right = right.serialize_no_witness(inference_context)?; - Some(serialize::or(&left, &right, None)) + let left = left.serialize_no_witness(inference_context); + let right = right.serialize_no_witness(inference_context); + serialize::or(&left, &right, None) } Policy::Threshold(k, ref subs) => { let k = u32::try_from(k).expect("can have k at most 2^32 in a threshold"); let subs = subs .iter() .map(|sub| sub.serialize_no_witness(inference_context)) - .collect::>>()?; + .collect::>(); let wits = iter::repeat(None).take(subs.len()).collect::>(); - Some(serialize::threshold(k, &subs, &wits)) + serialize::threshold(k, &subs, &wits) } } } /// Return the program commitment of the policy. - pub fn commit(&self) -> Option> { + pub fn commit(&self) -> Arc { types::Context::with_context(|ctx| { - let construct: Arc = self.serialize_no_witness(&ctx)?; - let commit = construct.finalize_types().expect("policy has sound types"); - Some(commit) + let construct: Arc = self.serialize_no_witness(&ctx); + construct.finalize_types().expect("policy has sound types") }) } @@ -113,7 +107,6 @@ impl Policy { pub fn cmr(&self) -> Cmr { types::Context::with_context(|ctx| { self.serialize_no_witness::(&ctx) - .expect("CMR is defined for asm fragment") .cmr }) } diff --git a/src/policy/satisfy.rs b/src/policy/satisfy.rs index a685d212..da7c866c 100644 --- a/src/policy/satisfy.rs +++ b/src/policy/satisfy.rs @@ -401,7 +401,7 @@ mod tests { assert!(policy.satisfy(&satisfier, &env).is_err()); - let commit = policy.commit().expect("no asm"); + let commit = policy.commit(); let program = commit .finalize(&mut SimpleFinalizer::new(std::iter::empty())) .expect("finalize"); diff --git a/src/policy/serialize.rs b/src/policy/serialize.rs index a2c748f7..b56ca453 100644 --- a/src/policy/serialize.rs +++ b/src/policy/serialize.rs @@ -258,7 +258,7 @@ mod tests { fn compile( policy: Policy, ) -> (Arc, ElementsEnv>) { - let commit = policy.commit().expect("no asm"); + let commit = policy.commit(); let env = ElementsEnv::dummy(); (commit, env) @@ -306,7 +306,7 @@ mod tests { let signature = keypair.sign_schnorr(message); let (xonly, _) = keypair.x_only_public_key(); - let commit = Policy::Key(xonly).commit().expect("no asm"); + let commit = Policy::Key(xonly).commit(); assert!(execute_successful( &commit, @@ -321,19 +321,13 @@ mod tests { let env = ElementsEnv::dummy_with(elements::LockTime::Blocks(height), elements::Sequence::ZERO); - let commit = Policy::::After(41) - .commit() - .expect("no asm"); + let commit = Policy::::After(41).commit(); assert!(execute_successful(&commit, vec![], &env)); - let commit = Policy::::After(42) - .commit() - .expect("no asm"); + let commit = Policy::::After(42).commit(); assert!(execute_successful(&commit, vec![], &env)); - let commit = Policy::::After(43) - .commit() - .expect("no asm"); + let commit = Policy::::After(43).commit(); assert!(!execute_successful(&commit, vec![], &env)); } @@ -344,19 +338,13 @@ mod tests { elements::Sequence::from_consensus(42), ); - let commit = Policy::::Older(41) - .commit() - .expect("no asm"); + let commit = Policy::::Older(41).commit(); assert!(execute_successful(&commit, vec![], &env)); - let commit = Policy::::Older(42) - .commit() - .expect("no asm"); + let commit = Policy::::Older(42).commit(); assert!(execute_successful(&commit, vec![], &env)); - let commit = Policy::::Older(43) - .commit() - .expect("no asm"); + let commit = Policy::::Older(43).commit(); assert!(!execute_successful(&commit, vec![], &env)); }