-
Notifications
You must be signed in to change notification settings - Fork 6
implement inclusion proof verif #63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
KyrylR
merged 2 commits into
BlockstreamResearch:main
from
topologoanatom:tx-incl-verif
Jan 29, 2026
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| 000000207e3dba98460e4136659f0fccf3e59338dfe53ed5f094fb0bb94d771c48341854d875900105c87e5dd46c740cb1129c06f8f4007e868f61b25e37cffa946c718d8742805b01000000015100030200000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03510101ffffffff0201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000000009b64001976a914608c0ea8194a8ceb57f0196f44a6b48a54fc065988ac01230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b201000000000000000000266a24aa21a9ed8f8a98e5623643b24167266c2648ead4a50d18b0491c6f34e11398aaee0ca6e8000000000000012000000000000000000000000000000000000000000000000000000000000000000000000000020000000001eb04b68e9a26d116046c76e8ff47332fb71dda90ff4bef5370f25226d3bc09fc0000000000feffffff0201230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b20100000002540bd71c001976a91448633e2c0ee9495dd3f9c43732c47f4702a362c888ac01230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000000000ce4000000000000020000000101f23ceddac67cfbbc997199daa651384d0746fb2a5482b8c8629ba8df4b788f75000000006b483045022100e0feb3e2f292000d67e24b821d87c9532230dac1de428d6a0068c9f416583abf02200e76f072788dd411b2327267cd91c6b1659809598cd4fae35be475efe1e4bbad01210201e15c23c021652d07c1557b607ea0379fca0462aca840d6c33c4d4927524547feffffff030b60424a423335923c15ae387d95d4f80d944722020bfa55b9f0a0e67579e3c13c081c4f215239c77456d121eb73bd9914a9a6398fe369b4eb8f88a5f78e257fcaa303301ee46349950886ae115c9556607fcda9381c2f72368f4b5286488c62aa0b081976a9148bb6c4d5814d43fefb9e330575e326632136389c88ac0bd436b0539f5497af792d7cb281f09b73d8a5abc198b3ce6239d79e68893e5e5d0923899fd35071ba8a209d85b556d5747b6c35539c3b2f8631a27c0d477a1f45a603d1d350b8cbf900f7666da66541bf6252fc4c162141ad49c670884c93c57db6ba1976a9148c7ab6e0fca387d03643d4846f708bf39d47c1e988ac01230f4f5d4b7c6fa845806ee4f67713459e1b69e8e60fcee2e4940c7a0d5de1b2010000000000008e800000000000000000000043010001dc65ae13f76fde4a7172e0fb380b1a5cc8dc88eaa0659e638a25eac8ae30d79bf93eb7e487eeee323e4ac8e3a2fe6523bdeba6acce32b9b085f2286174c04655fd6c0a6020000000000000000178ad016b3e5d8165423e56d8b37e3eaee96009b2f970043ccf65d61b5c3c1e1ef343e0c479bdba442717dc861c9591566010240b9d4607efb9252a5fcef05edf640e0bb6b606729246ad07baa49d0d3b52042c65a03ca737744e45b2d2d6d177c36569ae9d6eb4437305b169bbc59f85cabff3bc49a2d6d08c177cce3121a509d3c47961bd22e35c932b79d4ec5ccaf913fac04034bfebdadbc4ff3127af96344b02ee6b967bb08326cbe6a4e1c924485e64a8c0fdf70b98c99f38acaa15aa0adb2b5b7335ed5502443891bcd657310347cbd928f40f38f1dec087a2b947c9cf7d304798f77bbc4a2c843796b2d49acce91de4e88a0a9c261277df28ffc3320d7f7d64790f592ddded48a1068ef88271395fa5606389ef90856ddd6bd6710a8d27e0147983b5dde2a7efae44e83ad02a3c3da04be43d5f2c05c205f1e17b48554c2177670f46dbb6600bd2e6c75dd5ea2e1072c5f22483dcf05d8124e3f9063a5ddb179a29c23a2d15d6e89f2192f03dae5938f66fcdcff000c5a96ffd2920f23881880af72153c96a56dd80c218bb48b44a18e54a8050ff32c869c1264ee574cdb4002f86e0779c724d11dc4a768dbec1bd22054886f1fdf2e7347e4c247b829159d1375f881c6ce0a5c4da8534000e7fec3a980afb1edc99b725c29de80f260dcf144c873bf589ae1812ef6cb05f2234f9c66c23e874a0d5d0dc52f2209e015bbcf74ee449a397f6b0318c915b7e58dea5904abbe35285e90ccf548ad1f3f52f60c3b19b3cd67644d633e68aef42d8ef1782f22a8edd0620f55f29070720ca7a078ac83e87b9ebd2783ecad17dd854ef1bbd319f1a6d3a1e4931f9097422f5a3c4af037b99e06c7610ee61102c6eea763af108e9a16b93b2dc0891658d5c6a197df6aae9b306b2c895d21c79cb6cb6dd85b4018b0a9fe7468336e3907eb4adcaf930cacc97e8e951d2d6b25744a4143679bad1f31b210c9a2ed54b80d8f5d7dc1f1c985681534c1926920cd683d95dca7e8ea285f9906d2e89cd8bfa76a98e38ee4b5152522d55f79610fe8d5278fe6ed5866b5da4dcf330ea84307c34f30e1a66eb1934dafebb0074fc27c2ff73d8c0bae8416cc87bf611f81119aba9e2a911beaf3ac9507e621fc1ed1cf15dfb31408cf55e2bfdd2880db2d3489a336d6f8348347648d882f9f376331e469e809115c6cc82468f363c910673e9ded172ded90a369e1cdd135676f623e11a1531ed221177812b1ef0c65e5ca92c0df8de7fe664710f3228a226e019c99607fe1395ecd5643e1c7ad8a132bf5131737cb970a7f0dabc00029755bf71b3f47bd69ba39b3ab104c74f04239f4919dca1dfce7c9c41cba9d449073e106ebabe3c313b598ee8b11702ec46e9ee53fb9422f0326371898b8fa4c21a951684c687398e0bebd6f6fd91b829e8666b9a19a4273cfda0f34b8ecb902f7adc6539fb9a0cba6f87a63a957acfb2dfa18973f4a3063668767b2be780311513c63f1814f082176f6a953f2ffaa49ec9b39fecc2eab603be7a969bb4c1dbebf8d39fa90f802d5ea52378b5025a19b64a8c2c2dd6a6133bd8d29730bd5724b5bf50c158b238d1137082937ad91a176aaf91577868db7581b457c917e612b242ce0065ad47e11dcdc1fc6158687142249bcf312497a547b6f43e795af7d4ae8cd022e44e417987e35e83de21e39dcdf86b97bd421e6e61881a432fa2284f20be80e32459443736b875d9036468ceb881589394441e2d10aa10b6c93332951e8ba56f89fac70baf415b4511873c0f3e418ca4fe8954a28f1f7b5f590d34470119f694e2712f184882d90396c8e6aa850eaa3c2ae51990543638c46c59512167a2c5ad593532dc2142ffb6560476e4159213b9ef017ec75310d2e4624a405bb26f7192a485a94890674928c9caa4a5819ca4ddcba8fa71afc1a6baf63f039452c8fe994f8b63d58c876dfddd61a476345eaed4f66bdc0fcfc38d485c6a5b0e27d0fbc50427ff591ba38d63445c01642cfbd7d4c032f2546a6fe80bc3b598362502c552049523fe360c3bcf1cc572feb04386f97d55871dd8cea0393cdd964e724082adc98126e6f2fe1d576be4bf911e9aca70e35538175f8382bbcd614bbecc97c9607ef25da2ff08a6e5b6f76cbe9ccb0e0fdc3528e3e2c3675a5c897d295bb76524ec8a73a70b97909368f44d92f9aceaef0b03f3dafa1faa89fc663a92da3c19b4952463fac0e825e78cf046e266cfb9975af72e9d50d2c2cafee88fe2cecae2b1465fc07b280d83b66062dc9e7a372f81aec8e0bb9e97877814a5a6813c67746e35cd068d45d8664528bd00d5a306a5319e1bea7f38345da92d3a10d91476a26aed6b8441f0f72fbbad5d5e0f8ae5cabc9f4f08e6be7902b5c53632db5264afee7422c87b3237a32d5213ad0eb807b61977d9d90666cbb0c70500526b0eb762c99351796db41166b0aa2f221b5607e0d629fac4e938488245c11557381a4f8addcc49913b11d42481cf8668e37bacbad4a20509e4fe4ccbcee7aea2909a2abe59052f7f28b9340cd92f69729d615b8d3b530941c0b30506498cd4e561a9c82d915266bb7115967bc76c5593c06d094bdf4294b868afc5fa52742d3bdbd5932df599f0e1187c49f0dba8679c771a514cc9da75e03506957800bf470d4a07c4bb8918d6085499bb8ceeaba23c0b465863327e9ab8b6b8cf8b3ca530ca7b02cfadf85437b750f305e8fbc8855c95bee8595a7e9e1f0993a03adbadc68665a18936cc99b6530b4518c0754990d7bfdfdac76f88cfcbcb7b3d9a71ee10cbd3a1bdbc2e50b642c1fef56511962f845bbec6eab727b1d4add335db8d80c4c07e8356ad05adad68b012489fa5bb5d9019a667778ddf7f5edd80f1d3c4abd64397a89e554c8007809336ddc2b2e7d5219c39fdf39aad33b9350f6b18fe3b98c690b9068f36d4b7669530fd216373842fbf70fe9bbe80854b31eed4bd515d6caeb065d6c609846c9bfae1b3fce3db70b5bfb448ec69512e7f25019c789301b77a75f2a0f81c65ec29f41bf96d597a00c310e8ba4b48ac82b5a735c1e83f22394eb2fc9b35d42a35533c938f26290a5860175637982f1733c99be39c44ac4a09187406306bde2fd3d28e4e7bda73719912c338804dea03987757dac4d73def665e11da126f9414f71624a3b753797eb0472bd334094515c4f9fe57fdd8d185f22b4bf82e4b5f6b800870cce19a0c8174dc11ee9f1cb9ffe0ac6f6fff1ebf7c915c7ae20172bb70390e3759912e0e0a4e83a0a2d2318f4386314a89f6438ccb331f89377ff7947fe4b24f788aef85c1656ca87ee41c959f1b09bde09f20c2a51ac481646b28e9b0fc2ff49cfe8cf28577bf5bf6f261f54f97fcd2875da4210c6dfe685450280b68e378d9a486243cc682ed4ec747c37de1fde848e4a8f70498d22e40c462c469c884cd67330e77b694e759232313f31a1624e0e1960f23ddae47b68ff553d0de0910c8abe2e8e5fb063aa744ff77465fc731c7af79a84dcaa9b3f741a46dd3c932877d49242c6d883e14392b8c4530986605812b636a73590ef437f27e40d1af37ed1cbd68fb4e9ca5b0b41e5daee0142c1bf59c9d71f6c19b25e6148dfbb9fb142107aabe3701e36611a7e0b13ea32d3c5f8a51f63c5f34415baa15f6ca77300eb323241ffe73c5acd97fcb682c21dc8911392979e9cb81be5218acf452b5b93f6681d323b7989fdd10efe6fe9e2ac88d0d76a4cf3ee45e3b5c430100014142c1fc7e8a658eff437594a25cf34d269556d8511918f27fdc7e9d6dd73f0e4790b91f225e9d131e6abb3dbfb66549a9aa57948fbd2f183fcd951b1d2305bffd6c0a602000000000000000016f5cdf9fb6c1b5e98a36befdc2c55bd4fd8793d554b2506f51c909362495e1216ee83cd270ddb0a00785600ba23bd3363f0798e3a7a117990415adec88e61be65170bd587ab4d2ee38edb22a91e5c29afa397dd5a73465c51c6263f5fbde47fa801ce84464acc32589acaafadfe44d6558774b7085612a88f3424b6dca3c6f07217d1cbd5c41bda46a6a492a0119c1de4d25b58c94250bee3fba6b8223777535673a2f4da6af27598030f88144f408120f07ca9c98d5d9edcdf6cdc9073f118fce55e6c9d0be80b5e87992ddaa9c22053b3a00d42bdedc9768de25c0b37a5c4fb4e86710b33cebed5588d88adde607f6bca14f0279ce35126d403ffa50f288c87f528c19749ed43bd846c513fcd92c173fe76d8f2e69770439d3d075cb19b1094a42ee07ae1de197e8c136e2bc688a75a74db24adb0fbb73872dc80074f61c9cce9bd33861bdd921ee3edacab1d6e7cec325c172b6b6e82ada11687e4fc931225074dd1f20a0f9342dbce1fc3fdbf5bb6cb74ab6475e574e9f5f247a2f7e4fcfcc354d4da8c8066e574642c7fccbbb9ef0aa592ecab5366fe87eb8e14cd64aee34578aa48f68f8f4c5372df2c3fc429f5a3e39ef6c034c87f9c52b2ea35e28c7bf3be737c3817efd6569466dc859e8ff8965c5249b6f045934d3d08b0ffd388aec58df8194ac2c4fec2152942d2626595e65664b1fa33b5dae8ee796a840a56d885cbf7ae6483fad05e507ada3f075ebce0d791b626c6dfe93f8492c4dd3b34aafc33d7644c5c8e38bfd8c19194f65be88fcb4538778632e489a626896372fdd2498b16e64daa7d3c5cfac688d6f9cdf3717261b0a1f25be1bdd6be6558ddb826fa04b5f668810a291aea51a6f05ff7c34dcf81c74849a8015bad5e4e416989b10ef01de304775db725fa0b665f4330dc9c540dc29aab144837362a97d6bb0165cb3272338c2d32386cd95ee3e66d876b591a25a6907237523cf908f736d2fdc8e54ea8d9c7562697161d1f72fc4d7b775052415cd0e5ae5bdf6edfab5776b6ff75ce5e1f8f2beea6ec74252b63966cca58abd638279dc5c998a1068079f3e5dcc8a69165c304c3d8c362ccfadab05ad12208a5655ab389eb727e8ed5f86b300331a13be26e2fbabf89fbfd2b98481dd5edb52ed456a0e03a84b6f89761f91ff251412f5cfa286e35fb9f48ef0e044c4742b6e860a08767ecb80548c2f3df3b371cdb40e86dbe118f64e84faf45ecb78d73364e9e31e3412ca2a3fad0a35983370ea9e6264a222edd1fd4aca30e3c169d7ca2d07609262e786ecd019c1417a06b7dfa32a54e0897afdc6492f26611555cbff47dba3b76381f239d597a8f687669333e0b47b53d5bcc4fea1919490bad3c6f0b6a58a50aca7ddeb9745ead454e0a38d9486fb52aefe0dbb92bf7fd6c215078aba3482b11274ec8cddff92c359bbc6d20bd823ad0bbf859cfaadf8e775b3d37b3078319f46c6d2a112cf60a673fee467538c70f1687d97fbe9d9f8a0856061592a4e00b6d10e979e674dd2cd0ba8b853f733877cd508062d5f723d58d215ad69c2be6be742496aef54eb87338622eb36a9bbc5a7a602d280a45e095b1e078dab54479e783a513c722066acaae44ccc15f9560da91ed053ec05c36d82f6809766876c45c4fbeb2321d50f48f7995437d0c5fc365974a571fb0352d28cb1cdbd21d69fab576a2e68d6b881776027bcdb7f01be22b1c847d91f26e680ef6ab2c128a89b59432383d9bd661b0b01432cf8a25319426d38ac2e2114825f59b4250569c798b1094920bb31130728313ff56a6eef2e6c4b275215dce3786d0f9024952b5f572566c53597e7ef4ab1f75743e605a564054d667f48906b5481d924769ef65751e349891d725a2c1bf8b102fea4c25c874d2fc2ce1bfec4b39bea76fbf7a28855725d52b595a4fc96892c3f1f961d46310ebd5221df729c02060035c559baf0fd7efa73a2213ca29642857aeb8ebf7efdf9d2f5c84746b6fc35ab355a8dca56e7dde4831e47ca1be6b62af30cfcf807c384e56ab84ff03bbe786251e6c4b932c9217bf671046217bd0511fdc06aa69050c1480281e4843eb73d80095a2fb8e68a2c0c98c9aea637b99d87ad847a3a76d59ea308c751f9cb4a4fce2989822bd6ba2f901f09df647536dc30730ea3160dd35b8c6dcc9aa815b79ed492a8a299a298ccdf784b9b0211ca877ec1723817c98529acaa4d3727162b5740b0fc9b498dfb2212a3cbf0c63dc4f7663fafad7905643a792862b651e8497b0f0da632b897ecf9ee63f2b20b54fa5eb2f2e424dcce5a075f50b856af266655be3a815fc83ed8027508b2536976982196b160e2219ffdb5c7a56dd3e6b700860c711f4439dbf72973f4f26fe3260ec43a3446fe14444b9787d877e107be610147eec4a3574745e95a1f424aff062f84c559d13b1e6b59e8dc2221515c229f07db8eb39c515a321d8bd07b1bd6c9a79dac6d951c04415553c7a2ce1eb77495c7f89c4d5b4cffd289435b69bc53585095083cc5a1b191781342266e204e1566aca8175e2ae84a8bd711d188b666dfb65a6442776d3e23c1b5192af09ec712537f2157d0ccbc1bb3b3a1969d9705671f16bdc266e615ad2e50a8cbd666f3ee7465cc430c6cd69d30c91e717b12f7094b6f0ef89134d6c1620d28d8f238c181146448b348e4ca2e93c737210350f18fb878fb91b70ecc5689e5b6101ecfc545f6a1c903115b0c6419c91a50fb2dbe2edd362f2815f0c75070974507c34130ac9b29747ff7efbe6e37ee4c62be3ecfedfa817fdf3309163aaff677775b77f0d288c9858cfe59cb0fa18afa591e7d574eaef43c82e79d71542c4177de4e5bd724b18cfd33c68530665728a9d5ef192772094acbf3d885d5146c1634e74754e3fbcb94fa349eac8280cfd7d1f46a0813b57a83bd078b1f7cb5a60a59b59380fe04e1c600c33b33d1add69a9ff1be546f0ec5c0083979fce940b23711f382ac0d011c1103f02cb6082c18e39cf7a9c3bf4c081f905ae7b87951a7880b57e934465ccd634e5a17fd8d8866abfdfebd33b2c3d2c5be58144900c04e9c18de0c80270660e62a3c185277555f89da4c41bd33cec1359f4ed21abdb586e1d97f720a92d16014d7f1822f1836f74c97cb7f7b38e073477c6ab064fde835916c1e624de81f2ad90f6260073c5e1848582860f033630bde225821b39c2572b30c36adf8fdb8317c33df05f6413447f4985d12e9012629df09dc8f43373a6d0db4b0048453a6f1ec662472c77a30d5cf4ac7084f736d0d598c251f2aefc986052fbf12a657885d7140ad36b07c63ab86388a2be12d943747f3f29ef9f2e11e1444cc873df0ed7826eef675389a0d5a0388a8504fe89c4791ea4a572bfd406d5f01418b4f888c9a7a566e32811936bf6950bbf786b86c41c28f2045d31953fcd15f179e7bc00c72870890537921f7deff82270b0e44b88720aa738f60a85567deb7c90b0c2444467621e53e1c079436d31d3d0b34dd237fc281eb9d87175237a9a433142db4bb7f8c4cb6a34e2dc73f074045d216695ce88ef68e18564c935c9cbd902e939655c258de2ab78def8746bffd972083afce3b6881b7147262e1a44e0224689fafa1a3cb823c8da6eb7df091bec0638bf728b7b10aa95f2bce512ec8d3252938d2eb77b44ace7a2f976588032cac5af670f9e5ca25cb0721bc1baec26f9c3a9f41b02fb62997d6cb0a01314845e9d0e78139ea49f2ead8736e0000 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,180 @@ | ||
| //! Transaction inclusion verification using Merkle proofs for Liquid/Elements blocks. | ||
| //! | ||
| //! This module provides SPV (Simplified Payment Verification) functionality to prove | ||
| //! a transaction exists in a block without downloading all transactions. | ||
|
|
||
| use simplicityhl::elements::hashes::{Hash, HashEngine}; | ||
| use simplicityhl::elements::{Block, TxMerkleNode, Txid}; | ||
|
|
||
| /// Merkle proof: (`transaction_index`, `sibling_hashes`) | ||
| pub type MerkleProof = (usize, Vec<TxMerkleNode>); | ||
|
|
||
| /// Constructs a Merkle inclusion proof (Merkle branch) for a transaction TXID | ||
| /// in a block, using Bitcoin consensus Merkle tree construction rules | ||
| /// (pairwise double-SHA256 hashing with odd-hash duplication). | ||
| /// | ||
| /// Liquid inherits the same Merkle tree semantics via the Elements codebase: | ||
| /// <https://developer.bitcoin.org/reference/block_chain.html> | ||
| /// | ||
| /// Returns `None` if the transaction is not present in the block. | ||
| #[must_use] | ||
| pub fn merkle_branch(tx: &Txid, block: &Block) -> Option<MerkleProof> { | ||
| if block.txdata.is_empty() { | ||
| return None; | ||
| } | ||
|
|
||
| let tx_index = block.txdata.iter().position(|t| &t.txid() == tx)?; | ||
|
|
||
| Some((tx_index, build_merkle_branch(tx_index, block))) | ||
| } | ||
|
|
||
| /// Verifies a Merkle inclusion proof (Merkle branch) for a transaction TXID | ||
| /// against the given Merkle root using Bitcoin consensus Merkle tree rules | ||
| /// (pairwise double-SHA256 hashing with left/right ordering). | ||
| /// | ||
| /// Liquid inherits the same Merkle tree semantics via the Elements codebase: | ||
| /// <https://developer.bitcoin.org/reference/block_chain.html> | ||
| /// | ||
| /// Returns `true` if the proof commits the transaction to the given root. | ||
| #[must_use] | ||
| pub fn verify_tx(tx: &Txid, root: &TxMerkleNode, proof: &MerkleProof) -> bool { | ||
| root.eq(&compute_merkle_root_from_branch(tx, proof.0, &proof.1)) | ||
| } | ||
|
|
||
| fn build_merkle_branch(tx_index: usize, block: &Block) -> Vec<TxMerkleNode> { | ||
| if block.txdata.is_empty() || block.txdata.len() == 1 { | ||
| return vec![]; | ||
| } | ||
|
|
||
| let mut branch = vec![]; | ||
| let mut layer = block | ||
| .txdata | ||
| .iter() | ||
| .map(|tx| TxMerkleNode::from_raw_hash(*tx.txid().as_raw_hash())) | ||
| .collect::<Vec<_>>(); | ||
| let mut index = tx_index; | ||
|
|
||
| // Bottom-up traversal: pair nodes, hash parents, collect siblings along path to root | ||
| while layer.len() > 1 { | ||
| let mut next_layer = vec![]; | ||
|
|
||
| for i in (0..layer.len()).step_by(2) { | ||
| let left = layer[i]; | ||
| let right = if i + 1 < layer.len() { | ||
| layer[i + 1] | ||
| } else { | ||
| layer[i] | ||
| }; | ||
|
|
||
| let mut eng = TxMerkleNode::engine(); | ||
| eng.input(left.as_raw_hash().as_byte_array()); | ||
| eng.input(right.as_raw_hash().as_byte_array()); | ||
|
|
||
| next_layer.push(TxMerkleNode::from_engine(eng)); | ||
|
|
||
| if index / 2 == i / 2 { | ||
| let sibling = if index.is_multiple_of(2) { right } else { left }; | ||
| branch.push(sibling); | ||
| } | ||
| } | ||
|
|
||
| index /= 2; | ||
| layer = next_layer; | ||
| } | ||
|
|
||
| branch | ||
| } | ||
|
|
||
| fn compute_merkle_root_from_branch( | ||
| tx: &Txid, | ||
| tx_index: usize, | ||
| branch: &[TxMerkleNode], | ||
| ) -> TxMerkleNode { | ||
| let mut res = TxMerkleNode::from_raw_hash(*tx.as_raw_hash()); | ||
| let mut pos = tx_index; | ||
|
|
||
| for leaf in branch { | ||
| let mut eng = TxMerkleNode::engine(); | ||
|
|
||
| res = if pos & 1 == 0 { | ||
| eng.input(res.as_raw_hash().as_byte_array()); | ||
| eng.input(leaf.as_raw_hash().as_byte_array()); | ||
| TxMerkleNode::from_engine(eng) | ||
| } else { | ||
| eng.input(leaf.as_raw_hash().as_byte_array()); | ||
| eng.input(res.as_raw_hash().as_byte_array()); | ||
| TxMerkleNode::from_engine(eng) | ||
| }; | ||
|
|
||
| pos >>= 1; | ||
| } | ||
|
|
||
| res | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod test { | ||
|
|
||
| use super::*; | ||
|
|
||
| /// Taken from rust-elements | ||
| /// <https://github.com/ElementsProject/rust-elements/blob/master/src/internal_macros.rs> | ||
| macro_rules! hex_deserialize( | ||
| ($e:expr) => ({ | ||
| use simplicityhl::elements::encode::deserialize; | ||
|
|
||
| fn hex_char(c: char) -> u8 { | ||
| match c { | ||
| '0' => 0, | ||
| '1' => 1, | ||
| '2' => 2, | ||
| '3' => 3, | ||
| '4' => 4, | ||
| '5' => 5, | ||
| '6' => 6, | ||
| '7' => 7, | ||
| '8' => 8, | ||
| '9' => 9, | ||
| 'a' | 'A' => 10, | ||
| 'b' | 'B' => 11, | ||
| 'c' | 'C' => 12, | ||
| 'd' | 'D' => 13, | ||
| 'e' | 'E' => 14, | ||
| 'f' | 'F' => 15, | ||
| x => panic!("Invalid character {} in hex string", x), | ||
| } | ||
| } | ||
|
|
||
| let mut ret = Vec::with_capacity($e.len() / 2); | ||
| let mut byte = 0; | ||
| for (ch, store) in $e.chars().zip([false, true].iter().cycle()) { | ||
| byte = (byte << 4) + hex_char(ch); | ||
| if *store { | ||
| ret.push(byte); | ||
| byte = 0; | ||
| } | ||
| } | ||
| deserialize(&ret).expect("deserialize object") | ||
| }); | ||
| ); | ||
|
|
||
| // Unfortunately, `hex_deserialize` macro aforehead returns error trying deserialize | ||
| // blocks from elements-cli regtest, so this block, taken from `elements::Block::block`, is | ||
| // the only test case I have found so far. | ||
| const BLOCK_STR: &str = include_str!("./assets/test-tx-incl-block.hex"); | ||
|
|
||
| #[test] | ||
| fn test_merkle_branch_construction() { | ||
| let block: Block = hex_deserialize!(BLOCK_STR); | ||
|
|
||
| assert_eq!(block.txdata.len(), 3); | ||
|
|
||
| let tx = block.txdata[1].txid(); | ||
| let proof = merkle_branch(&tx, &block).expect("Failed to find tx in block"); | ||
|
|
||
| assert!( | ||
| verify_tx(&tx, &block.header.merkle_root, &proof), | ||
| "Invalid merkle proof" | ||
| ); | ||
| } | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to file an issue, so they expose this method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that makes sense, since it is only used for tests and would not fit the original intent (all macros in that file are private).
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then, how the end user would use the
merkle_branchfunction?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From where would he get a block (what is the deserialization flow?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is an
elements::encode::deserializefunction, which is already used by the macro itself. The macro does not fit any use case other than deserializing hard-coded hex values.