diff --git a/Cargo.toml b/Cargo.toml index 499c80b..429f77b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "casper-litmus" -version = "0.1.0" +version = "0.1.1" edition = "2021" [dependencies] @@ -29,6 +29,7 @@ casper-hashing = "3.0.0" casper-execution-engine = "7.0.1" casper-types = { version = "4.0.1", features = ["gens"] } casper-node = "1.5.6" +hex = "0.4.3" once_cell = "1.19.0" serde_json = "1.0.111" test-strategy = "0.3.1" diff --git a/src/merkle_proof.rs b/src/merkle_proof.rs index 2999b55..734451d 100644 --- a/src/merkle_proof.rs +++ b/src/merkle_proof.rs @@ -2,11 +2,11 @@ // and https://github.com/casper-network/casper-node/blob/76ea7104cda02fcf1bd6edb686fd00b162dabde8/execution_engine/src/storage/trie/merkle_proof.rs use core::mem::MaybeUninit; -use alloc::{boxed::Box, vec::Vec}; +use alloc::{boxed::Box, string::String, vec::Vec}; use casper_types::{ bytesrepr::{self, Bytes, FromBytes, ToBytes, U8_SERIALIZED_LENGTH}, - Key, StoredValue, + CLValueError, Key, StoredValue, }; #[cfg(test)] use proptest::prelude::*; @@ -498,6 +498,98 @@ impl FromBytes for TrieMerkleProof { } } +#[derive(Debug, PartialEq, Eq)] +pub enum ValidationError { + PathLengthDifferentThanProofLessOne, + UnexpectedKey, + UnexpectedValue, + InvalidProofHash, + PathCold, + BytesRepr(bytesrepr::Error), + KeyIsNotAURef(Key), + ValueToCLValueConversion, + CLValueError(CLValueError), +} + +impl From for ValidationError { + fn from(err: CLValueError) -> Self { + ValidationError::CLValueError(err) + } +} + +impl From for ValidationError { + fn from(error: bytesrepr::Error) -> Self { + Self::BytesRepr(error) + } +} + +pub struct QueryInfo<'a, 'b> { + state_root: Digest, + key: &'a Key, + stored_value: &'b StoredValue, +} + +impl<'a, 'b> QueryInfo<'a, 'b> { + pub fn state_root(&self) -> &Digest { + &self.state_root + } + + pub fn key(&self) -> &'a Key { + &self.key + } + + pub fn stored_value(&self) -> &'b StoredValue { + &self.stored_value + } +} + +pub fn process_query_proofs<'a>( + proofs: &'a [TrieMerkleProof], + path: &[String], +) -> Result, ValidationError> { + if proofs.len() != path.len() + 1 { + return Err(ValidationError::PathLengthDifferentThanProofLessOne); + } + + let mut proofs_iter = proofs.iter(); + + // length check above means we are safe to unwrap here + let first_proof = proofs_iter.next().unwrap(); + + let state_root = first_proof.compute_state_hash()?; + + let mut proof_value = first_proof.value(); + + for (proof, path_component) in proofs_iter.zip(path.iter()) { + let named_keys = match proof_value { + StoredValue::Account(account) => account.named_keys(), + StoredValue::Contract(contract) => contract.named_keys(), + _ => return Err(ValidationError::PathCold), + }; + + let key = match named_keys.get(path_component) { + Some(key) => key, + None => return Err(ValidationError::PathCold), + }; + + if proof.key() != &key.normalize() { + return Err(ValidationError::UnexpectedKey); + } + + if state_root != proof.compute_state_hash()? { + return Err(ValidationError::InvalidProofHash); + } + + proof_value = proof.value(); + } + + Ok(QueryInfo { + state_root, + key: first_proof.key(), + stored_value: proof_value, + }) +} + #[cfg(test)] mod test { extern crate std; diff --git a/tests/assets/query_merkle_proofs.txt b/tests/assets/query_merkle_proofs.txt new file mode 100644 index 0000000..c12f856 --- /dev/null +++ b/tests/assets/query_merkle_proofs.txt @@ -0,0 +1 @@ +0100000000c39d7a6202e5558ffbf327985c55a95f606db48115599a216987b73daf40907601c39d7a6202e5558ffbf327985c55a95f606db48115599a216987b73daf409076000000003b8bebc1c31f9febcb2946dd2f27dc710da28b1ac662419edbe87ada286400150701000000c39d7a6202e5558ffbf327985c55a95f606db48115599a216987b73daf40907601010103000000009def00000000001f213de4d51fbc14f34c3a6c17ad96b0483ec67b4539c78ebb38d9ac6536091002019e6bfcc4e5bf22ffb1a7bb0ea34798200435969f4e05122b9af2939579853fe2040178e34a9ad29d864c674a031ca4684cd5272b7fd89035bc8d193fcc556f7b84430500d0e76b8c24cae7c3e5c95f62cf94a4c329aba208ed4426f4997dae95d488e9a70601f476387ff52f01c79ac36cfd0d2051173aa6d207de5a655f23e75a6e1d8759ef07010101cc1a9e3c79865cd9bae8d13871626c5158f5ea4bede83091ad46c191e0dc0801c85eed98907e9470868c11d93c991e9c941d084393f01c008e29782d734860ab0901cf12163d827a8c9e5a6cefb44ffd4597bde32914ecafd3caa72a00ccfb28059b0a0010bc16efcc4ca018de199688d8148b63f27f77433d56bba61e94abcf74b5fca60b01eaf3b44df0fb8e95eb514b1ec51d0ac976c7057af0961ca4dc402af73cca01710c0173deafd9db193250e2209b3a23ebd6c3d2c8cde14543eba65d90df334b4b1b830d01697b9d71963b430d13843666c0f39e41d3ae8518be4842ae123c37d3ca30e8510e013fb28fb5413e67ef2ac0ef92012ff44d7b20e501a92735000d0865125ecc2b190f01a269fd2544ea0a4f3c032d0571d980a388acd7b7fe95699b329bf07b015f98ce1001efbd9d7201264212060828f9a123e0184035ac51eee9c43f6c2ba819fcf26cc91201f2712b2e192b33d5eb45c28fe4651e855f4a3420cf4152a2ca08b7e4b5b5fdfa130083df3b9a027f39cf49bb186d3a93d424abe2569f64bb4a236734610c2d96991f1401db2da2312d8894afd30a59a64f222cdfb5eb94ea8e06fa1d4819c0f860ef439316015ea72a902d3951017a602f2460550addcdc1b93d72c7d9647ac678379a7a0e911701887dc7cf35337a36f8f7998ff6ce6a9edc26186f75a0957a474a255c5ee943ec1801517a37e83ba9aa77e903b0452851964597cdfc31eef03391169f1a40499480621900743448aedaee71fe456bcf8daf78cbce8d620d1ccc3603858be1eb20a1c138a91a0143015517acacd806f384af7e2bfdaf752a85cf0610d38d1d55ccddfa3ecd2da41b006284f84423ae793ef18da172487dc8cda025c694cfe34cce545fae138a012e831c01cccfc2fe7ec3a83c42a529a852b9ba7df115113c8c61d45c3b284493980b18011d007b48b8fd729c9818789d692060d86134143210c93faa0c459518d5a7f0bbb7791e01488c4ffb9079c39c3c1a255b338fdc9197d50cd61ca962751a9543b74b17b6091f0189087e968b0ff93abe42b1a9e0753559176874d3d8d79b03285a6a704c8a268820015c2bc3bc1dc9d3fa6371a6c7b4fe43fc5906879539edce256e1c042f956e81bd2101b392b8825fe25260dda24ac6f9b85d2985fe4b00339919fe3869232cc6eeb64522017db797bbbec75ac3bc7c9fb7c2535b04d7bce53ae072451a1e71411c4e59be6d23016222af1d7c0f85ea9c2d82f0e39cae006f7aaced20a842f57668922f487c9c49240117c508eb5d0655fab28686a7fbb3b496b1a7f569544f616c1bd3f65d4ad79763250171237793aa5f6ab2a28843af44a1e6ba4564db9f54cd5382901e8fd88f2c7f4e26013614d6707f7e0f6c99cd74cb57197085166dd49342e47784c5bd502623060a8027017dd86374fc53515b56ee680eadb249f04b756958ac0ff78d5ad87809bb30d05328017b7d679554b3b2af528d79cbe930063acccb79a65e819e79a388d5025fc6c3bd2a0001ef724012d66e881073eeb3d99c4dfeafb7aac14c390696f35e1a14563e74892b010fd8a0d62896bc126426e9c1bbf7438abf50837f99e0e309654bf2950766beed2c0170f42146dc171e7e082fd5d021e3fb7d31ddd570ee79c3d1d87105ecf4ce0ae72d01b4972d082c8ade7536758ace9fd60ed8b8ec2da127e8a271501ed6527e735dc92e008e438718b11edd915f23bb6e3363e74ca550beaecb183527f489fcf54b4faf672f0088ba7ab94cf281b7c2e239c4a63f29033d7e53c377ad9a6f80f9a030ddf3734d3001818120a3efc6dd900c14925eb33eff1071e7b0a0281bf93fdb31c780cf191cdd310198f1c0e4a97851ec6d83ca13926d590c243eb1702e23cf9a31a48dc25712102f3201275e5e7122d00a29265e0d19e1045076c8f98d190b42a5e6ce212232e1eb6951330183de0a519b9ee21a5f1a42c84e4b43facdd5a3c5eeffd3009b0f7d43e92bf240340107476969fea6feeb31035e03f0232a72beab096a3e7ba7164fbff9c7f0cffb9a3501dd2dda520137201ebcaf4edeee3df3a9939cbb04f03382a3473c611720a537d836010bcd24cfd8469c093a6f417e96d391a02e3988b9e27bde17cf18cba1940ec5e537000ecaba8414b148c0bd09ac2af1d1ee12f22e27f92332ef54711f9335e2c1795a3801b3295b5802f2d58adc17c3b91cc13e6289eb5ba8b54a1d4ba733909f779db3d439004189ac8409b901d1b93b8d53823c2775a5d615131f7d997c4e181727e2bd7ca53a0130e7fd517411aa9fdf24a5f83e791cc3b4251f4dbb7074aa18651f2e45e93dd13b0152df51e51893f22414641802092ef10fc16f6209547877c463ddc275ed9ffd103c006e0bd16bde2690bd3ad1c98ebd35eacf5bec2706ae31cefa48bb20ce6021eaee3d013fa51301b570f245a4999800c916d04b5d8072721da658fb2db74af07689421e3e01bd412a8f535bb4fcd2f199b1a485b5220a0784850fa13e6fe56e0cd80d574bcc3f01836d8874bf09ed6c68ddb6c1a7c212a3d30b2248725656a36fe1b9e7cc3c63b74001d5870daef52f537feae0fe58273b617ed603c5945bbad5c5db5c6242aa958d1b4101c1d685fc05a4563bb08781ebd387ee2fded681055e325517856994c26f84857042017a2364914ec59ec26455f74fa5f8352b084ff58450b5cfdfe4608c358fc0f48a43017985cad3c39d529cb1159ff2e8173d497fb2932db0c940a1634b639969f3510e44019199727274f31b783c12a7443123c314ddd0dd616bd6eaac19622ae4d30c5a5645016f9ded28f07fac244c1e8f61cede74a119ff67ba1f8aebfc9dca7cfec1eae64c46019fcfd329a30483fd57bc37a61a60d1fcd0c6f7dc4c859f51b09ce9214462e34147017f3de81bb8035cfb789a345b1284f52dc171f1fb1de49c78b5b2bcb6cc1348a048016d1f718fc36f662f9e3c6c84fc49684932c5cb379993ef2c2a6d3d5a5bdf882b49018a0afd921acf6ec014a2f2adf6dad0cbb5efe4038167e278c3d0c628611b79b64a01d69bde7a829de5a8555f194a1662bd3cd24cb0de8763c69f674cb2d0510fc20f4b01d245c9a68d9a1b5adfc645ab276f28de38a3bbae632be4075352e773f3890ab44c01308f7a87aa90bfedd74b0cda269e8a9ea0fc3e6e58d0379b0aea40ecf00a4d0e4d0148f2e711de23f72e8c47d4c4dd6775dd8e5c4747ee85092a63278974ad1b3b264e00c3d1abb9d7be3784fe058a2d7b97c3f0303732e0080e11901e9582be42a196a34f0105d4d1d30a002f710cb23186fbf77696b9a81ad7f18b2f11d8c61f15fcf696e45000ccae1fe5b2ecf83b738acce07a2a6eb28a09ea3d1d3e9c8c7a508a5ac1476cdd51000fdccdb1c7c494ae2f3656ada2bad329cad19233a0cbf5de21419eabd15f19c052013682acdf4359baf1fcad814b9ad9629b25f31a582c55fce6ba3947a85984a3565301cf5baccd7d4e25ab1adfa4823276b7db6cbccf61be9a355a152a021754cf46215401f77bcf104a22c6d165811fa8268efc0803dc6ef9749460ab2332ed656160420555011cb1ad666d02dd745c546f0deb27ed90385647b3aaa8ebe4678f054fd98a2a025600e9c528c0b60d98885f515d88fa78a0b216c88e01b8baf9d0ce49811d434d8c175701ea01fc8891b15002bf18ab480aa3f39c3aa913ecbbaebc6538d7461c7d45c70e580005453aa519da5821f161f5b332a38ca1e5c6b84fb69e0a4dcd514aab5fa48b9d5901900d00202c47b33218bcc4070352fc597e2443dd89c892f567cde367e793c7d25a01b750e851514d58bacae27a8d41a06666dcc146ee4acacd1314d878305baf26c85b01333d539fb9d45009d9b052de895dfdb25b64278eb6dc4db8a02b1df8de663fa45c0142c105c6c8c70cea9d50766291a499a057aacf7bd2350fef3e92f98defe2f0235d015500d6455af799e99cef5f71677c4818b214b27ff2e7cbf3b3c1b6a657b137005e0196e016b7b6efca059863c5c5655dd9f979627ea98d8e65b8db20d5faba6dedba5f01cf04b386c7f9fd3eb70a457154927c3256b70cc7a2a93272744c574af98cd9166001b3f57d40f5fba5a8d826c4151b8881335c0269ffa59805a75c2aaf2e5f1f78b661013ef20dcd2b15cf081726a5eaeaab68b9afd6b87544259b6077f26c504d6b146e6201e43f546eef60123f3aa426146419c1a7a2eac36e6ee1ea2c84baa6681460e9016301cf4cdc2de7b14be3ba5ff31e6b14f3c6c4ea654b3a31d1908ea6d1d59bff39ba6401b0387db92509facc031e41feef99fb6f170f6d61c0fc13353087772641921a7e65012b55458257a044049e165be076786921c3038b8ca56f87b99f88f3ffbf2fc7af66015d0ce9b985205567974e75df34879b234d99b9777d1eb59a4763e411517f1bed6701fa3113b67008ea47616641409491eb1d45f20cbe869d29b7ce60fdc074ad608168012a131510868596fc0120df1765c020f37f128776a196ec1f70eb8f5670b22cf16900753f4383cd45b1b4f10e706feff4c74b35ca0a15ef55ac63bc981b33a25f32c06a010f9362eb3d5efd0c4b7f2fb62370f40ae5b238bcec508ac5d87cdae598c093386b013278949cfcfd61304bd354a3a49ce2e107d04ed93ee1074ba2f09fbb65200b9d6c01811d984d1c52d3460bfaef4b0ffcc250f16ff82cbdf0c1e9db7373ff0fdc99b66d01df76595d4c327f9ed2bf47a9eb32b49dd1c01d16bda1ba657b2ee74051f30d866e01b865f3fb24f69fc90d29bbcfffe8a1fe7c04d46f618a741682f0beab3a9b2b3f6f01111d915681b67b2723e6f55ae42a59df13792687a7222f852342d1ffecefe3b97000d8f2482cf1ea5f500b72c02bf0a4405c03f0e44d97fa61e28a4cab5ff4919dda7101dc428e96ad62f4850f4c8c911682ee15208a31fac6083247a0ee891923558b0b720142669312079f83e89380900bcf6d15f8a0ba49a316e298593ed8c2c0b187ac1e7301b6c85df0c331f90417a4dc163bebbdf7a977197ea11e65f64e1837e464ece2467401180c589fe009d53ddf8af07d0ecca8b3a6d6c812690706ee59f33da4155fd8a575012499eb3e557ead480e0bb8cf03796b2f66b8bee1a5f4226b4704c0e916ff792576009450e67228d2435dd5b3a7c1e4df4088569dbd5715e7f881d817956cde7c9a9d770107edbb3f8846e3d7a01a92315c48c3c7643e0568db3e53daf9750d8a6be633007901e684e4f3477a3de74b3da322d58b3ebfed3b0f68ebf457337cf30895d2e997477a01e396149fef343cf03e8e8295cf78ac494398ad7cac5dd65a39c54a4115c0e2197b003bd133f81de757e3a3b6a9183e44a01b5b77359cbd2784c31a063797ca9f3d107c006c61df0c6118298af37752ba1fc50344aae11450820e6280a05035ab47868f8f7d00121715d44d9c1400b4999e1085d5285b3e15c066e4310ad11f0659f0dce758a17f01929136956866cf0ff49faad24af4dd9f3bb48d9da0480659ddae3efeeb1ca7c88001cb503f8caae6bbdf679d28f6b705f2e9ecfe8a0064be2be11fa95e3705f2c1c9810052593f8cc014cf6bd00418ba2aeac7173e5f740523105603857efa3b54d4bc6b8201bef59a0b3e089f0a69b128366617e73b61ba037c8c057a1409920c93aee001e48301d19db222ff96d132a1c7dbc255be3356376541bda6cce6e7dac462f0366e3a7f8401650d25f859e203d1248617577509b31c11e3398df171aec9cad5f0ba3d7fce0b85018a43567f487df13e9369addd3eb3469b35ef8a5fc87eb2a500b2574f1f9526268601c312c650936afa7ffa760ef3ecc322b2e51870e2c6233fb4a4fd4973915f4424870099241707dc2be3b87af587ad6f94a6764c200dbebebde4ffed6e28428a0df4788801636b26215382b4029a800213c95dea70658ccd7c35c884c20ec781c450c6c08f890176d5d15a0423de1767636bc57cce55f89556363801c45e8496545236bc697fc48a01908e3f41cde0df35d85f687b7e694bc96c833bde1a9432a17627a43a1eb559e78b00a5f6de0dd9c03523df4098c0ec4616374c57305ff79ed2acce13ba7168aec9758c00ea82ee35fc2d47c8fb9bf47f19e4eb848a0359861d72b855dbb7f2f4a9d267ee8d01ab44cede9700fb3070ca79c9a6586358b9d772baaf19060a9008ac43b75166008e01e26018383bdc042f1e13e0948d5f3cdda8f83b499f66aedb35dbaef4f3b6903d8f01b2095dcc57569558383c7c9794112cece5d49fdcab3ae21f2561c5b0196e58599000f5149d747d5ffce1988f5fa4cf88d42d24703a37c0b65e8d446d697c81f16276910180ed71c7dee858bd83a85dad8095374dbe889fb9f92b080648c756158531e866920125d5543dcb60c97b91e5e6dd1bbc5946e04d43239a6845af7022de3ecf1d8e429301f5ae5f09e723108107140208af13d87485c4bceba39717786abbe432fecdb4f494008965cebd1ec5cfdcf980a0a93138058445db8d68e2ed97bdb2fa0208a7b9953a950008c068d6af14862baa5d308c5e3a59fc47b4b533a2b867bf766d8e1466b569f796013ce1602bd923a61db95aeee211a55a04a77de179c42d0a91f9635f57f832ba8997011b6079f75349e3c70ed039fd68456e1a749f5e3071dca88648fc8ffd1573c18a9801cf7ea2f682250a3d4d3fca11e3c102298655488700e97021ef31d5ffba56955c9a011e5c0edfc04612ee7177881ef194621601710c53bb840e41ad3de9aefd1a550b9b01be7ca8467143f91df46145e28c1f7ff4dec55cd43b3507cc721c200b0592b63d9c01bacfaba63dda2dc9e32ae6750cd6b79a794cc4a8a81362ed17a475654221cc8d9e01b2ff89f6a32b4fcad856b0aec02abd07b35934e4e0543eee87b1ddbf243455b99f0156c3ba042c6923dc57e23e3eb19a344c60623a19b623be56f6c8b2b281e58abea001e1dc6fcf7f134692aefd8004227d2b3be54155735450d05a50b48502c9cc68faa1001c0defc36d8bd711eb36d1aaaefe708175ee8091617679554a897d33a0a48ab4a2000d51cb57f7de4734394ec519a8641dc3f8407cd0b9fa0f9701d7d9201ec9bb88a3009b6dcb719426e451ab0fcd75c442b847862589a8fa40678cd716973b0ef19116a401a293943cc92dcbc6199f8c399684b619bb9331fb4adee8b2ea2670357cf23fa7a5019b8effa9b0a3b18c7bddfd81b29748dc2e928800bae3623049bdf2d63b82e6e8a6019f7cfaea98ccccd106076632642c02dbbf340512ec2e12986cf552e381324793a7011e870a55ce514f09767ced943647a5434e105fd77f4b4559d371e6a3a97d9aeca9010d1ec9f5e76b0ab311a41b68ac68773368b4f47bd0a8d74599e39ea687cbfc15aa01677eca2fc724026ad72d1bdb8f1c2a1eb8254dcdba6d485f3011aab869c6c96aab00c69fb4880ef46fed5510e30685ad6c535a1d74d4bae083431b655dc13bdb4a85ac017da47d1a09f65b488f927fa7c3c0ea0baacebf7436259681710b1ce65357538aad010f0c36aff7b7f0e58a020300e3eab98820aa726d98562b9030110e4ab335036cae017d9860e616309c888f192aab0a642398dace5166f880b8f806f28de6f7549928af01ea7b22dd592e584284fa545e66f2348a86123d867766e83019d73188dd05302db001ffe986fa93153cc1ef1070dc2f73b8df74d6d9600f75be25af19ab661fa4e385b101814480931633102b0fa97fc71b22e82d5250fb453ee4bb76d424d435bf6bad44b2019178c2edaa1068a49c7a74f92ebf465acac708fc1bbaf8022dd4d53e95480780b301f30f1f1f96fd1e3f9d6eaae11768e8183e3e9dfbe9f41f44484ca64d6e98ac25b40074961a651fbd1914bc6588a05ee475ee4231d1a4c8961b1947d799286ef93793b501c477247ce9b23fb8c45b3ed9a4abd86cf5f05f7a468544d72b64ed705ff6bcc6b6016ea58844835d5b69ce664247d0c913a673d6d5888411a26efc46f212ee39dc5eb700ce3c7a9a4e555e72422fe1a2565631a28cb0ef5c9be73396da294a24e30065feb801bfbdf08f00459aa80fee10cc5c5d5bf90df9bcfe6e0752ff240678689a336845b9015c52d415cf14bc9297720fa3c7aa452c940514b2d5fe3a6615d9501311c2526bba001646368b6591b97183535d17c78a9b73c0b8aa08ca5bdf6068ae5b53291519f1bb00d82064b438cd5b75a98aa26e43db5237c4810abae67d6c40554ea4fba28f81a7bc0113c56370a405aa7dd2a7fbf2e23b9381e34531e40487ef14240e7e44f96a0d2fbd016f03e4267000085765631aa21eceff7d1073f280ddb5ae7dfe50831daf0e5600be01b171f2d6f8f84506043cc3b9947a8dfd107a4091041e5f68fe99f5f8b186c87dbf0134081c145d10a9a90087fee42f2e7c3abd56e9935be1514c70086d5341ae560fc0019db216fd342788fbbdc8e1e2f4cb99526f5c4dc53e5782e9ed06767da27ae363c101b02f1a3c8b0855d28c9a7919b5283b2b39fd491d0087fa53cec064c46c729159c201b29e5550da15b93d92edc531c151b0b5f8eaaada415d273b19bb6c3247ec0b82c301ffbacb2e1a162abc08f727f9ef71970f4c4ce47ca834f4ca43f154ba57d5bd3bc401b2ee983be27f96862aed0536d8b03dd44953a38cd1b8cf093cdc2ad66050b6cac501b416ceed2442664542b670e064d19f43ca61901a8fff5ce2dba85b62adb3a6b6c600695297628d67e115b9b93a05eafd9d016f82de6ea6d6e9c496908803fdb21402c701db6d29ca61e289d7af3dac139b7e4ace9c3a9d3c5253d31e4b31e3826ce36b03c8017cd8be618f61c27a7f00fce122c701ebaf7e010ad0cea16555b67f5bc10fbec9c901885b0e63da52346a0590a53646c3ed04d86f0857c789e1227aee7061b7f14c02ca01c065708da2adb5eb8664d55b782ba3bad67c904ebb7959d0cb6204c481725345cc018d8b34da0ca10d8d018f8b82227af39fef60f278187645658f691328cdcf0263cd012c6722c1045c45c2c095091cbf3eca4bcec61674cb3b4b105016383de67b2d98cf01194ebafb09aa30334593fc169a390fd058421f4409e8487ac3c76514a40adb80d001b091b33c728d1dbed5071b00d9bc0079e39c51db26ed5f9ffd6d9c0b5b76163ad101575bbbe9ee418b0c94ca3af96c38aeea3c298a687f55182f0c6b3eeffe52f338d2000ed96471cd9e2f98af159ed2d8b2ae00901b44f8a29619b4d4bbe6db2ece455ed301f8148c52ff213ae6a3408f4c55a95ffc499b9f6e96f534bc01a3d836c645be5bd401ebf5f13a5f2f8f1eac162cda437cee7280b8d8e5373e636b5872345ff75d1ea2d500f9f043e398e9acfee92c3f1008760ed471bb1c35f5269b468af13352c286e2edd601708e795cfb747465227c63ab77aabade5b1dd9a9ad8b9e43c292ed59517a546dd701587e05f065001a872569fde01548e59abf454ab90654521c88db8560453bce29d901ed764fac843afa37386e28e9cc9e679e8ad4190c446554799953c6f1c0a48fb3da0114971818943496bbc73ff85f0f7bf3e8962c944631eba67675e97e7dcb91f4fcdb018d4df02f2d6f18e76fc7300e714ee47f7fd3b1da47ee8e64479a3b925bba0025dc01144997f81e05358d21ab74bdb4a83a236a0ac3d23291cb8f5b5d907ff6560bc6dd00e856159d97100a42bf7ddc7f8cdfde391c417e603742b6eaba7a6ba36befe58dde01eaa607cad58ca642a34253372bbfd45b1a57dd97768cd071598b02754400b72ddf00cdd0c91c819805c7a2c967eada5fde021b390ffb756e8ddb214758f6d3a395d1e0011ba65396cfc9d2ccf379897482a49fab99383eb1df31b13b617d430b0be1c996e101fd3075e35797cd34356dfcae63f1fc2118450b27c39789cfd23fd7206f3cea02e201cf836564c05ac2e9d8689434e7963d039b19152bea54f40f9bf2460457e8bb15e301d0a88d8ea2525b805c1dee855cc05ad5fac6a4847172e79b81acb50452dfe318e4010757fd7ccf5fd061147fa1e016d55b12eb494b0ec15c5f8f8921c5d59bec5f0ee50182f58ed1105c229b97d85ccd57133f88de5f54136a246436d290782e46c41aa0e60141d902380a324d29c75e159bf04009a64f64d3a77aa87120943cf1343c6a5ae6e701a6858718f75ce88878ce44187e0120f1d9c507fc125933752fa9addce39cf88fe800f0de4d792f2ff3059cd2328bb882857c5564a6737c5c1bd2a1508c5ff060bec3e900179b9c25ba73ca5f66dd27c3ff29943bf2b250d0e4ee5a30f72eb8eb22dee92eea008b4ad7b03984ba6d5b029d94d0c83f807808862cf0e922d5c2fb4c288fc232bbeb002d53f763cac6072f05507b0dff093838ec787b5dec8a1a0cc24ff259e525506bec01319c21f27292cdfddebd7e4d94d3b0cf6cb3e0d8fce3709fbe0e35ad6e6dcd87ed012d7f4024c3109e7d662d971e6f92e36e500249cb8fcfbf2d30a0b2bd618297e2ee011886cee73f3399c9c7011d990de4b1aaa37d69ce6adb54a90a2895f14d3c7fabef0056c270b3ada21e780b73977885ed0e14f8a90f5eec1da532a3ba0326ae5c0012f001e9f28f30cc94151ecd155dcaf5710d0def06281c6f409a4f728baa5bc2a1cea5f101343dcbbe8456ef20a7d92cff91c2f8d1ceab44e8db28602c0b5604100732ef3bf301751089949f4ea77c17c993da688ef9e0bdea0f2275d429c5a2af39cb8cf36082f401a32ab1cfbb6e838c0e20a7901d0289106320029185766bb7d8329366bd094584f5015df5993bb88542a38d3267bea4d27e513a9fdb1ad320307e6edeb8e19a03a00cf701d6959e2405b0057e75e93623545ab27525051da64e345c174017621f6bf0b35ff8006cd2995e422a9a50b4fb1e542014a4d5826865810ef0b21dcd75d71f7d50b40df901b2e5dee13e563d55188b747c1739ede819c64823a8dd31dee1f428926e0cdd65fc01b03e12b179c117268f1e499c55e8271ee25c898a52d6f609ce0678344377a5b6fd0160b9853e41f79d0a6f1d37b69a4668a2007c5b626194c800e2f31db4b0f3275bfe01257f59aa4a824d63833fb608cf057f273a37f17678724c24bbe02c93f67377f1ff013a63b41f76d9f8888cf6d982f37c664aec1a4915a807c6f17e14fb0941f3cdb700c3ff0000000001b7447295762bb6346fc68685039ecb031974fe8da6397262fbba8e8a834d99c201016df67362f1f69055b194afdbce69f3279034f53d841391326431f90a9fb0261702013b79bc40e9d0864ee5beb2c0a8a7e87fb1457f2b45574ece271bd76a2103b98e030170a8d626d66ee858e98a1f876c03ffb7c30bbe0a2fcf9b6d5d9f5ef1a27f051d0401c91df9f381763f46b947c34a43505ba68b39d8cfa3cb14be6446c7810b5d74d70501f69163172c09d0b55028f56ebe32a21d02ef4df0feec8eaa7a420264f312d6880601982dd4e2798ec7be14f16b21ad20d960115ce9ccb997a60632e915acb28737ba070136ce7b1535d855df9d1cb2bacc3d0790518c09b1501835f1a3bfe0adf2595831080107a618bb7f752e0766a0fa83398628b5b90dd873f532722a24e8119d9691b0f109012ef003f7a6968ce6c8bd5a839d7b9b4b5bd1863b13f557281e3852186c1c90640a01853d53e73251d8f8469be744d035eeca828343dea2de4c2ee9dd0630bf12ae5d0b01271ac6eb1b53d096857338122c10f5e5e1bf1af93161cf7bf07a30dbf142a60d0c01b472ba8719bbada8a2167495bfccf846887c919493ddae5ccdcd9bdf48d596b50d0182f2294253bfd8ee2c771138a8d9ce0062268d9ab8f6eb77ca1cd915bc5053210e016bdb425d00e5a7238448f2b9340ce44edba98aecacf23969b7ab80e9f101b5760f019ec44b971fd0a9c61fb1f89a59af1b491774bd8e0bcd139e337a697d1762e49c10014a73a52421af9aee548757c3edfadc49c04bab5d85fa8e1a12941bfaf80ce8a71101965463b4b813b3fa5100045cb16d98ae68774ae046afec81410652df69ded97912015f1d4e66fb7d8dc6f482d2d87a3f9b80684f56a45106a8c934775a5e799709f91301629046d00d0b98c12ac518d09b7d8f12e7ef20a8e009bf6ee73eb645e65fc1e81401f9629266b61ea7ff62c6e386e8932e840adeae5407840f408449ef97276e27381501530141f1d3ca31baa81816186346b86173b51803ccd140bb7213ce71824a37071601213331f4b4109f449355f2ae3d0999ecd40809ad8fb8c4fa071659ea33abe7ac17018ffb69df95c15c9f932476ff38729cd44b8ea6de6f67639d3aef312ea4a2e0851801c507659375c5fd9a959daa0d486c65b876fafbfc7d25bb55e094ad4738c50f651901411a81d840e05d5441bb7b0ed3373c8d47af7f10b03446fab3447499f3e030d41a016a6f6d4a50ea8b0eaa93ca5c25844a315980a883766abebe9aceecdb9b5bfe091b01f76781d473e6422775e3755051d9058c81761b38c8a2d6f26126c1f0999e92351c0121e1c195d1a417b396306d2bbc657a6a543c3aef65d339528edd4d2b536868951d01bc13894b10cdd4ee3d4d3326f622ecf6d553d91f214dc2e1c434cdf62931fd831e014bb76d8b093091fd74b2a53e3f2aecf9c5e5b1762986f7652b022462048ac4c51f01b2e0a2fe0afcf09403c7b10f41002135ee5be3c2657efb7d0183bac5aff0670420019620ffd1c9af3d7912e448b3cf1260d195abae2040a35f99fcbea2474439e483210184e412952626a0b1729fad6c6a1f38cc2aca8d405e7eb8251227b042b723304422015ed5169a00530a40e8de4a658bc1b05ebf5bee93dbcd0bb4a4d73b29d18ecbc72301ff496e074d5650efca300ed62a2c5abdf3a3782b16033fe0b455af33bf8db4392401a539c030f471b5529ed7b191b8eef093be222c6bd967e3b8f715b222cc8401d125019d421a7bc62348584435d60ffffa0a5c5bc955aa384decf199a8511dd71ccbcb2601ff4150a5272682869d26683f2d351582d4a5b6608686686c266a0e7deafe559127018ebb1b63a0ca87b33aab1474f7afc381b77c42ba24e5dfc99e0cc29dcd873f7a2801914d0b6eef81900237e2d05be03aef7f2a8be019d49c48218ececf4d5fb9f3002901d7ea0ec0100461f3c5e515d1b20a6f92572eea2c7ccd98996651e3dfa6e45d562a016b3d711fddf560bdc4b31085b643cbfc98df8ab67e5390cc11cdca54d1a707692b0192a449f9981e283b1bf1442acfc5921c17241b704fca87a2491300da0f32807c2c01c03abf18d63c704b62a9cada0962e05fdf2a4515839a2a39a600866c51d3b8932d015bb641e016a46302af3544da70d724533b897fea9c6e579879bab6ed9161d8b02e01e35588acb88e460be0078780f4dcfed78dd27bfa0b8b5555301b742b9145eaf62f01a569ebd32917e78ebd50c2823751967a7e9ee9242fc3eb664e6771dd1e44deb330017878cf7523bad8f4b6572a8e20f18d64bbced7d584436934aacc6af14b6d3a94310187a3f0534bd614970a4b03d699b180511bc562d24f9df8476355815c35748b46320136f6eb1703157647821e464c8b66ea089d8c692d5fdd20024a0d29796ae989733301fae6696e9125dbec0fb1b1477c72a35997affac6c2a0ec40c93c2af4041f409f34012202b7a421b5475ffbacb9b81feaa1c0be00fae32113afbd3b7c26b5d1d130633501e77f126526c6bea1641d63d342b1231f6e4125bc884cce939441a657a1304bd536018a22b292a43d6616e678ecca7858ff1270ebef4e93d998dbfb3c593d50af8fc737017668ad954bde39fe80fdc453d0b4c561b86b44813838a4e30c0345ca404f59c4380196d7dd65ba4dbecac505c519c478035608a128ad35490311d070115984a5819039017f4d884c42749108cc1be2e705331f3d6f35657a1b69886ab2e7bd78c9dfd6ff3a01eb03d7ae6b131b6495bfad562a97149ff8af5412c7ed32970d52326944aad0873b012d0a8e88b123428fc8a7bbb9ec12f0c7382909920892de1528f3649435456e9b3c01c98d47eb05aed8fcbc9abb3ee63ab83363a48452d475c7123ad5bf54aa3343993d0123d210e2aea8e1b3aafc1df904e1ddea82f9da59aff18d6b6f4968a9722615063e01f03de44a183470576073f47ca8bf5e8c9828c4eb770526fe109c747b28250a243f015c95b0fe6ad26b78c0d9a02aaf0c0600545a3bc3fec86bb2c2cce657f0f157d440018c7977d8a002a809c275dd5c3cc7b3168503d9196b35d02f849aa846ecea6b524101cacc72ea2df00fbfcd180c24671271e3b716b8f07bb3f36d78247108bca43df64201558640f38a1c74c5b1c59cab40103982bdea5a9f8386dfac38d7e194521b9d144301c3bb4b7c06db0c4cbdc819f6912392cf38a7a590530c85b43126bf62a678f7da440126cf37ad827ddd9bb235f79f75bd7aabbed1a1f8496e3eb0c5061afa2985fc9d45012adb88676378d093df91c4787f914720ca5e72c74258e3fbaf284c1020adf2f346013a6df9824a87609d59320cff9cc841cd420c9554a69f543364bbfa643b30ce7a470112ddfe080d4def1612367b70a054c3dd52bc301230b58c3a0295d4698c2b19b5480136e4da4e2349d7087e7b2a3d70f22d2a5628da7a6027366b4d4158db582d10314901be47ed2cca2df5e31bd8232d602e6246d7a8156ca9d4f4620d9e4dfbb5ce07544a01817d996b480eb284de773cd4141b81aa04f592df5edb1c21538bb578aa8b7b2b4b0145e367a6f299f24cab19f3aa283d6df74e480665964d2a83890f0195b62cc9cd4c018f300ca7aaf046504f273a0112db441d93fa5a46635328755d835babcd2a18f64d01963b4e81d7bda3d892e4e0846878a9e7f48c9aee8b4eab79e5f5be3d963cb7e84e01f2c95d1dd2c3a6b969488eb01816e9c6c2cb5c2673a12a4092ac33c46e4a524c4f01ea6fb2e3ad01124b705ae50338107bfd7f4059e1a1db2ba165f0c6a9b990815050017ed42f140b97e01c4ae8c8f3193546269eca6cea5525776a870819b3c53977f551014eb38b5e26e19b73b92c05bce14a254641bd19acb232e4fad52c02125544b053520165a2436ff1fbdca07ef644ec9c82e8abfedeb6b62004f7a07d33fcfa23f98bed5301378c34dbfd7feebac342d39283a9ef1a2c28c9e9c5e9b23c6aa168f1526a53245401424b44a510268fa55eed64094bd0798dc4264c2c7dc244b1197f23ee5d5a1f9c550126f86b6fd341f866ad26045638a4c42ea31b17df9543b548b84a3e4c314679fb5601f2e2a5554b486595e78021981ceb3205dc7e7d9aacdea7beb070f032ea237f3f5701de5beec451dabba6a12614c4d81b86df269a00f3abe27d33f1aa23b0bb64fa865801d375939533907d5d710ccd1fad6dd460760eed97411c02e32370c1100b8e2bf75901d86456496415a70c425551b1678ccfa30ae7ecf6b6eac2df0a7c4ef3002ea3f55a012dcb7027793fd91604d915e33a3ce981188852c5693c76dbd38d40065c6d90e95b01f2d5c20af4ddbd178054f99751d4465d2390eead10f8fe1fe7a81a5c62a3b85d5c0195a78740ddec10387cb5b50d2bd26c62138d1a666e01aa88073deb790100a4905d0172d926c9f09a0c3917da38ed6732701b537d6869571a6271384d5ee576f3adde5e0162cb8b06ca24c8e1cb800d70189edc1b3a4493fd1b5c222092b3c8237985bab05f01b3aecb8501f71db5b80516388cbb9b7c25d6bee5025935bc86ba87c33e74d7d360013780705ed0bdb6125803f76263d07cba8ab5ae7d89c43c2f103c08dd3fc5acdd6101a088c3f9bfa94ab9d68144f353d818cf43f6a320c6f131150fb9119f52d6e3c4620152c53500cef6e7091ceab4be2e0d8d5d27f91aa0af89b5f9452765c7cdafa892630122346b8fce7f67960987a8ac9782b0acf74ad3bc788963cf7e8036f7f1d45ff464012ed443d92137dbf76c2933f428eda316d3fb7269236095170bdc974ccdf3139165018656f29e4334f6ec6c97e7dfb79a3594b735a3c107ece144e1d10845081f949e6601365078c0d98ab66d9d67a03b471035de651964f69e82d22443f412be8cc9a4946701d160e62e3fc06b390a72fedca0a8125070c4f2d87b18f3ea78528545632ae23f68015e9beada3aeb3736b9e989ac6513774fc95052096b7018f0aaaa32ac6db931dd6901fb947940c908aab91fa52300bc2781e280fbb96b0e5b4671065a0aee29a64d366a0130d46daf1ac8a2f564b5eb349e9809b3359340980548430c2393ec79e213e0556b01de4e8f65e92411728952fe3e1e76560ba2ce6a3e8d3ff5fc966581c219504a166c01d2e57fa5122b5979afdc8f6038b1e1ed297af191c3e8c770e40b952e743815b36d01ebd42aeb5dca5f79d5bdff49c9ef613091e2ea0cc70686c2cca591083c62e3736e013164dcb330856354df898424167de03d48adfab4d091e1f422aa1b59ec35a0226f01d5b97d33f7cbd51c79ab2bd9017df6988b81876c422f3b5ffb738aa788e5ab7b7001b830dc608caf76a64fcb6135a330fd299bc0b6da911294ca4a5a10e23dda0bb1710170a86e6ae51ef103590f46d15d243529c051a4d1c20ecdd96348ecafe2ffb0e9720159a5ec2e8152288154921d3629b7dbf32b7f4400715ab1b4a5506e3dccdce0d77301bfbfaf506920474bc5e40ec1159e4ca1173e241754b763272be7ca1a4b3d43417401437ffcdeb0ae814e799601b3f480c7054f8ff2bb1b18d29964ab0d3a7fa23b9c75019076b5cd300233170f5ca611ab15f915439a2dfcc64f646579063dae4195681976014687eda7ef91c370896cf786ed08c8bc259614502ba16f805bf47cd5f8c77dbf77010e3d7feb6fe555c96af87ee56bceefbd93fbe45b45a72d3a19bd46377586db57780142c75128d9972a2997a7d5b1a0f409114b44d32e56acd67575507677c7cf51bf7901a76c7f555390ed69e9d43bcc6735751638013f9afb6e4d9b5c7489c9a897f6a47a011460785e8a580ddddb98ab51fcb2214695799f070ead91632b703117292bd05b7b01fe80a11453bad0ab0df3fe940291fcfa013daa0ea26ee15860abef86e21dc0db7c01f6254159a02dcd2b58580c9b86811d8bc96b34b9e8b4e709573d3bf6f9f52e4e7d01371c1fecc516807deee5f32413952eff28a173e984ec39e096ba429305fcfc727e0106c58949a17c540cfe8d08074c9e9128152e77bb77f5674d3891ce18acd995fd7f013547b4356763b0e5c51c1f6261bd96c0f26e3d2c4c3e2b0886d2cf51a47440b98001c511d2a33950363dbab47d95445b18b2a9d292f8152a98b543a2c685472fb9d08101dddc3952ecd39fabf6be2415aad8db2404cc148cd8a6065d22b3eee5ba0128cd8201bb656ce895352e2c86d1de9be1c080e1c62ecd5ce518f7d8dd6b2f05c78470a38301efc06421fc24c735582145f3d87bef0f6d5c7279768ffeedd3fbbc30c83cec6d8401ff2c9cd99949b92855400899c2c9dfb7ed8037ab87d51dd39b289484b91bef798501d1c63c34fe8d06b1b8d1584946c04ed5cbd7e9b768418fbacf5a84c99b406c1d86014d98bcb91e5431174fd01220243aeb79fc884c3cddd2742bf1125ba703e5d6788701f0dccec96a50dead9544d134acabef1034ded5d503cfaa487b47dd22517182ca880100a4f6f7ecfea63f71359da59e7b2175f7f54f6dd35e195f5ab8d5c9d80a833a8901ed02f15fb8099467680f22df559b654978b696e13702eba7ca85677746ead4a18a015e7c3f91a604f029816eb5da7adb86e2a7766e9dff900c82161a97977eda14d68b018dfb9b4456ae357d0d94dea024b445db497037b3c6a7750e936c6aeb9c0b00c68c017c69b11c8e7900acc68bcd687df19814e50409868f788390fa9ed6d9249472e78d01473706ca1469c9ea78b34d44cae3d6b8c925c8be16d99ec5e9d8ca32601b19e38e012a2e93f53054647a7a72e6c18745f6e53e8763893d693d0b0fc5d8035273cad88f01cef277f7868e7abc7870a851a23dd9f8ce6722e0559f44c197c4beea57bad5909001e2a9a1f8afea7ebe3fe0e4c86c7ada5258758ada3f71d2a65a55716dde6e834491011f48fd2da22294aa139fc65690b23d30d32d9055a92fb7478ac6974fad061fee9201f6617b10c39952ffbd3d44095ced8ec0d5a02563d1d0a0420996553d608f53da93011a58c35348f91b51be9c34b07ef5f928586043f4e14f59b9c562702c155e2f7d9401bdf13e22bfb2f1168b9c34c39d7d947f8a0282f6bc9f35838eaa2cfdbf35f056950175ff665c6c45ef489fcc901772fe401d8b05bf91d278d3f75d15fa70cffa6ecd960131028d5ffda81998f691855e40619698ed8e195783ee305c9f4745adab4da21a9701e45f39c4a641a4a035bb740ebfc8e98731c40713e7e4a0d3bbcde052e2b036e39801e3b72069f031a55981e7150ab4e4e2e0b8d43cf812887132130b2b5e728fc3ea99016bb8a4a775e1f29a90e4635aca6911b1986d8f32cdeef698c8532888cc89f2699a0152e9f8e98857cf0faaef2e9a1731eeadbeb74c7e6bc5f7420d23e515fae622d99b016d9c024bc6a9e11375145689f6e456f6b83e4f85132d335de5d4d4efe93e4d459c01fc0a299beaad431a2a55cf50b99f9a8c931fdb6847d32b9083c284665372aac29d0196e83ff50bc20961cd5250dc7bded818fd14f7acfb7a2143bc9953e29f79bffe9e012e0a9ca1b2de7ec417beda9d77e803dadd6c9d0ac1281302fd2492c5f768a1669f0174e4629f24dbe2d148806841dfcf55a6faf83ee6111472c325bd96364de6b03aa00165d67942200f7b1943e31fb8f6d99703816cfd80c1faa7541e27c3876a26d161a1011d312a495da8a220ba61d5e3bfbf24111a1a26844a437c4e2350807f1e4cee56a201abb58789be50453477c94786f9757da598f6a590f25edba0794d7a494143f93ca3019806e5ef3ac7b27d4e7dfa398c05a19d98a86ed560f07153d5eff8d6946b290ba4014465d604ec6366a680f6ec5db894ef6e21ececb38b8aceefd2158f9e20294dd7a501d5a3617e6ec209a8bdaee87133d859aae0b4ff8d87a0e44b79088dbc6f2ff5a8a60192f672655b9f7daddffb6235c12f1e81255f579680acfa8ecf8611d95a357696a701c2f158e670f85819e5b138836ef49bcce33da77c69a77b415ce5104916e301a1a80118fd5892723369a52de57c4dcbf652e73eb373d765a686eec7841ced3f41f553a901708cd5d1dfe72e4e0d0d2e55ee26868d750d83719e392daee0860a9a16acfe07aa01cefe750f5c883e729b907ecb2b73bd7a6f4f519e741bdd96d71bebf8dc267066ab016bc100c5f382b4afae6b1a2df69263bebee7f0215045d4000649a26e631d704eac014d647c3f54b240fbc20d12fbb0dce9c8c1ebcd13399416b0337a5ae202ae881bad01edadf4600f4255484ca53068c71139475741030488f0df4e5f97e48b907fda0dae01b062cc72e3d690f05d1599d9950dec250ba47f671db7f904c05df5b1abf709c6af0145fb5e9b6c8d60d5be97176f6ad08e3357eedf0aa5625b24a63934677b6d281cb00148c01dd44843fcb6fc195e72d636856a82e53aab79f8d812e551d46e1231b061b1010ffddbca8122ffe554183b4a79a1950abe1691db04c7edd4d25bed1d7421da9cb201072f39fa50ed835e9d5b14cd81028650904f893823e291bd8aa7fdf6fb92b971b3013848a05a749f0e2c0fff118eff41690573b2ffd4dc645f27925e846cc6e02b53b401a1c887f8256026d4e968e9fc716ab549641f54bf7333664153b92735808efabab5013a4a83a1fe6a058cfd1c792b94f821b0891274f2fab60011afdff411490cf391b6015077665c1f5c594db78dba10b30bfa9732fce64c17e544ede432680b88b9f29fb701d6979e8e8c6b52730afe1a3783fa8a3c2f8397db8406703735c8a4e70478096bb801ae17a8158c5c7c556ec5fa2a719a06b55d72b19e89decf9fead74ee0c3cce53fb90142ad18b0e81d94aa140f5d931fe8b9c0be79f9ec20911a23d3c0d45274260799ba01bd7270fbdb1e52cdeda01936f013ee8a52b2612f80bf24808e4b65d054f47310bb01156684fa58bd021acfa3f7f6eab924fd9d042d3f1ff5454d1c819b8fbf689a45bc01f8c29ca3ae113d92ec37f346188d41fad9569e810b237fbf82c6567477b6d7bebd01de3e46ae2d823cb9710911e4e3b1c1ddddd3424863a202bd8d05c49653bb7704be01d40bc44beb2f6de1484f3946ba23d979a5b3b756435ad0bd9a5a2aa951c3d4ccbf01b600a3f79705a547cef609fee2ea73c310726502511fc36757c7c6aee6e1a543c001ec91fe9b5cea293838dfa053c96b70d5135a2d305e759d3a4d76fdb836c957bec101a50f8f5c9c2d15548a2a9415fe5f499b88e4990df8dfbadbfc07414c4608a2d2c2014ed69239a67a7e82e7979493b503ebcc70fb29e74a80015d1a2afdcc5653b725c401ce9426e0d151e81f52764cf5fb5fb33d3b04065575dadbf11ec4869593b7d83bc501d949294187c7271aa350cf137d8440e9b9e95c4b7c1fa63a89ed75cbbb12b51fc60166e06dda53ad1fcc78f20a5b4e23d8d903bb875cd17a144028019ed8614269f4c701ba17c133fd922b8239b24b77a3dcec9c4e91c9625d65d6e5ca7977f04210c71fc801befeeae203434dc5902cea97775d50b6b68b005c5193dbf5d1db6e14820c2f47c9016fdf53a641491ad25891a4c7351cf4d158af2250b1d4384806ca190a6470b410ca019cfb3377ccbf5375a4842e695f7d765e37b8e7adcc96bc1ccafc1e93356d49eacb011c329cfcb79a398a586e47d6fe29f0ce4303ec2a8c1538f7de319255a11f0b87cc0122a19d3416c381497eb050de9b71350f716722c40d8d3e48af0da327fbe236d4cd010795a16533121f591fcc12294113f3caea233ab1e3df7e3bd9dcd7a9698c3f08ce01e83179457f469de43015fa782feebd52ff6e51842b4654a3d49528c1a2e33198cf016735d45f08b6837098251e0d41b929754cd1faefd9066bfe94c577d56f01ce35d001d8bcce0cddf81619e29bd228f25edf4170420234729274464df4e5f42f64cdded101ee1c482461270c87f1cd8a4a35ac17b5355239c2f4fed97b5a183bf7b707f3bfd20187203da88b66cd1abdc58ea5016359063d38727ac98a66f416bd8cf7c667ddf4d30149bcac32d3b14ac52128cb48a6c9c64f25335df1425caea342f41c379a29eaddd401a26674d1ec16844c16d5a8daa0043288bb33564a6f0724be7f8f621bc74d8973d5010c62389fc607f1df0bfced71ddb0c5371e6b6c84160d5023c0e6dcd85ad19445d601aa525ba86515f566d3f946d6c9cf2d860c74d43749be768093bd37dfabf958ded701ae2158b5e3d12d7ee26fd0d214884d66c714df8578045ccb97a2bd391e96c962d801748a28f46f90279e6c373f20fa09ebf46227ebf3a6ba3ef2f45b69ca5080edf9d901c9c274fe2e3177dd01240997b51281afe02de65defd2aff234a7958bb5ae1b8eda01cac98f704702f1444b933ad1a7fa785d738197aa13d416a0e805e87c72b902d3db01c7a6a268bb20bbe89c816619a942c2380c65e19cb71c040a4f11e4010abf87e5dc013c748e51a6d5022e5f53c70133458f57d1cbeb71bfb6c71674db37972962c0bcdd01a61917994cf79aded58053e685d856c869c7c2d23515ad6c027d99fb15a66596de01dbd21d17f2a51d032c6f579995ff38ff709e8dbfe5ba544df104af63affcc084df010f42581ee80517075c68243af7d68429f9f0946d3ebb85d44322e727860d6903e001156a84271f72e49a1a5b15e37dfbeb87aa1cca880ee4425fc9644ab08bb67a52e101caec38938a1db7e0a5c4eb2f417085ff86045b4ffc92f7e20980927e4e586620e20109e07f28692e521671a8be1207faf5bbceb8fa2d3a1f88bfbd0b936465f2479ae301c0b84d2cd1d72a64e83669915975ab72615bcb6be7ba6d97fd73c7e4e4635fb0e401a8f3f5490ddf4eb4a08f340520214dc7e5f2621c6306934d3e1786864d69b28ee5016257d40629e620dda5f78da1e0f951dcd8110d9136622a9f1599d7bce4a352c6e601b6e884f1a833a42fcc9b3f20ebfe128d59c69165070b46769bf4a1b95ebb8585e701b7b41271403cf67c295ee2167adb5ffcc87142a1856a307c1e86024172469fbbe8011a584a6aa5d6681012547d69503577c1b0fae3a0b781d4efd4d76ae0c078ac58e901ce834700654396fcd84f05d6f0486a2623e23f65b0292d0733afdc5d326ef7e9ea016042eeafd137ac49389d28014aa8d4fcbf2e19d8a11945c52a4d5919fd46f726eb01b2dff85792508d4523491483cded2bc3fa5f942f8d7e88fb2a057d37bdb6ff04ec012ae0cb0563ca3254c2fe584ba6e8884a11bcaf20be3c600be6b2b5919d67c717ed01ca71408e040473d41453ec69608a710087021d5a00a336f8da921531b2df6b29ee0184a42b3a20b8c4a260d15c518fd837d001246641cabe0c812e258d5d4b9f9919ef01ffb1713078ba9f02319095c82fbf822662648be92308df84c29308736fa0fcf2f001e7122af7861ba8918d992df0d1617ae2cc0f9c4a04495644398f1fa4e7333967f101aa1eb54ac8c9a73e08492d83beef0c99b159984ec90680f9c7c7178a9bf2d466f201f079ae857eaaf48a749cfc979e96d8b190e273e2ada72d432d1a44be737afc8df301aa5615798e4fdc7b51a71a3fdf943bee028ba841d3c6dfe30f68e743b4723dcaf4016b10c818f43ae6d802fe834e9c617e647b2a35b09ae3fcb31217787cffb2ed4bf5010e5cf352f11022271b73c6458f1af063d184bfb40750e56c5fd51a6049532b50f601c063db86180d67e0a2a8e1a6eb30227d7d1b84220b7d1e233c5f367efba93982f7010ef5b676e9bca5289acf2e54e62692d8aa560c937bcb289df6bfe1c11e27cd2ff801d1ca22047cb9d0c22c2472df01184ae5dcc5de653dbc325040cce42713e31105f9019a2453b0cd41fec023b67777a26e0820fabec8dc518d66c05afc024068bc1c69fa011a323222ae89fb70082ad0eeb610161e92bebc34b01abdc1918faab655a2e72afb01914b0f02182a38fc89b285ee8d61f45653a2322dab4061fdb7738f31a4a84f5dfc01c85ccf82c343967fb75c01a3b75e2756c4b430cf4b12cd17d6b2b1b2b75bcba4fd01b796b815e24eaf6408aa490a83e0942d5cb7f53450417bc0375d5369ba367f7dfe013da1cae7a12a5ce543aa8bf26843bf38df2c97e1ecb410942fdbf9d10f78f6cbff01465394e2106231b2123475b3da6edd462989b4206c6c2bfe0bbc544a35b6bc6300000d000000010118c62a615093eee0fb2234b80139e0b12a7ee2f331712753fd8650bc07ddbed502012f9fcccd660507c8026f553dc6d8b1d13444bcf38747620668bc811a598377720301c6daad709a469a1200d04bc70eaaf5d3738e4fd5e8265bf9b7047cfd0d3a3ed604010abb87f65c89d909809e2a6a47f80bcb5b4a6efdfe9dbb108d149ac3d251f44c0601edb7d3ec87cfcb5aa9986faf80bfd3874782502bf7db8f3e3d02a4f7d05702a807018f3d829e0e07adc729ef7c98d4dfed18fe9309a4b6f37c623455b69776d96ef50801732d8be21ba8d9d166007384999a8f4887b01534151e5d22a1f388be99412fc40901b54fb720e8ad389665fab394f230a8b6b3c947837068cc7f87659792a61df5b30a00eb428202904e34f095bea9fae95b9ff4589b60f74f509dbb543580f4bebbbe1c0b008867cb85ded7b81ce9a27aa034eb8a2733240d6890c4c7553819dd060b5ec74d0c01f8bd192bf45d9f37b634812c80d511aba02720799cc735d186a7e4e68089e6470d00701de233c796cf12a5329e6b3fcfb0b89f8ce360633cc28602cbcc74ca9b3fda0e0043d39741a6e7b395eace3b1fbbbd5bbf288f7a788e80745a2e2bb0f2173a8b4f \ No newline at end of file diff --git a/tests/integration.rs b/tests/integration.rs index 192cabc..77afb95 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -5,11 +5,13 @@ use casper_litmus::{ block_header::BlockHeader, json_compatibility::{JsonBlock, JsonBlockHeader}, kernel::LightClientKernel, + merkle_proof::{process_query_proofs, TrieMerkleProof}, }; use casper_types::{ bytesrepr::{deserialize_from_slice, ToBytes}, EraId, }; + use once_cell::sync::Lazy; static BLOCKS_MAP: Lazy> = Lazy::new(|| { @@ -128,3 +130,46 @@ fn update_kernel_history() { assert_eq!(kernel.latest_block_hash(), json_block.hash()); } } + +#[test] +fn query_proofs() { + let proofs_hex = include_str!("assets/query_merkle_proofs.txt"); + let proofs_bytes = hex::decode(proofs_hex).expect("should decode with hex"); + let proofs: Vec = casper_types::bytesrepr::deserialize(proofs_bytes) + .expect("should deserialize with bytesrepr"); + let query_info = process_query_proofs(&proofs, &[]).unwrap(); + assert_eq!( + "9253bf8484bae2b6e4d5302c792c6a79f729b2cc2a9d87beb262d3266a424efa", + query_info.state_root().to_hex(), + "hex of state root not as expected" + ); + if let casper_types::StoredValue::Account(account) = query_info.stored_value() { + assert_eq!( + "account-hash-c39d7a6202e5558ffbf327985c55a95f606db48115599a216987b73daf409076", + serde_json::to_value(&account.account_hash()) + .expect("should convert to serde_json::Value") + .as_str() + .expect("should be a string"), + "account hash not as expected" + ); + } else { + panic!( + "StoredValue variant not as expected (should be Account): {:?}", + query_info.stored_value() + ); + } + if let casper_types::Key::Account(account_hash) = query_info.key() { + assert_eq!( + "account-hash-c39d7a6202e5558ffbf327985c55a95f606db48115599a216987b73daf409076", + serde_json::to_value(account_hash) + .expect("should convert to serde_json::Value") + .as_str() + .expect("should be a string") + ); + } else { + panic!( + "Key variant not as expected (should be Account): {:?}", + query_info.key() + ); + } +}