From dec49333be21b4365688e0d57400f1938961b952 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 15 Apr 2019 10:01:39 +0300 Subject: [PATCH 01/12] fixed fee calculation in BlockCoinbaseMinerReward --- test-data/src/lib.rs | 27 +++++++++++- verification/src/accept_block.rs | 76 +++++++++++++++++++++++++++++--- verification/src/error.rs | 2 - 3 files changed, 96 insertions(+), 9 deletions(-) diff --git a/test-data/src/lib.rs b/test-data/src/lib.rs index 51916d2b..3b4be70d 100644 --- a/test-data/src/lib.rs +++ b/test-data/src/lib.rs @@ -8,7 +8,7 @@ extern crate primitives; extern crate serialization as ser; extern crate script; -use chain::Block; +use chain::{Block, Transaction}; pub mod chain_builder; pub mod block; @@ -104,3 +104,28 @@ pub fn block_h522() -> Block { pub fn block_h567() -> Block { "04000000cebd1746de07a99a67e9204174e31e0121d248a0e3fa3b8dbce824921f000000dd377ccebdb0c61b13f52cb5c8df1a38caf3d2eedfbab12a981ca505f114a284000000000000000000000000000000000000000000000000000000000000000031c313584323271db505000000000000000000000000000000000000000000000000000000000000fd4005007fa21c89d39ae9dd239067bd5c43d089e7d5dec8135b3578f5162995269b92bf139de1561707dc40fe1b919e52e54941e9332801f22f20eecab9e1994d363089128a56d777971ce2955f35d6e0b57a6d144b2f051647d9b0052994358e50d390488e44c9ac8856f837e832b62cd6fcab6f15f5c80bc75f6db4b69eeacd0aae3695b5c6a85922abc30a8d3b337933901e45430ca3c438a2c420593f8c40d648e0499a171bb21a8e008bb0b085d8f65f2061329214e36932207eb1acc41b2296fb853d1ac5f261748630e9ec5596acf4435218a20fbb9b307c2f92fcf257bb9d4fd5fbd6bf2cc0236b49b16f611cd7e77a8a25797cd6d6bb3f17e034068085b4c096f620bce6a0b637d892dca0eb98904408badb66625fe3b5bcf68740b864a62306bd9f31dd11c13d87fc8bb219b515418cda17b59d3975b641183be18ad9adddd42fe08e45e0126a8aab8e4a5c727e061166b29dde570f09709510056accc5b2f7decf6b14457dcbc9645c1db8ca816ee9653dd5178d50ed0d0f34fb5618e56d4d60ba210734d121b761403e0ef029177e991c4f6c233194731298f3a4ee01329f599a08c27511b98c30f550aea60c266f352f1f785919e51590356b0acb84231a22e15eb060047c583357aefa0d14033e1ff7b9c7cb6b08cfba50d18318d53ff3ff14ccf593658a8b22d0ed8699547a39a2231e7ad6c6097dc0e2f9c4aacc9539141198366a397635922aba1323a1cc7962433fb304346e1dc8768d7a26d5326611428efcd1c584cd1986d3b14c4df2c2374ef43c381eab27602796c0cdaa6779900958eb2ea6d25d6ab31355938171979d6172ed578d895370a2706694036031bf2c6dfd4e5cb1bf9fd368ce52a5572f3f1c919d2506bca52561462f86714643f2d99503263ebcc39a3fd567aab66925a7eca434b909cbd7819290323f5700e3e74ffecb5e12a259026dd7bc70c5520a32ec360ae17a263fe40ca3fa22731ead2a5b250ffb8af00601f16e464ed9e6b3b12a449d59d465025c501b78074574a6eb9dd5c57f13771649474c6abf0c363d3e800c590153b8dada6bd86c345f94f4caeb431abb1772473523d4919acaebda16d71920f0fd66b930b8ce2610188e121458d223d6f9917090abaac4a99926662014b877f0c118d2bff261b2535930de10bd7191005e0e0f649f33d41601d73ec36c3c64b611e5aa32a12147ceab6e7ab5bc5bbbc1b7eaa373908631185d36d62c8423cbf9ce8f4b408b199d5657d1defda0bef8e8389322d6ea68aa49ac7ec680116a75ba250bbac2ee14cfd9df800bc9ec7e4651f05b5c3886b707984725162171ca08db49e76904c4340dc966224820be0af423b9268d11a5c343acce7b967f65b11bc1257eb2b024567758aaac5e19bd51c3672b693f69b9f18ff31900f129a3555d37f3a78b2293262e3b21a6467b24220cb6bbb3eed93c2ad2c3e117b27884bccebcb38ffa1f39f445c115c7739df9b25f5abe29b2cb655ea6b937b2ddab14f3d3a9c6b7a5bbd8ccae6b4e071c43bb02d55e88472474a5d95f0103c95daa3461acdca1d01d48a91ae69339170ef7f4601b7b5b6dc90494ec6627e55f7ab7532be2b196a2c06039660e5acbd833654e0727d92f13ac25c8c3a746a0737b4734309c27e304b9dda5d5493e4955e9c2ff83a44e4d9b50b054163217b4c9a7d12fffe9ffa491247ed77aaed4d6fd9d20b55fa59c8bb2a7164c544f30575659c51b1891992da16f78496106631bb62366b6af3e5a6ad2f450880e09172ddb052e369fd49102a351448de1227b9ef0118b3ed010621eeb2d13033b7c42aa2a702bfc0c381083915482595d6d46a3134edabea484de0d3c90ce3a925f543073e057df71c646fe65e9b9eeb4d387d40201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0602370202e606ffffffff02d234b101000000002321031c34baf8aade11370387ba2d63f4f0eb82d9c4ed0c92498d5b84a3aad45a68d9ac8c256c000000000017a9147d46a730d31f97b1930d3368a967c309bd4d136a8700000000010000001a9748cc5a3d35623fbedbfe530d8019e00156cff0c5d3a958da8b90a0cfc248a4000000006a473044022002e2a63d7bdd3e51be2583f9bba216a726a301ddc0e2b4eccab92361a14cb8c502203607fbc22352dad00cb8b17b7d9a51f252488f71a6f09611486d17ac9b7183c00121029c76453976f0dd019ff681f161e81c446675e2645bca05feb71cbf07550b50e9feffffff2187bfb1824646b09d2428a6e8834d1932115fa45dcd19987350068df71c8e4d010000006b483045022100abd3508a5ddf743e3dfe89bafd94f2ddcff6fe7393e075a19da20086b0d3c5d202207008f0fbe6c373932352b9cd97efe54ad8298df1a57892a01767ae6fae7d6dee01210304e4dcbbb35adbf8cfabd89d57ec907e818802db8d9c209d496d26998755f266feffffff450fbf999f0e599cc7e58cef161dcc0e5592a591abb8fdd7634e345d6bfce955010000006b483045022100c48ea1a1d8ca785b9660194d6a11e8f8e7c411f094c6885ccda0a6061b62f1f402207ede22001732f0816085192d69b4be238c47fa5f2684dcd3165608cc827df46b012103df463beec0ad9b219b358c4e1944713b193570b9b389ceab5b3fc0ee1104e8effeffffff4dc24bf03a61c36f29c231e2650ab77df1fffe5d41545c70a1c2960d3edeb0cf010000006b4830450221009a732b463747133909fbef98de5ac9043eb2b2e10d8fdfa01e01274e81cf3c17022004e07bdd2f2bb3ae4dd9f91b01b3280387b537c907295c53c21caff67e133d8a01210212a99cbd1d9ab618c39d6b4322f4a5e270d0a0b4f9f66bf852705ea82a84807bfeffffffc8838d9a97e40675abb4a36aa65c554103da1e917c29dcaa4b8089f37e483a4d010000006b483045022100c7a0ef9905e7ac6827eeda483f416fe6347b4dacdac3d50b4a1b81ac7fbe4dd1022010379d6df2797df146f1eca9aac3ca2953a954a13a86951910612fbd1643911e012102e42b6f50fab18df47c453dd7e2f1cf0bb2f9c92ffad00e2d397f5b214553f230feffffffc8838d9a97e40675abb4a36aa65c554103da1e917c29dcaa4b8089f37e483a4d020000006b483045022100b10ad946b0e171e5d3186ddeec82be988ed748ba440d21666c116f38efef946002201401e8c39c5ca68ffe901c49e492a4bd78bfd0b448533b8ee5bfe7bdade02a7a012103c95f9aa09c2bb0a64521cc293fa92309501761ec63cbd43b4e2670bdf6cc07bafeffffffc8838d9a97e40675abb4a36aa65c554103da1e917c29dcaa4b8089f37e483a4d030000006a47304402200a569af65000ea5c325e4f5f354839c5e55260a54147f132ebb8bb3e469af3870220736c69de66d2f32ccce2c8fab4d69214b0ee4f0f9bfcfd538ddfa0440aca90ee012103973a03e510fd70b68c1438c8374389134601782975afe232a27a1d895916d5dffeffffffa4ad5428a1f005bad3979695595925846e9e62caeab1462332725f286c58b4b1010000006b483045022100e01e34160844ed79705b446a1389f14f35024ef3d1dd3c31cdb3f7790c48250402206889f977f418c983e2bdb7e3bd0f985c563b496e4a7b634a633bfee022b2c6a3012103711072744fce64b25880393c06953622a688a30163f573a30e617e8cf12f6561feffffffc33363c565b43ebbfc39505b4eddebdb433e349407a4e845f124fbc873c99edc010000006b483045022100e349c35f94e7f8c03d0871a88edcc082a9085839154e10853aaa119c77f5a3100220220bc0f87f2a38bd96a5ac3827adcc192f61052121bfdde63ca8e0e0336643d4012103363c66d6ad7315fae30a8e91f9f6210023d3d712303797e89ef2dbc2c7575bbffeffffffc33363c565b43ebbfc39505b4eddebdb433e349407a4e845f124fbc873c99edc020000006b483045022100aae8fddade5a013548d23867cd0310813209a2f9c5b00e573f7b9b095966b2e7022013882e6d4cc175e92f2c9f10ae863e9d2c07cbbe0e7784f627c8c2073a56c792012103234925d6530f5278453f57eaee6da010c2f75f1d54001fe204cf7ba74096170afeffffff6f9b5032cb05dca1b159fc65ea21aab3cc0b5303e9b928e458ea130db2573aa8000000006b483045022100a8fe46e9c4e29af7293a5760a076a7e9891a0e650667bfdccb12c87845fcbe6502207171bec03fb75f59423a74fd8990d04ab6298a783d8ec490d897a0d977214672012102ff974068ee901457eb6d1b456d8779225fcc18f6d8b8c832c293f56adfe1ae25feffffff6f9b5032cb05dca1b159fc65ea21aab3cc0b5303e9b928e458ea130db2573aa8030000006a47304402200fd832d19f57545cd845e73f52831d3ad5aa44aecb3c9cc5940d8dd4127444f602201ca201d40f149f17022f2a2ce389f11a0fb79ac02525df6a00d6ef4372548b630121035980c207856721e436a79b24d42adeae4f040a6c3749b4df25b3fc8444613b06feffffff694db8a8571ec076f45751ea0caa4ab4dec62a051e4f8f9410d781eea043addd010000006a473044022019a4ca2c3be06957233a22647c97978f6a2576c9a13ff81e4d8d07f9d181ea2f0220475ae61582dbd5de9b88861c40659db976d02506b661d1a7e3405e32fda498f90121032172d30875d6f69c96c7173788aea855c33785f7be139ca6f72a6e4f83a27ec7feffffff58f91581c7d112e74864f8d6f82f1bbbb66e127e558334d7e82adebc5b02bc10000000006b483045022100920597da5694d593bc0709947a481127375497ee6342fb617b1b82f18c0d65cb022076822177751655a1c559f4a16ff66dd7737de62e81d81a991f4a7b296c810cb90121033beebcc6eb5033a5bdbf306a47ca2c034c25a5ca220ed87211d38335f103f7f3feffffff58f91581c7d112e74864f8d6f82f1bbbb66e127e558334d7e82adebc5b02bc10010000006a47304402204124ba75b504458f919ef632c931a397b111040755f7e6dbac8fb2209232ebca02207b68b8834c97d106ab800a0aed5e476a4c7018b47b4ea9bba0fb8a3dfb50c2d701210353e8f114093da7723109f0c7358d7d65b05fe050c55525f694f607ae9fe8b905feffffff58f91581c7d112e74864f8d6f82f1bbbb66e127e558334d7e82adebc5b02bc10020000006a473044022026e4f74758dcfe499f4610e9402e31b7d3ee1be3fa3bb226e0b6b42b1056c7b7022045966848c2b7e044ad645e083b30fe44952fe68807b64ba56dd0eb5dd03011860121031df16e856417dcaa1eeedf6ecc4fc5309f61263be6d56628ceb6a55df2415cb3feffffff151336d961c3ecfa9162d402be0a6fb3e97b6c6cfa268de472edc5b5b904b01f000000006a473044022066c2445d7039a890991d2d4b3843e99f9881bca50a1704e4a42d7e4b416a9a5e02205d4abb117012bb6186f510f640bb46c8209c4f25b3bfeee503f28884d391ee530121023c8d38fd0533579fb9583faa7f8e1671d814185550ba17f74b3a45e8cc016fcafeffffff37d4d84f4c6d92800f887a7b1d4b45b483eac2180a61d3c74d1933b8d3d1d867000000006a473044022047d1e44da9dc7a63b7ef448ced14f2724e764c8561aeacfb424e1a8d43a16ee60220794a7e99d2a7b8cd65a5faafd76aae45849801548b70c6f3ad23e2d1d1173a5f012102c7ff60223db00e491bb4a212e5c163735dd9c6d6cf0f2056c2d2e55cd5ec332cfeffffffc9619208a042b2525f79de089d32136abe4842f0cccbd7ebee2f85abc9bc527a000000006b483045022100fa6c7f190af50f1554437bfc72051742425e3a2c203d95af90abb62a2a4c59fb02200db31ccc7bdabfb37c18af6b6862de6f18a5ed04cd0978f36314c54e4c08f3bf012103a48da2fd779b6508dc925835deb0db687695c18f6c8142d91f0d45160b5e8d50feffffffbfb7a558b64fe713a263102f24291a23007cc5fbc20dba614c6dcd0dde569922020000006b483045022100e9cc9081e67ff7627c8bfae96a2f2f77fb7939eefee1380591a1aee001614fd00220764165826b41c70a368586bd87a0f0ea5c95b3e27a5f362560795fa7cdd5bde1012102637cda3ce968a431f5a4664ff9ae5262f652981948edf29e8be99835e1bb55d4feffffff016ca63f2a7d80d3f79103aa92c2eee80ef9757e69ee2889ded7463440817490010000006a473044022029bcfaee4d6cc6b3f0c80c341214b3d8d2025180bc0b9154fcf20464837e70b702207ce8d534c2285498e82291c453cef791de5ae66ce9747d52184cd40318259ee901210237df62470509e32d54c34afa40a421f7a378f56049cc44c6aefaa31eb3d31f81feffffff016ca63f2a7d80d3f79103aa92c2eee80ef9757e69ee2889ded7463440817490020000006a4730440220445ad74d8ebd44297ce8c9db3105e97d8025762e8d839cc7d856215e2adb1b2302206b559656c060f54830342a1b1b1b0971944c794b7984caaac1be7dfbdf0a6dd5012102f40bd3d5bb5e10e3b62eed48b0d51f0de159a94a4bc3b91c95aaad67fd8f5843feffffff016ca63f2a7d80d3f79103aa92c2eee80ef9757e69ee2889ded7463440817490040000006b483045022100f15d36af7973dfe474951f970925b6038b73dcc503c6e690a88f3eb596aa281f0220087af20bf9eec4ab66089d17bdda7eb52181d21cba1e5b2b90c8e02d4f25ae0a0121035a39bd206fbef90507aacbf07b0df241471c793e8ac0ee60ac7fd9b27167a557feffffff671971f9811ac48c028985598cecc5b1f9e23f0343044de34d7041cd7a7cf2242b0000006b483045022100fcad4b0f704e86727c6b23bbd2a2dc1eb8acfa9df93d1824540950aa2871e38d02202e091b327230a57e3f3f58f92cd90febb50f2e691e5c4536164b04693215004601210251754a16f344dcbbb73abc10a87d5ac64f88a9deef245153d3b448579e76e690feffffff671971f9811ac48c028985598cecc5b1f9e23f0343044de34d7041cd7a7cf2242c0000006a47304402207ce738c7c12753647f7b7f273dfef55794bb2c33c8e482287065907b2d939cbc02206dffbb59374be2f6fbfb6a601fd0f06ec5ecbb614f7c1f1628f40438385e150101210226ee88d62d6b0a14aaa0bbd657eae662659f933c4ec10d5f7d9865bf7d6bc5f7feffffff1bfc951b583904dbdc6f1f69237bd2fb468221134bed1320646b138b1beb606c070000006b483045022100c76fff183fc3120d06d94f729886b3c14879773185bc095524f71f264247d2c8022057d7d5bfd4b8f347b2b3dee3dc3b4d6cd4eafc83c44169a35d3ba33ec183a9b00121020c118fceb6c92efd99b5b14712cfacefd5f32930ce3a121720bd4587a9802e8bfeffffff02a0816a00000000001976a914f1e695d3bc5042f016d98358b831dd97a6dfeced88ac45420f00000000001976a914437f9a7f8784fb7547a26ac54e41152b35414e1d88ac2b020000".into() } + +// https://zcash.blockexplorer.com/api/rawblock/0000000003f9a4833161d37c1f31ef1a0b71ea8a3e2e9fa721bfcf046e1aab45 +// https://zcash.blockexplorer.com/api/rawtx/e92d6b20504c6ae9ab1e0299a99d0501ff6f0395a21334303656a81c5b7dd319 +// https://zcash.blockexplorer.com/api/rawtx/a61b50c61c4483d21ae76321847c85e628db4638eaa6b9c3eced6ed99fd92e09 +// https://zcash.blockexplorer.com/api/rawtx/94d33f3f457e0acd67df4e3b5f4c4b0caad9cfafab0f782e042f96c88a86e282 +// https://zcash.blockexplorer.com/api/rawtx/9bbf25fbf9ed2be0ee7d0f0070620200277b04d20008d254a7ea32cde65b58be +// https://zcash.blockexplorer.com/api/rawtx/2ec0bab54f718c4c091d836d13aeda2c3058eacb30d02d9bff398bf31d07790b +// https://zcash.blockexplorer.com/api/rawtx/18c02a60afe52735c09841a3b68494c2088e1885b28a5d970fd2045144ff1234 +// https://zcash.blockexplorer.com/api/rawtx/96ec40bbe489a51c7a66b29aaf50a74116aa154adb518bf4c58c020bb3283069 +// https://zcash.blockexplorer.com/api/rawtx/15e172297402fbfa1ac3050fd21ea21239a14fc79c6d601a9d82aaa4e1aac824 +pub fn block_h419221_with_donors() -> (Block, Vec) { + ( + "04000000da85eb7024cfbbb0a3d51a4afda67d6861ea814dd64b3c9492cab9000000000072b2d2e13cd502df52213b26956a3ebf0118e304513fc46c0b34c71a249f0662c92596821755b0e887c70dc2c27b539cb5c696ab8f2842d584c931814ee04702467ad65b2425041c250044f5ec0000004000448a0000000000004700000000000000000000000000fd400500caa7ce08b089bf96f7904630a5645cbe4a3eb85117a90292e91a2fc5f18b457d9a2ea89660bc566f01074f82040f1b76e72497a99cdcfcb1ef24401e61a5231a5793fb1f4ebfbd16d63427cdaf56197b380868019199fd838c4b9095dce4393f2d8155fdc3151834381fed754915baf70708672d7e741faf1a567efb32158a1e0b00117dad09f898138af7f75a186c1eebd5160f4ebe61d2636eeca402e4496622cdc23758dba504acaa10ff08045f00fb344af5774a413c833e983d2e06a4dca2e2c6e9880cf2f347f05d29147a534c43158cf672d26726d9edd299014f6d7e233cc03e584225f382979469d88b77a2a38914f54bf67bb0f8170d0e94cfa3af73ef45ab968177e5dfd15ca881513ba715881a15e8081cc6accd716ef8c67b66edb1fdb07935c2aee898e56fa9ad4ee576c83f99de5f8296aa0c7426abd6a5b42bafe7e637cbaf5455d9f5e89dd725022795992a266509fbf421a1a04fd0c162d36ca39432dcb4b21da25cd3d31f289d4c4b592a8bd89d103b05121c641a1ebb93109ae29df3313f4d6a88ae63c6247e724e5e6466c1264a15e8a0fc239e6fd6fa5c600b7ed8ede5041bbedcb4b0c4b37e11b229b4de1aa14fc5e3fcf1da59b9fdc776613c5ac5edcb17561198428c5d05141305e6ef91254048d61cf9b7777aa19980b27f42b865c8e37fc56c3abc69ec8716c37f8719044ec6ed55ab1469d5c4005a4c48a08cd723aa88c73fd15eab3e5dfd7fe510d6068c601b132dc5de773e159e78b1830f918f846475e66772a305efca9b22bc3fb67a626157db38c3bd16340031ca8a1a62fd1eb70624e6fd159c03d8e924c2dba4ce2a3a99ed56cb5e0c6fd47732e312a36d1bd825c87f2bffaacabdfb544bc4dc63bb68ef8fb10bc6193fea879b2244191d1f551dc571d36c9a95672d8936bed7b1fe7ef5fb982a030a3739c41c15e1ea8a76853cfc22326e9998e887100da90d5cd159c7789c82c0bce5af0decbdb6e83215267a74dc9475d6cc7af1b32d989800bdb8ec71c2316f93164a4fc272ed9df9faf4ddd18756069c1af70d47f16e1883e14ece5bc428e533b496167573f5c9a135aefd65ad71f5e7145a5dacf7e71eea9b5f95390e6cb8f9cf6aec87dd3fe135c6a5fb5906ee51631f25d46325da11c37bdd02245c904bdb4b682bfcf2350732054378c7ccf559a4112d4c8fc51c5b7ec7a0e127d47982a38c3814892886fa40dd8e52267638b23d2a1216d106959415e48776898f3cec064b8f72eccf4a2ced014518034f499116443cf39c6a093d3bdf13220c354f82ebebe9b5a5b28a3ea9e0d951ca3bcd7c3207ae70dfec738592bcb494ffc315fd669b8b572d28e5666e248e1046e516677869f899163078770d5e3815dcf439560d77160094d06cca032d8a5dd718fc03625fd698816539c18c61335829855cc76850d80019d784602ba33d75738fa71d915383d25285167cea0dc4869fb109776734cee129fe0e2cad44ba8f3abf33f09b9a355dada9499547b14f7df0aa3b6bfb485d11cb6fcf3d79d6d5f41e118ecdac291d02e24b87791e5d3f9ce61ec573c6cdc36e51d16f46338854248c170b7c6af319847cdc2fa36b3638ab3b6d32bcf0802ef309ad065413c94f75930b4e34fd629a66bb53c603d1084e99c86acacb4750ddf2d3677e568b7ec93d3d6dcbae82eda9bba6db14ad6765f9295527b6993407a7157a6d54652afe57a112a8d217c984a555c7981221597581902ac9144aa2b3c5e6d088e0e75b8819166cc5ce3c4bb632f1f202e79ccbd2050029afc18f28970a9b5a902599a34743d6be60ac15fe0cb7ba2733b214a2975ef445dbb3c86cdc4fdbc62d3f73db4e66867bfdabdbf08592570791fc4cfed69f1dda1cb7090400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff050395650600ffffffff02f6199d3b000000001976a914588219b4d6bba2e535c2f92af7f85f5687ce82ce88ac80b2e60e0000000017a914e0a5ea1340cc6b1d6a82c06c0a9c60b9898b6ae987000000000000000000000000000000000000000400008085202f890519d37d5b1ca85636303413a295036fff01059da999021eabe96a4c50206b2de9660000006b483045022100edbb353fbbfc53fcea769597374bdc0f198cb10c0a491cc9774f5a59b9ff685c02205845920e3bba2509e1d718be0a6f8aa45fcc548dcf5574337a4c40823c185c090121028a88a535edf9d1e01ee5353c4001a8ac67e9f784fbf205922d8484cfb6c8ba43ffffffff092ed99fd96eedecc3b9a6ea3846db28e6857c842163e71ad283441cc6501ba6c80000006a47304402203ed36a86ba4b19b906dd0e184deb381591b4e41a58a5442a29e5b56bcd1683ff02205f1c328d5e4a679aa98f0fda16a048acaba6e9412c02d5bba0cae9da4003ed780121036e198ea4f955bb5fa35fb5a5096e548123cf5c54e0667165b0ebb8809bbe73c2ffffffff82e2868ac8962f042e780fabafcfd9aa0c4b4c5f3b4edf67cd0a7e453f3fd394010000006b483045022100ce2a26a3fe65551ddccb00a4b30c0dae3df0b71751c72c3619da037e192a547002207dc66c55831ba22687a206865865cf04459e624d30a9640026eac362443509dd0121035556ffd87eda7df56cf4a1f180022e22572920d70994cb9139ba973f2a105d71ffffffffbe585be6cd32eaa754d20800d2047b2700026270000f7deee02bedf9fb25bf9bb10100006b483045022100af6ee487e3841872c9ec4703e13dff9630e41bb5a749dcf4ebb8973454774dcf022075fc0692b4549f939be8e22bb3614850fda19b23fd97195300963220a2154590012103e2c4cf77241ae38049320220c360e17b4146df8bea9dbd30eb072e93a6ea8420ffffffff0b79071df38b39ff9b2dd030cbea58302cdaae136d831d094c8c714fb5bac02ead0100006a473044022028d40dc590d5893a2ea361a1ebdf9f54a48d697bd577b5eb9f506ea3a7eba05d0220780441fe9b750a940ffd49e7f9f2da49b7602ce61e9caf9daea5235ef37ce1d7012103e2c4cf77241ae38049320220c360e17b4146df8bea9dbd30eb072e93a6ea8420ffffffff02b222e51f000000001976a9145e2ae850f6fe97f8a77a2497706d5f38a77c8df488ace3d0ac05000000001976a91418ae4e28f414a29a171a922cae6fe4cad6370b3388ac00000000a965060000000000000000000000000400008085202f89013412ff445104d20f975d8ab285188e08c29484b6a34198c03527e5af602ac018010000006b483045022100dc4ed5623871e0479bea4724ca3f9c46d16f8ad9bec2faae23deffa5e1f3204502207c5205739270cbb6e9533517a23336f184764ae5ff3a9d67cf68abcb11eff5ed012102010a560c7325827df0212bca20f5cf6556b1345991b6b64b469c616e758230a5ffffffff0233301006000000001976a914d17b9a4effdcb6d1e89902911bde10f19fd936e688ac44f4efd2000000001976a914c8b56e00740e62449a053c15bdd4809f720b5cb588ac00000000a965060000000000000000000000000400008085202f8901693028b30b028cc5f48b51db4a15aa1641a750af9ab2667a1ca589e4bb40ec96010000006b4830450221008b02b68ee2ed298932a752ac7280d424c237db91d56895f8af9fab5d15b247d5022034d2b250946758a97a1ff02f80af4d65667b91ede9163252badb6c83f96da80e0121027e442b9448000bdbc3ffb7b88d807554b9f9b3aed7249730d5a9839af447e1bbfeffffff02823a8400000000001976a9146353535156dd23884a4d3857e8288c64e534b26a88acbdf20d02000000001976a914c5a30599a16221eb9b9ee05ff48e761af51a3bd388ac8a650600a965060000000000000000000000000400008085202f890124c8aae1a4aa829d1a606d9cc74fa13912a21ed20f05c31afafb02742972e115620000006b483045022100e58e050d6a96e3a99d33377e182a2ec42459c356e8a944b7ae5b68bd95191c39022075f035130d688f9585884b874d3fbb8756e571f8551b7856f63ff675391bfbe10121030ae247fe1f1425e64e57af4af277252f35107c59f9cfd56d09b74a34025e698bffffffff018072fd05000000001976a91414dc37491fd8e197583e9f3654ca7b71d8ff50dc88ac00000000f6650600adfdffffffffffff000159b978091bb9e9711daf080aac7d616997c99812edb7244f60ba136624e34f5ecc79cdf5eac0eb43ea119f9df9d36ada413326feeaca18abb0281bab2a8cbb50dd7cb24201fd28984896d0c974ef381d1fc14fcefcbcd18d6241af247b7a224d261957f45319b3fd1ae76da57ad78fffd15bbbff3b3053ef89efb942dc8a82121607452393b60554c39a21d1d0226bc110f35026b89b3db2aec7e85b94c4f5d71eefe1dd0e67c2644d74a5ccf66c7a318e5f31b21b5ececc39c24c77592d3b3b78113e55ada81bb30ebc925a0b8f11abe076b93a017459ce806f3d82468215d3b7e50796d516baef0520278d97f165bfea762992814d28d837a6ac23072b7e86402cd9b3765dce153ab21926bd5f686b0815e8c5e6a8e1d720f195a858b2831b7e5e7478e986b0c8222375ce059bb8eb9b7c0a206dfd44c5859dec74e35dcd9e4fc03adc06e9ca7530982d2656e1e0f791974cb14d2ad6c014271ad78d3ce40aae01d27a0f28c36d5a7b8e8413b7d30f05c7f0451312568363d79f18c0296abaff84b2aaf93da060e47e9d47ea7642bb849c090436198f3b1466aad5188e03081777930248ebd24d0fcf90d29e4d9ca6b878c13d4f2bc6842375bbb4c3dccd69aab8a80f7e64b5678ee5e0c229c18879dcdc1e9c3fb1f55ce3562b63c5979beaafe3673c4f2d7a21a63553aecd203d7e507ebca37f5843c299e32c77daaed93abee033fef83fc15475602de179c9883ffe29ebbca5c96ff8eef7bb1051fa85b65c831b5ba2b529b7bbb6ec2d39060afb9818bab85cb573b25ff1478eb043056b963fcf98e92e5e973ae5d44180f2fc9e84558e49fc054f8fea2ad4aaeffc6e0d9859c822e5f5a1ade12a05aa91c756803d3229283966d1eee52c6465a70de9a991d7cb543208e13f3211156b85128fd06357d4022d77fd3e90f0c8ab7b452ebd55278fcb86e094ae15bbac6040872ac347a54e9b3f39f5dae0f5fab94aa86169d02041458018e022c11a4967b9b5b7e7855545ca96f414357c0b11b05a08c131d93d6e00d92f7dd5f9e2f9d1e9118f94077ea9d6a38cd1be18428fe3e6c3a58246b7ba356fc518c76968cff6bf344f93108f96226eb9de8de4bf43260c544a9ce78537bfb88e36a1ab2c32b61f25341d8e0c1a83128d93763a0ab9c8b143ea0bd2a6a6c2484d508a73c700d62dc6295260d7f40616385c08b87d24c136f218b8eeb926e6239612dee97b824fe9c0c2b0df51265834d7be10953ffc892a0492c0d058e49ba1cbcccd89674139ba8d64c9f97d9a0296bba6cb9d13fd3ee42c0e9d198b7f386d28cb3f5d4585bc2b830e346965d6df00c7386a1ffe5fc68e129de65f4cb864d42103f7050fabb4245772dae0a207421ae353f1736c158840047b8820e0694ef2d1728f3cdddeace49467c840f4199b030400008085202f89000000000000a8650600102700000000000001ad6602850c977e45413e67aa1cd538c26cde1c278e466c1a1c1888e6d9991ff0835b4c82a3fd3679fe04ed83b79f6e0b28eb54fe7a56dc2545dba703cac2f80445a76339b2d1b0f3966f34c24c53f9b8d61f76216905297bfe8d35cb7bf7d2e070c47129c5f9c558229e828f0779c6bd09c33281f30541866453bbbed00a746a928a919101358c196f4da9e192dd9fca8d2d9a91d0854cf79f1b6899b4347022bf7aa0403f226ad9980b1fe3abaa3252943bec860b0079057e9835be1cd4b5619a6932d31859c021003538b497501adf5537316ac5b312d80519ef1b74144bff158336a6c6d5d5dc1df01ad2e7a681325566b0e034a4ad52b932cd73b3d970fb1ae27e1bcda15242b585f4cddf806c9792e92cf59851c5c3313d3117931eb2c5588ea85491836a2522b84206aa7d54db7546ed871d02a8c9a024ede70e5988d786f3a348732376353ac2f3343f7e3a9a4d0a8eb3983de57f33fca0a53473361488a53acb707aee11208e8cf3e25940566561fecd520442f4684f13bec9281a05017fbb9f8ec1713793f291dea720dd9cde4e5ef12f24e5a857c652ff4eb1fde15dcbfe943157a535a16db7da5a584e456105c1ba2614007ce19e02a1377dc4fe665c62ac5e1364a78600d1840ba4ea2cf16b09823f6d9fc915ac47df2bb8ccee58dfcc450180c5a2d109534d06b65284b27ca2c6c942defaa069fc8e579603fe2d3bb8d378c0ab893f1f7386c2b10ca5d5e953c56466e2faf16b5cec7cb09fc1c98643554f8590b1911371d76f4e1c2e3920e424d18a70060fb1a901bee500bf6f1a82a427b5beff83442b4666b9f5fcd8e65154e42e0e0bf538a6778e9934db25a4b5e451876e26275e80b38ac383d5ddbc75e9c4c6ed394253a3e7f9e6a9690348f5666ec71bee4759daf1671672db6bfc4cfad9a02e3198608221b5b53f1b26434ddaabd37550544c8a1a85528966c2928575384ad35b495775f78df2767daa625dfcc6c872c9874b791a5f96caed0ca874cfc5ddc70e25a89ed576e1221ed5d2144aeb5c5bf6a298f4b562fe78446d9386618e8268e57a2285efca0903881e53f64378fd8cc23602b3d71dffc84fe46b94698977fef93058821866a2f4388105025e90786bcb8d9f7846bacbb941831ce109412f13270e587d15d8be2a9e6f225b9ed9692b250ee52250479de97141b75da667130fe00056a78984619eade2baa6ad1770718df897a4f50adbd91d10586b5b46b80bc32605456bd5e326a6bb91b3c52a03194459650ef5a6fb93d451c53fffc06b9e58eea9f54ccab5ed790b8c449d51c5c9ce6ec67c27e1f904f08b3eda01329feb0859f435cbdc1dd154408dad873c128267c2f60b4bdaa198c6acaeeeb4fc9777b278d035c2b258cd2beb20d629b2f9aa62a649069273034d3b30db4eab32d56c10e156f27c1d8ffd5c3796b18644bd893df7a0dcde3ee58a40fb02fc464eaa43aa79d7c00df98a1f2194b2fa4896e5f4912720bb64d6416aa6aa58e6561d13658d3ef6effac49db0fb54f3415a4c58937291000091decbffbbac6fb8818169ef94cc4b87ff6514f2bfeeebca107f5e758ae45c915e8eb659a2c434438e5b92aa82e91db8347ddef2bb8d8fb21d404d2adbb9ecefd118069b48e810a5483b6414b97ded7d89134610b27aa58b25c4acd1522f77a8c6b303fa3db5d299ce21363b81219ae8d09dd92bd6ad8de27faaa5a1e232510b4d7e09f6290eae85531908b8430e01b64ddce9150478a9d2ecd9b107fd017a464f8522b6d8a11c9deb5a6d81c72243b0daf888f921612b52833691474572fd5136fa2aa27b087973eb5bc8f9f201d3b91fca431b2c9b1181f03b45c4985c91a65d19e1b4d27e6ae2980600b85f10ab6b21d3c1cd7eb3f1c8bcf928a0e81f8a283515b93d3e8257c9e0cabdf108146b2b291a3d5c665953aafc5e4fd2444aa5ee85782779852b351bd4c2010400008085202f890001dcfb9b3b000000001976a9146e3cbfd5dd78406bd2f2d6b128da04e4e67ddb0d88ac00000000a965060000000000000000000000010000000000000000ec229c3b00000000c6fd3df9df74072f56e1aa910d9c4f8af7be3a5f4a652f147f3eb13912e4854e0dc0c4962a83085251d7616088bef5a16d25cf35c5dd1d169b931c17877723674adea9ced033d0e42f8b4fdbc9e77d66beff2d893c71e889196124b9d04a33cf98225db2baa915899bf6848971137c53cc1658acef97dd79e782381d1c637630dd84fe58237e84c67decef6f68ce11898ff8cdd467f60e29b2e2e4350b854bb053c697ebba7b1c7748cfd2ee6978c5979bcafc048fa39bd596b05c88d6c9c8286cb37a1bea66e100d931e2eab1ac07e1d4db89133e2affd01fed21e43ae385388a06f2892a8ad513f99468a860c53ae40a552acc6c3358a0c6a9e97807fc8ae68aa68fd80a674e3ba062c1f43bdc2bc47a29b900b279cb2a06500f1279f03bdd82c949d599fad1ad6f7fa85e7bf97ef3233e7aca423585a8d5dbcca9912eb68c3a26d278f0b7c48e086ad85107bb5fbc877586b9d43fade3331e7e64282d6d7b00151a2ca2f1f872fa700e95b6dec4e0546220bd9ea4e0054b0482e851de3d3f186f730a8c425a9380b0e1b9635ef492f25db2e92b725c0db75f88278d6cb59a928f04d74c24fa1866779c0ac2726a06b97d7141c7c2a7bba7c7b10adaef3aef86773884badf8b58058c9404ef4da32f7f8fd3eab32bfcd397f4329b449f333965bd297013ee9c483ee9f07c2cf60c4530f02097af6c3f94546fc4a32bf8e026457ba5d5e3466fb20a0da169ff021c5262035c54628a632499870adec562b954db3b76cb52a6b5ff5846cc2eefb8380428955a4e96e209979a7ef0da97aa7a7cb8e0a242baeb4d28ad641bfb818ad83f798bfb4f9f0f1400db9596060aea61e9e9c9376c8f8487519e7fff54b4e119d295d92a46366c5255ffa5be01114d9a27973c0cce23503c259f697e7c440719d44d4df03c658ca5ce7867548a281d0bcef8807c7ce676fe7db1a36d8a0140612724406ed3ed4696c8a2fbc74f5b110bb6ff6fb3d0eba14afa17ee9eb07765ad80818c0c86ddb0733122d03e67da369a29855f242950f1aa6291b2f27e44f0ce5c64964b86e8f4e31efeabcb5e44b13e6e90072407f7538e90d8e5d8c7e070e995be51f590ca81a66992e2c7b4bdd68714e4c17789d367fe2aaa0b3947bcf9c5f537c40b1ffcd816d98bf162d28bd0451a81de0ca2206e4dc117185dbd0e58f48868647d376021fb683f05e468ca38077627c10dde11d2066e27b025473a66a379ce58882f557fcfc79f4899a6eff7ecfb38b7e0112bf9d91c478379fb106205ed76da4fdad7353b7dc6368ca2afb71824842ba08123c55e888fc70f3d376d404835de8777fdeb99edaad93e36e1e12aef74664b0f9629e00d6cc48c5c4f48b85103d633f1ca58100fba128e0bfec73f2f7f10214d4a44fc83fc8459f80338a6742b9e9c422a7122d84cd5d02b33eb99d2711f7609d1af95218a608e79c925c138576909cc99403d79d0701de12a4e0c6dcd3cf867bda3e94b34ee9438bfdec2e469965110d57c99a6c2ffe7f9afe099dec10792e817f04441d33145f8f8d48b99b011e27076e7f9281970350efba0f6bdefdbde4ab59670a1638553a5ba869abfae6100fedce2be3d09a576f7d96421c0b16a8316009344f9df5ec45c80da5ad44289b8bdb609aad3b514e1c63718883bd0f295f880d8603e2df2d55c7159352ca5cd79707a517ed22e50071416d132fd03abcbbc729d490a6bb0743b8c01a43e00335f55349564716496085e26e38fc7d5359d6787249dc3a623cd7c8ded239da77eebf060df39145b785c6d8282bde827021c779ef463de0de47b01f2fc60b2ed096f1fbdb9edda702de4647159d786d4cbd727b1029b0c7be74be5807aaf8742f530c6b8e26842d9e66c54d5137d05cc8d39afcbed63097639f00e6e798b8bb40f8782ac6ec73f6cb723afe6dac5a7390d8479c5820a1aa84bb90316ed2c0d0ad5a66b76a07d55ea5784b555783d5a3ffc07673c98c47a772058e80cf639bc3cbcbc93197ad918670fdc09b232cf6bff38fc700c3d76ee88e72591c27a53ee10482bfcbea94be49bc8685a6d79815b0b8eb9e476588ac544a0082715a5cb7410ebfe98ce289b79002aca49d9ec2aff3ea03c3cae40a635cd29f3d62117dc457e8d443aff8850248064f4153a3404e0bf0793505b860a7eb670ecca380f10ba2bf0e8a78e75cfb97091715739252537306b2d4d26a888dcbfc398b6ddcac114b3f44de090ff9a833b67ca8adfb67e13a256ed20d070e3d580c09cf12410f64445b903ee1ba9794a4d1c5c5588f879cca8d6288db013dd687005761d32a7266c9bc7bdc29b20a9533d0360002481d8eb36545438792dc8fcb7b6f1a6e5ee1344243d847a6c549c064b0a3e02fd11768fb7ec395ec9c27cab6d3159e44ffd7f6361adc789dd1266c0986ba6cab0d9756c72cb83652471451b6f7e4b1f152147cdd50e03c70f56b499fe1ccc850a56f9abec96e7293664ebad8adc8a34ecc277e893040400008085202f8900012733d8b2000000001976a91449d3f85c4815ef10a8016e67475bae76604dec1088ac00000000a665060000000000000000000000010000000000000000375ad8b2000000006ca5fac32e9f0044368c883880c2d565983e1a6408cf28ea9e16f81045966880ac9378ac1884a7ab6d793164ac3d7aaf70561d72d148895acaf5c1186d57507a53551695791b83a67f1d5850f4dc04fd1f6d85d3dcb81558a6886c54ca5656f3022f2a553318507356680367cd92e375a44a34bffa8d1977ff54fedb0ef2b7f275d75dd00a5cb50f8cbf20871e8a5b2ced9e918c2592b45e8fd3511c08da19d80525595bea06910306ed2d8e55e2965d2a5a1e05713dc006a814ef6600d9c7373800a6422bc9cb5395fd1718b710e06ac17dc4128869ec5a4f1ae1522dd77a78fc176fd239113166381baa7b8154ffacfc78c3bec5f2fd62f6a22c60765cdd0e9a809b2c49682c5846496ecdfd7b8bd29d059404e8eac244b5168beaf868e06483e690484c46a9982c180a85dd74d5ae5c90240eab4c7d68729fce8e1188274b661a47b66ec1b5a07c647e9ef5d84fff8b3de4664eba2f1781433196e1615d0d4bf1b4d1812da8a045e4ebc23f7b8c78abb223a43b9b274ed33e501c1568301f1233b37e5b390583d371dac68939912c23325a6cc06c944ec4909659883384724feeb7879d5572085e0e7c7640660511879761ce9195e1cccf9aa153741fd53270e96e55c6e23466a877ab9c836cd2c39a2db683b9ef343a59e5e79564bf330e9456e26166cfbe33214ab3eaeb8eca7b4e7e3f9f8e38bb74526125b4aec323ffdc62989b6cbe8c46a01cbfe6af6bc8cddfe92b35b44ab7e59b434dd78a95041a42a31f8ba091beddd0c3401af50cb9d82cbb7be2e0969879e1160a4f8ce8b46532dee6277ec04a292fefdc45f2a61846b882e5c1f73879a6f2b0afcfd3946e2931f2deca3524a104130149670ff89897d56cb7661149327723a90ad64a96533d7214bdac8159bd54f7151d6aafe21984313bb8737266c084e1b9fe62e30fecf22b3050f18b3b78099254b692bed2f018582867b625e64aa549c66ceb180438c64ba4336663ccb127645ae4a7b6aecb34c902ef377f2198fb53dce851384e42f5b80988aefe55c3a13ea3e9cd3001d587c0bb5b55a38a728bb22052540ddf8a6f8adf73aa81e9b38a1d7ef5fc605c2f72f0bc5f0dbbb3ead787d465a3e6c50538cef2c5c478a0201d7d4e68e368f77d586fad374919225cac96fa7673dcf467923d48806f56952e6ed8431ffa79e21f2c9ee8dd23f10f5daec04b666505a29a7ae14d11260197d98f1674bb1977abce2f1e84f4230b688943488d0dcdda9ca995afca1a342f3d06dbc33753b1421351218f040db0932d308219d9be31b76561467a2c2a3affadf023ce1afa6be51e4ab48455e186373f0ce26150da42c10b94edd2092f6d0715eacf56958edbf0469d9a10fb0a50ca82b9652e033f941958a756c057efeb7a13593cf2ba2508d0e1f84a9ffc3e32ff0fc9a8a624e2eadbfaf0afb5b5c37d6437ea310c2d6d71a2268bea75c6903f0e500205b41b656e2d58ed96629b7b4a7b7b82908119423bfbaca377535e58d0ae0d8aaa3c71976851f1f2cf0ff1fd48bfe1f1e53d5e1de29b776d9aabbc0cff005150e3e9f214e5a0192e855188b6bf84d3f6243035fb24e136ad8f8c61d858498f3f3af9aff67c2628da5eeafdf2e165dece8dee7b39d25a463aea98208b11a74fb5522d21bef74af1b1768bf01a8c3cdccd55e41a6af6e099022b895c4ec166153cd0096be077d2c4986a9cf2b71b7da3a992ae22ca199ff3216cbaa32a2b900ce731a8819b4f29ff96e8605d21bc184f56f70ced81a0ad25a752b6402d501bcefdfa8efa77724e02461aea2780e912a3319b856bd9784f99be1e9ee495087c459565ba736c55a1f852e10b8af47fb28775aa6ca8e48f7b336748dfc0c65d0266c162a1ca4521e35b128e571f0b193c952c65f93cf07a01b8d711633ec974f74b3eeddc79f0e8338bc971258c35e65b5d07dfc2c5329753e95037e4f47d36f2d46cca86c451ff7f180d7e2ae1d09761623aad5cb5e78df3855e795b496d677e2b74c500243f93c7f14ec0055726e5229be794661c9e7704fefb3ed8a7367b6358a5b6af178740a0b8990194a25bb981ce56403e85fb6fd4355cf24a6a117f9f0ac87f7095141067b526680a8b6e3cb7d93bc2cf4b5709aa2f9aa873d9c18d2ad2ecae4f02a4a259f9eed02b22fed00bb02920f5115f97c2a88da6e6d45b4f48b480d464e5148d1851adf793d707737828f47a49a6cb22e9362b86de0d8d0a28afc3cb40f1c1282b41c44e9f1e3cde1c757b40434ab8b9b2c243eb4681e6159b2a5e3c9d91bea34c249ff3e3afbc0fb03958b8567dec5346e82628c2de610436e1d18755e49be413574a79b0ff7fecb4e5d0409b1358dd94dc38e448fb9f3168c4396fa96267d05f4907fbedbeaae54db90193aef8676ea1429a4d14c8ad281d2f566d8028a39429d09d6f61d88149982d06d3796dce9ed2acdc6713dcb66acc43ee3d2ea6446895e7659f78a2c6c96954886ad856824abbb4a177ee000400008085202f890001f0ba0400000000001976a914c25730c3ed94acbe234efc66d028ce6bec2f6c5388ac00000000a865060000000000000000000000010000000000000000d8be04000000000056a4bd02758fa0265f765274c68d393f6054f47e6599859a7fcbec6b437e904aca4dc937e8cffd4d7b9b2c48c557a78109bfbd5f75ee2288e986c0c3cad9daa62fb182a76ebc64ac353a2be652369d8d3303265d5a3d81301aa8e14fa8041676c71934d7d227a5090faa3a3f2b8b0b15b08269c9929df3218aa857a0ee767dc2577e569091f2be78ee8d038b1a2b664427dc06ab2ccf50eef722f706ba021f0b5857840652ed884e472aca41c5eccf9ccb88351af15de37d7b0383e655d1c72a1dd434c84c2a068f19c425ed487abd6de69cc56470fdbcd77c6864bf4af31cd89e34895675f72546c3be34e3bd5763ea49826596e57656cb33ffec50e84ce67cb09f13bbd7bc1f1dce69e80f35502328b091b3f0ac6ff7e02c8bcef9ada7520bb34dab504ff45704a1fba1e4d2cfe327498e03b7b5077925d492c1f0bc1d305d0cab64d684d92d51c6be8f422b7209ec87f7c45ac2500f7c2d6d8640c645feccb89b3e06b5d8ecb529617c5c5e4e606f0a3cbc97b2bd1dd5a5160974d819bc68048daea33e6cdc4914630dfdbdd71231e48e586880de1ec4c4eaa3b86fd0cc8afe9fb53007be1b36d1a43a2980e50cfba7bf42409dbdc194f2aadd57f139ff46ce758670a418501d7573adaa7e5d8d0e87e953fd13017257151222360fbdfc03f571358998ea029807580ba9d969aa27670e2e3f79c8fd23e8d9895798324a4b3d75db66fa50e06dd4c93c6641bae7ad673ed7176595b58ba595060d42248b5437163498b46f0eb570acf3b654d150849d56f22b82582d0d504ae5a7ac1a4cdd892b5fb525035cf5d159177c16a071d0d861b3b7f843c0ba2b9667f47c092fe385d65fd028080f639c8ab3645eeb34592998d8f107cd470b78339754a6f4a6c740a142c1a511e2ac021b8529f73232427df69dd1368e4a3207fed0ed7c1c8ae236d85704438dcd95c88ed875e7f1b6c38ba51893e7fa222f4ff683c2389d214d06b6f27f2e6e72c14fceed5275157a151155170d5711fdad5bbb8429cf4339f9dcac872238120d8c846ae9cbdb843dcb559ea7d2b13ca25805dfe300312440a2738b2d350f94cc079c16d9d6f175b314bc338aab2a0eb3a4f5ba42d960f5184099e37c677d1ac4a002d79d648940cd68d54a1fc003c895c78d01d864eafb736aa3ce34de6b1dee64fcb40e2651c68df139019d1a00efb07587c8f73ba82adc3633aaa3a1f33f1fa210a6b58cf15ac2767c31ae54a63c1a183e496ab58bd1a947d36e3fad220c66409c045aff0de4d09d60a1d962ca87c794af997da0cdbedf88a96efd99956f00aa0ce096310a620622d6dac7603e822235c1f1ed84095b8b4da58f2746a78db4dc267d57f64d1c9d94ec386e2c479692cc2f9d84fcf68d8ec69ce2c247128cf8a2bf9a1fd6df35b8a896b677499df021eda723ae6d3fa49dafdf216c427ef6a72a8f7398daa82ed12c2df338a708285ff6c1bb6a85d30af93563bb8c25ae57fcd5189a16aeb173a8e2b00ec1b63db83c94183b288df6ed3da512dac1e344fbf4d763f045c3bf30d608cd82c14713ac995e77561f8956442fa6e2ea934f413ccc543cd9fea10c8b7c3ef92d3aefae33bcae1349af60c66a2d48ab36360d5dad192aa38497200601f81209b82d902f85f3c23fb911502f9018f586dffda6626ca5cff8264b52018d989639359c61546272ff4fa4ad448a5cc63166eaf9b35012acb7ff7ad1f79c0bb252842ae609e7aa360355de0c8ea97849c3e089de09a4b238de3279c490851b2503e7aedab0a7e609d95a0167538fa56ef678f3f6ff15c4f35abfe63d62876d9da674e64fe37d6c146b5e3c3daf3b398b694a8f6a487fd55fe039b4b89d62cf4b731429227d19226008159f853f2d184fe8bf751e8b7f9bab8e2c02e6e1beddf0447366f1a1d3dabd7d688281716a44be5c8f1d94a673cfefcc147df8f1bd19502dccfcfec58f71803476de8eec7efd0bf511b89a1b855ba6d5fd3f426bb077bc11551eb6dbdda251ead5000c83c9c3ab3b1ec133579ecc47277eadc12757dd95856c094c731a2c50776bd6eab08d534215381d3b6cd8595fa5adb36a0658dc6a6c74e217f19d75eab40a51ba9d3a255859c9624e52eeaa114ffb21ca469deb8a2bf2c9e5e094adc74404b34035526ba5eae38b097205b512f851d019b6a780847e0d9f94d63ab17a5c48cd25045fb62fb305fe44864c5ba17951c18b80f1be6e30c1ce3e2d1fdf3437c6331574336900bb2e2b05d428a18f18bcfce63ef399f965ecd69cd88f305e21ed887fba669cf1363649af8bef84ec7aa4cab09296a4531288d87fe8a4bde17b1c329fa106fb605d53c2c09c23dab6dcdc6ad9c2b5828fcdcfd6a823899e4dd042a03c55a701f1c391a02f85b8e134b01c666595ddee1b74a76716576b4dfcef2198d26d21d0cf3db88237534d9d6aecc3ed20ba77a83b7a27790882637c154a2b85e00cd789f7510fd18aa7bbd1f5b8c9a441121863c1acf60f".into(), + vec![ + "030000807082c4030378851681a49bff0fd6645c6d1a57c23e3098eea2545163ac5b97d34bd2d5a837000000006b48304502210097bc967974b1456770db03b6d93f0ebe470d6efbeb5ad1cbc798e770c1f66bf702207cbc177d843e5069c59e774fcf03a6fc180c1c26e2b1b81465909c8c995d16de012102e44d36780d091d104668b75ae19a91700b7b2835abd9cbfdf0310b57958d655ffeffffffe78d2530d511a8c7a0e3140d393a4fdacb95542bd9ca9559d146303e016030fc000000006b483045022100e19fde876d6f0622389528cbf8b1e681f47e7e8f56eca665ba2be65b7f206c2402203bc3688a10c36464e8eaf198921b4e5230f54393d00c7b96571f94aa959c65d2012102e44d36780d091d104668b75ae19a91700b7b2835abd9cbfdf0310b57958d655ffeffffffc0224510a48b6d1cbc335b3b5a7b9ced7ef6291c5074bc9a8630b6d33b2b65fb000000006a47304402205eaf09905b0e8d02a872c684b49951e756085d8067322177b61465bf5868675f02200a94999a985ee6869c99b6beb88d9e238b857efa8e29bf8f580c90e97fdbbab2012102e44d36780d091d104668b75ae19a91700b7b2835abd9cbfdf0310b57958d655ffeffffff96cfa09e00000000001976a91421eb35226156a812e7476802622cee9d85dad11c88ac9f349201000000001976a9141615d01d28bafa60a57b29dc5ce06ecc5e59538188ace486fd01000000001976a914f9927f7d5aa37eda0fe38e5b7263ba86845b0f9b88acdc6ebd06000000001976a914319abd328dae8e54f3a85af60d481f2d2d9bbb9e88acd7a10e01000000001976a91480d4492bf02b3477174ee447f02f25e6148cc60888ac0556bf03000000001976a914fea1ff1e14f94aaf87b9e5486b9c2a7f5eb47d6e88ac479c2601000000001976a91482474f02ff031c7e3c850271c174e6982bd36b0a88acfd0ca400000000001976a91480f4d20d2730acbe232e1dacb54783c7f95ba8dd88ac2a44c600000000001976a9143eb6ac28018ff61c0aa40db2d7dd488fac88332988aca278ad00000000001976a914328034ca739c160a8c3be717e9f8e0370467f4b488ac45549c00000000001976a91417dddcf504475432f164a85e3aea7cdd0a3352be88ace8c9dc00000000001976a9143637ab0b1a5f38ff876b638a9aa28abdbddd1e5988ac40502d03000000001976a914aee85cf745855700cd117ea7242846c336841dca88acce8e6604000000001976a9148c679d67375cd704773cbdd56a7e64b13aebe38588ace368a900000000001976a9144dbd328cecc555c670e4cf5fb2797326d9645aad88acb2edab00000000001976a914834473018af535b228bd691a9855a75e9f8c095b88ac3a742803000000001976a914b936157433fda345909c2f7ec7f7079d946e349f88ac7c55151e000000001976a9142afa8b4cb1ce3beb2fbb2c9b4130a72e22b17f3e88ac54afbc00000000001976a914e4d41695fc7b58a1da808b5bb1b79ee05fd8bc4488ac58a5a000000000001976a9142196f3894a061092c709eda78ad4df4187e70df888aca056bb00000000001976a9148d26d843bc2bcf1a3bc368bad594baac31dcd74688ac192ba200000000001976a9140a739905b97bb41c8e14fea76cbcba31a115f4e188ac81dfbd00000000001976a914b8c362dcb9da5cf507cd25de5053e5fed98f4b0488ac7cbea800000000001976a9142216c98baa163831be00bffb77cadd5c2bd6754d88ac0e0a9c00000000001976a91486e16422bfdacbf2a9533392e9e25a90eb08e64a88acba67c000000000001976a914680b8871600686620c11e801aa35d36e03e2767f88acb2c9b000000000001976a914bdb6082119d4f48b164c22d63758aba0fdad926888aca7850806000000001976a914593529242c97e187b690eaf39a739dfdcdf096dd88ac1165ad00000000001976a914d8e0ec058f2789f4a22d98e5a5ec6a9b1232cfba88ac1542f805000000001976a9142a4dff63b373ff6f6266ff489f11ae4bfddce12388ac255ca600000000001976a9147068373012a2666be5e138dcd79edc02c231e64988ac991c0006000000001976a91498cdade359f9bdeec8f0b17182f8e63bb625482d88accd9ea600000000001976a914b1ab2a5bc90664b9dcee85634645a53219234f4688ac6c42dc00000000001976a9146a229ac12fd2a904ead277c229618bfabf9d0c2c88aca5c1d900000000001976a9143edca62e019de642164763879ce38f99d28ff2d488acd6e12603000000001976a914b8013b9f6f106291b0cdf3a0e9377a838ef6371688acffadbe00000000001976a914c47eefad9bfd4e058ef6e48e5e2cedc043a1ff8a88ac797aa300000000001976a914c43d6e6ead9d6c131a7b8fcee3beac49c15b284788acf6b1fa00000000001976a9148007d79b8e5d543c733f295be166efd404c20b7d88ac1fc89c00000000001976a914f1feaad3fc0f1a23a178ebc2bfa6f49f3c832a8488ac3915a101000000001976a914885fd4162d5abd4948b9816208f3f00154aa5e8a88ac8fdd9e00000000001976a914cf80b7364f6bc19d2ec4a6f8e2d89fda665badf288ac88c2e101000000001976a91442856ade28f265e2124280d15d01f3aae01d868f88acd256a800000000001976a91421133eb6a70db47b9831a9a8a69a2c352045f30a88ac7ae1d100000000001976a914e1884aa4bc2f8e62f2064280ce49e5f3347e179588acec7afd05000000001976a914d9587bc9829456d7e0410e6512185a9448832f7488ac6c14c100000000001976a914dbc32e17e61c268d12fb634d98bd36decfa9366688aca053c900000000001976a9145ad33c7a0250c384585f29c19111166dbd69191988acd1ada300000000001976a91445cee94dacec86d02fd10a8e2eeaca15322d2fb788ac954ea500000000001976a9145199e7d5ba67b77dd50d1550b74b64bd9d111a8b88acf9031501000000001976a914b8263bee80598d957d99ad95ce722e23b1873d5d88ac39d6de01000000001976a9147ebad62eefab20847d9a8f6bb5ddedb6c3868e2288ac39d59900000000001976a91428420b742a33520c5fd3b6e752e33116667dc72188ac46a9b300000000001976a9149dadfa258efeb60bef589a9dccdc47a871dc9c8a88ac9eb3a401000000001976a914d572b347b0527ed4ce0bd1fcd663bce51e74471688ac420db200000000001976a9144a00f7179861d14b4889507a1640f1466dbe51fe88acb5a8ee00000000001976a9144cb7e5ac88ad3ef2f7e81e5af41df7373a72ef3388ac2ce3a100000000001976a91477ef8700da198ad19fe20939f0f83053158b36d188ac039edf02000000001976a9148c3c398c5686d904487b195a5cbf0a9c0606c41388ac5890dc00000000001976a9147ac0edaa77740fb2796356df790157ce5907aca588ac9faf1a01000000001976a91432a0c471feb14990a8f8d104197b37f2db714c4b88ac55e49b00000000001976a91412c635559a96ce5142a0456551478017af99580a88acf818c300000000001976a914316571326e7e7c66e6e5354b54a54955730a953688aca2c3ad00000000001976a914d079212282704b51e42fd54f9c6f7fc6f4cf741c88ac459aad00000000001976a914f84871feb528cde643ae7a2fda39f5be33476b0288ac98dff701000000001976a9143f756e44a42f3de93da1c5e97008a1e7d895a9b688ac10511201000000001976a914ab33f5d9536953ad5dc16e3722777dabf03f345588acedd9c200000000001976a91461625c312bfd7a8623a257f7e120559c228ef1bb88ac19f70806000000001976a914183a38c58ba26e9b1de7a3f0f558fac4f6b0492988acffc0e203000000001976a914fe7531f6482ce23d36105c1afec66f88a461210e88acb9a2a400000000001976a9144cd3f486a25bd65d1db60458c8db575cddd685c688ac55dd9e00000000001976a914fe509ac5c63f0da6846a6ea12aaacaac6a65a4e488ac4084e401000000001976a914bacc7bc75d6bb3d7f48dcbc032d0c3877049326288acd63acb00000000001976a9140f74ea74cd6e34b049a39546c16df56765e96ce888ac458e0103000000001976a914e1223336944ccda29af8ff9e7cd6771aaa5c6e9888ac6d90b000000000001976a9142f822e9d32caa0061fb7f4a19b15c690229c627988ac1ff02001000000001976a9142ab7542be7b0deb248174192663ff7024b360e9588acb66fa401000000001976a91465c5cdfee879dd86804e1afd7a3571b35e2b207488ac3d2d5103000000001976a914be2b0d22383cd68690d77d2cf8c234f84b7453bc88ac928fa100000000001976a914b585f8e607b89e98774add7cdc38d300cc6fd56a88ac2c5df600000000001976a914b70661b7ddf8450e44b06b9f8e466cb209c504a688accd5ea300000000001976a91406a998e503d592247aff3405722c8d8ccd38217688acf614c802000000001976a9143abd9aa056340ee96080200c96e76c0e6507630688ac4003bc00000000001976a91446d91d91a3cc17b01df5d33c0fb40a20ff1361e588ac61121603000000001976a9146a25355644653e3fb323bf02b28c2275812aabd288ac74c7b100000000001976a914e823ba680d293ffbbeb5e3290e78491e4b97bd1288ac0ed49d00000000001976a9143786e532b6afa63919067d6927d667626459750788ac48852b01000000001976a914efa26a00c465c63367f6d01247a36f43e3e9252c88ac26bbf300000000001976a9146680bb7e80f4633c4fe8f5d454cd6249b09cd84088acc35aa400000000001976a914479639243e0fcd3534187f318c9d1e0afcdc858a88ac4709ea00000000001976a914d82c2b5e03d2db4548bfa3532b568644bce3e34188acbf4bff05000000001976a914a3db4582e21564ab6ec6737e4c5ae6611ca46b3f88ac1974250b0000000017a914d0ee49d95f1af8813af892f6f8f0b20cc0b2d62387cf01a500000000001976a914f0313a85beb2d1e21029734fa3eced9c60f8cb4988acf2abae00000000001976a914466efa6719f99bc3d44b6c1a8acecd1be37667b088acadc4a801000000001976a914c715139e37843572f54960b13bb405c209c3384d88ac2191f809000000001976a9146ec2652d7ca3e7c440b7084685177d863d67cc6f88ac4cf54902000000001976a914854afab8e7a75099a521d0c505a6ed158266786b88ac66a35a02000000001976a9140455e7f504ce84fd1ea87f2e84bc49ec1e3748aa88ac258c9d00000000001976a91444d08af1838caed5a03fa6942ef87333789452c288ac7548a200000000001976a914d7d8d7f43a689b6092b8bf1224be7dd19afbff7688ac4ed23b01000000001976a914c69c22d9365e6eae53116dc39f1e5dc13e51aac188acdfe9f705000000001976a914b115372552c8c1b0ff136295085b55cd9875681388acd0d3b500000000001976a91478eec3dcc9233585c3a507b4ba902a046655282588ac7a7ca000000000001976a914fa3d5eac4319d63f0ad349a19c9fce3ea4932ba088acc96cac00000000001976a914d4c879287329ad191512615e6113af110f89806888acf86f1a01000000001976a914d67999a5e325aa873d6d49b79be69d7e725eba7388ac73a7df00000000001976a914f9af58150f214a16b52b0e45d717afce6df0dc2f88ac0d8bfc20000000001976a9148d0eb17f15132dc0700fec0b54a7449e1cd9831588acafdc9b00000000001976a9145204474f836819a9a02a499ea7be7428981ce79788ac0618ad00000000001976a9144d0ca314461008b907caf99316394796895ba1fd88acff28ad00000000001976a914539e8b1aeb0d20bcec30a42e5322612b775fc12288ac98dae700000000001976a914ca96c16ff18c6fd7130c82c294fdccb8559574f188accb9fb000000000001976a9143c592dd7ed65a1666e0f5d5bbb29803025d87a0488ac1eafbc00000000001976a914e6b0ce42b2cec8bac699f99657711d61c58cf62988ac37a41b030000000017a9140514a081ae7d43e6d8f19576eb7f11207e264b2887bf9eee00000000001976a9140709612e02a8772011fe65c2308bda2f4a1cbfb988acafe78803000000001976a914956ed6f5815510da0a95078888402538594d587488ac05c9a800000000001976a9142ae03b9af7ea7ae9d01120279b219ed93f60508288accbc59e00000000001976a914299679aaea95a81c29ef731301498b1ac8fa323a88acfb6fa802000000001976a914a2640bc79a6f27a50090de5c09a4d6a6080fc95388ac9b129e00000000001976a91490eadef684e5d3c95d1ec084f4ac97cece48c0ad88ac8929380d000000001976a914b30c5355ddf6b4dd1cd0d39b80561dbaa204125288ac7d459f00000000001976a914f297b727b0d685b9f1a6ec925d3c1775f947e9ef88acd6119001000000001976a914a834b5fd1317e1c65a9ec152900a24d8f93bf4f988ac64ddbe00000000001976a914e4ae17578e8d0e59bce8872973ad2a4cac036a9388accbb3a500000000001976a914b9d281d5132ae9c213834a2cb57e5cbd05de579a88ac7ba1be00000000001976a91400dc262d1c407290a110a0d5d6b2b4a0eaea6c5b88ace37fa600000000001976a9147277f252fc5206fa793c8159082dd76fd9fbed4088ac0d551301000000001976a914d99e2c0bf0486d0a2a2c204bf4485e29abb0f6e288ac5f93bd00000000001976a914255315bcd063e91db9bbd87c1e09fc50550e2aad88acc890b500000000001976a91479f61dc202f773c2032a5ee173b7292b126c71d788acb698b000000000001976a914f33d3b5b3477d67bb4ae651520c04a66ce39897e88ac08593101000000001976a9145ee9114cb924d7723ce6aeb112709bc05a96eb5988ac52a49d00000000001976a9149ef47ff7999d0f13471cefb00c261aee1bb20ac188ace6703f01000000001976a91471475c94ef5606caf5cf414b276eff7cf29f04f188ac64c6a200000000001976a9144e68e1b1b78fcba97cd3b116a831a9a4a451bdc688acd1861601000000001976a914a858eec8a990f1ecd84649bfb53d773bca4f685988ac2abeae00000000001976a914e865e3c41ab81d558fd98c402944fc8558088bf688ac5fe2c600000000001976a914bce4a6472dc2b41dd09ee52259a2d81acc572ce288ac843229ac000000001976a914f103471f7f17e19325158015f4701d0003d2b92688ac00b39c00000000001976a914d83cb8c50ec3a3793ddf7222c05d6035bec6de6188ac99760e06000000001976a914621488b1bed9ffd4375383433bc333851d22f32b88ac32ccc600000000001976a914c72fc7b75e248edd94f5de64ff65ce39898ca82f88ac8fb1af00000000001976a9147be7168d972e2aedb1ce603b75448c5621957e6588acc0233c01000000001976a914eb1189add202fe7f6c564f1b1d985e24b2705f5e88ace6fae000000000001976a914d74fc9054e22029daaa31a76a34a67eedcc077b588ac1f72bc00000000001976a914cd5ff546aae1946d357bf19d4539b436f523d3bd88ac857a0001000000001976a9145657ea5a8890ff6cea69ed4693670b8542612c6388ac61d0a700000000001976a914e250f504dcb727c93c043b9477825183a829334588ac566506007565060000".into(), + "030000807082c40304a03b11bdb6ddf13f9c70049e78b8a0f7e262c82131ca8cdd30eaefe8df636cf47c0000006a473044022044d5ad4cfbb9690b96cac3a41157c3e87778bbaa95992efbe99c3383ce54c284022032b7e3a1c815370eac65d68c882a38c7a8f468e296440d8c36605732e881c5b00121030ac6ce10d3a9ae4ef77e7c44a2cc278b6360a26ba9386708aaed81e5ad2d7d3ffeffffff55f11f76f3d62f0ad7c554c4e9d90b1fde2d514d7659ef3e885c51f96987243d150000006a47304402202d8db204366f0ce60e651a773df037609438e1786c04f6bef27add32af63d75b022077097c16919e5ce8207b1bdc6bc0c3e1e04a642035ec70724554e726f76a3b310121027084a092b65fbd340b7095d2e554a1082200aa6df3727d20431ad7dfb57fc362feffffff369b7bd932fe0d59545f7ec9aa7a7ddafee5e76619a21b051beba993f9f0c08cf40000006b483045022100c64ad57e8fa7d0f61adad8209601408f5c02f23589131b578b1db550b2adb59e0220399376922510f600037202b9705c12ec70b56f9b5aabf7ea25ed75842aa0a0bc01210301b230b4de0cc1b546781f9504bc609e3a22bbfbdaae6250d5a9d1e6804d63abfeffffffe8bedc142e2998cc1f0ccb2581f7f450d559fe75b5a07e5186993ee22396d5030c0100006b48304502210095c9916bde1d743061083dfda7cebc8300c2c097cb12ebf7bc4dbaf1b813ab21022021da1f01eeb07f0f08dbc5f5331958d16d17069b436bc9a27a4c0486bf0bb61b012102116ff9d7eb6a71400819bd5a82bec662b8e591c9ea428079ecb7db24a6c82040feffffffd5b9790f00000000001976a91400433032fb84ff06a01bf5e5fb1189b602a8978b88acbd6d0f00000000001976a9140204ca6ee86d0205e5a64ea8ea7708b559325b5d88ac60970f00000000001976a914022cc60c75040e5665c95d8ad85e1588b5ec23a388ac29580f00000000001976a9140410408e195a0fcf6d35f3f3cebc42ab4981d1b688ac84821500000000001976a91405245789ba684dfd22860a900326f3d2c197ec2c88acf0650f00000000001976a914065fcde63ceb99dcd071281ba0f797e456226c8388ace5e80f00000000001976a91406fa1d983a91ff44c6e359ae85db0ae3490ca18c88acb15c0f00000000001976a9140af04cafaae4d0caf6c303a9e87fc35e486cabd688ac1d9c1200000000001976a9140d5409a2f62191e85be4e435e4942083efba6e8c88ac29630f00000000001976a9140df81f861f85782785cfa5e7d2061c00ee4aa44e88acbdb09800000000001976a9140ef1de4ceac0db1b6a4e2710a63450e26a1fa6e788ac7aae9800000000001976a9141199344085c72e852f69d41f954ba3ed1019f9a788acd47a8c00000000001976a91412fda64e5551a560a8536bc62d8146a12debfd2c88ac6a5c1300000000001976a914139bf6a5462e28098fba88207dd370025138ef1788ac72bb1300000000001976a9141483283ca0915942a927c334c89e88a50dc1eb9188ac1c720f00000000001976a91414b6c5e51e06378dad3154d94f189126b68f7c1788acb14d1000000000001976a91414e5167871c3773b5076c4c2ad70cdd7dccdfcfd88ac76440f00000000001976a91415129971553ac6e20e451a637e1b7a68d7fdb6db88ac70450f00000000001976a914153df4af64f8f3f56532e60d99aec8fb6f99a52888ac17721000000000001976a91415d64a01d8a3de53d61e3396fcdbbad10469c4e788ac085f1000000000001976a914178bfd166253f637df287520727d1c47f63d2d7388acb50b2e00000000001976a91418207b1c513bf7a5d09113d9367aad9fc1251cd088ac8538a100000000001976a914188bfafc2a5a67e6e79ec11438db612fc825070988ac046c0f00000000001976a91419ac5cae2771a49a98f58161bbe83b08d2cf288688acdc781000000000001976a91419f1a566975de74bd5248045df82c473c339a72788acdb520f00000000001976a9141fc6d0ceb7e35bf7d0cdd1740c86a747e949f08488ac2e4e0f00000000001976a9141ff29fad8dce9eb8100bd8facd01d9a954793ccd88ac62790f00000000001976a914201ab2289a831a4181b35c6766d72abe7d6437c888ac8f600f00000000001976a91423d129664a7bea4240729544afadd268dc8ba73e88acafaf1400000000001976a914247b512c0fc9633cf0faeeb2b04b7813dede4cf688ac96422e00000000001976a914249d2365a05ea7d8d75a13550deea5c73c07547f88ac1abf0100000000001976a91424f3196f9f078f4bbacd78855fb2986eb17b49bb88ac76ff9a00000000001976a91425423d012cf5414fdb8810fbb64079f1fef190c788ac09eb0f00000000001976a91425622ca550600908571d53f3dd39dc7ab752b0f188ac67670f00000000001976a9142635b4730f2d72d917df911a7f4e3a79a13bd6fc88ac7bda0f00000000001976a91428e67fda013cc759b283fe4904164b1373f6fc3388ac33ca0f00000000001976a9142924f9a0b79caf5348e2f19d1654c3607442b3b488ac01870f00000000001976a9142b23c6fe877e8a09b1238fd75d634c4510be92f388ac23567f01000000001976a9142bd6d9e6ab830142c3be3356ccf0eb6aa198669f88acf09b0f00000000001976a9142c06737871e559ef9306c6e0ab982ffb71404ec588ac649e1e00000000001976a9142cec4af6c393281c7ec85e1816b5233502ff457788ac83d71300000000001976a9142e9ce814270861c79d25766cb9861b1eccfa4b6b88acc8420f00000000001976a9142ea66c48ea6a8811b163fa78f0c979bb809126f788ac47760f00000000001976a91431d3435a3ca1067a795e4ab9cce6ed42ce3d404188ac22c00f00000000001976a91432f897dcb046e05b9efd7dde3ae767d0922b006d88ace8241000000000001976a91435b3c3cd051de7112ea9750d8c8cc26eaaecda8188acd8ed0f00000000001976a914363a0399650c57c21853583a5795cdca0ca3dc2588ac98d14200000000001976a91438d411a1b9a61b8aaf94f383fc21fd6460c9bbc188ac10361400000000001976a9143c3ee9629c853ac3b7e14143f4bb3fd9c2ad89d788acfa1f3a01000000001976a9143f01c7b428954d82e1315e215f9faccaea3a7e9888ac6f780f00000000001976a9143f70a3b4eefc1be6a2d96eff58e28816b0b24c3088acc7460f00000000001976a9144214ccce2574954561ff3928c879a53eec88672d88acd5cc1300000000001976a91442cb831fda7fc20201ad85b9ab82bb8b367863d388ac5fd40f00000000001976a91449e5f33243aba8ec564a0b2294d6122125410f2288ac42311100000000001976a9144af63196a28bff87a9ca0155f538cc244fbbcdf888ac481e1500000000001976a9144b2a41a411a053930d7080331949b84878a6913088ace9dd1000000000001976a9144b64df075b24c270dfe91b25bb69d65290bdcf8e88acc7d91000000000001976a9144e1be9e4d5cae56313fade5bb50c5bc23e2663c788ace0e30f00000000001976a9144e55f62a3b1064a0acdb3bef5476533a27909b7e88acfad34c00000000001976a9144e6cd41ff1270f0d677f1960d62d236b28bc53cf88ac1b611000000000001976a9145029d03dd593db359d3f108b3d0ea2c9184be51188ac2ab01100000000001976a9145126e02d9d0ee40d230750acc9ce40ec8f7426cb88ac346f9f00000000001976a914516b3075ef2eb9102a32905891667db51e5e682a88ac793a0100000000001976a91451aabe60a2a6037d7840d127ac7165b40a5f72fb88ac05ad0f00000000001976a91454e5125b368e4ac3a06e9cb3a1e6e85b191b88d488ac325b1100000000001976a91454fd0c4adee041f7d735d58b051e6cb1ade286f488ac521f1100000000001976a914555bb95e1b604f6c0eb74e019bb38606834d8f4688acbd252600000000001976a91455d1356959c4d0644a265d622637664c5909889688ac34e21000000000001976a91455f6ac98b11a5d1d77d1b3b3cbed9f6983d1483288ac79841100000000001976a91456a8f3aa900a00e9219a891023eeb76ec43b4ffe88ac65ca0f00000000001976a91456b6434d95d9a32eb5526d9985c1421df5175b4d88acef262600000000001976a91458c78ff18be8ac4fd0ef960507de91529bd8973488acf50a1000000000001976a91459356ed030a4de4dfdfbb012c393541b8863424188ac51970f00000000001976a9145a267ba827476c6aa73293c511fa0a1bd488010688ac3a845000000000001976a9145a67a1625aa56c4330b9470f15d98faf81aa4f2088ac876e0f00000000001976a9145c786d41dadfd07d0499cd5b05e097eef278b51888ac8f480f00000000001976a9145cb7209413ea2f6523fbf4f84b57cc8829833e4688acb8251400000000001976a9145ec7f2d857f1afd9124632a124b858c243824e0688ac28550f00000000001976a9145ee548e37275ae3da6a7b807c6b6a6400a5c650888ac8fa50f00000000001976a91461aa4fa568c53da332a5f0d68ea4e0754fe5342a88ac96895000000000001976a914639e63572cc7383360d2929f89e4df31176e89f788acba341000000000001976a91464419c298e0e39fa352957535b1ca81c380ee0dd88ac0f700f00000000001976a91464f341e603d53606b6b7ab78ae4c1465a683890088ac86ce3600000000001976a91464f9f503fd5d2bca70c347ab4ab5a2c0b0dca88588ac29861e00000000001976a91468152f4b36b2b9f990224c42ae34753e50287e3188ac63910f00000000001976a9146873290636065dc2d87be8e68a551284222365b988ac19281100000000001976a9146a02681bc60006ac7111040fcc052a9c53ca45b288acb9760f00000000001976a9146a1b7b7f713aefc005eafe44e4e264ec59fd4dd188ac7b6a0f00000000001976a9146b6906f4cf6c98e63a17a11e1dffd6f682883eae88ac8f109900000000001976a9146bfce040d644a276c7d0530c91dc9a2a91def6fb88ac0c580f00000000001976a9146c0509b6736e2825ef34ccc7adf6adf826120a4988acc59f0f00000000001976a9146c95e32134ef11a94920b0d120b8f8eca08c700388ac78690f00000000001976a9146cc115e22590fa906689ebac0925b3e7e84129ee88aca6341500000000001976a9146d134c6009a87d629149ac18f708fc15459d717888ac421c1500000000001976a9146d577cd0a7ee95ee98eced1838ac141c0fd06db088ac44680f00000000001976a9146e9be5479cc5ae8a4ef3e120e4a0c4f951be1f7c88accc661000000000001976a9146fca8ce153684890ca3d27e17846db04dc90deae88acd56c0f00000000001976a91470284ca6e402a666cd6cfbb69254630cbf7ca8c788ac06450f00000000001976a91470948fb9c98a3495f1c14b9696edb24028f274ec88ac26950f00000000001976a9147135c3528d58b4e8d9e92aa2cbd33d504cad5bfd88ac366b2500000000001976a9147263d0801e840fbf908016faa0ecc6a0beb2605c88ac83670f00000000001976a91472acd9a9f53cacec01d53563c44e6e5a07f3c15b88ac954d0f00000000001976a91473152785bb62803ae6776936ac6e2ccc87b17b2b88ac3be60f00000000001976a91473b288f89b728faf3ca2036ae67ae320e95d50e988ac045e0f00000000001976a9147527fb9755d05bcc71d089b6ac824f7db69f500288acba651100000000001976a91478c5a1f419b6aa2b247462f2bddf3139492605c888ac94171000000000001976a9147a15291be47cdad5f4f0d5e6326279ed7d4845bf88acfd5f0f00000000001976a9147c0daff6874208f6c962a1875b4f3360adb9141788ac2beb0f00000000001976a9147ebb6751dee6aad3a91aa4ec23809a1af9d75acb88ac09ea1100000000001976a91481aec48be53e00d7b7255b079eb4025822784e8e88acc1d41000000000001976a914823c58f257798e1f4ec4a99d3e6862af85860a7c88acdc6b0f00000000001976a914836618e4c0cd262fd4c8dc66e5e3e9289f728b9588ac3ed26200000000001976a91485c41ca3d1bcabc4181c5c84ef0eb3f0e76e7ac988acd0b31000000000001976a9148697db8f3d23af4eea5be02b05e18f1b3ac5e0b788ace4490f00000000001976a91486fc4954e6ed17137c87835c3b130d345a2da38d88ac3d8a1e00000000001976a91488104f788611b1f86a9bde2a800279b97a89a50088accd650f00000000001976a91491ba4a8521302ba297e02e4d59ba63368aa08c3688accf211401000000001976a91493e5b808e0598a9a594ceef0fb38cfc8e7fd536a88ace2540f00000000001976a9149411fe96e90926ebde67f292f41629b54960677688ac07480f00000000001976a91499780b9d5d6fbda78d482f1317be113c2fc2f40a88ac3c5c0f00000000001976a9149a5dc7162dcdf1d1aec4c20d9b3e4f118c1302ff88ac8f231600000000001976a9149cd66186ea1abc4334794e89c47dd531b6294d0988ace85f3c00000000001976a9149f3c9ec24b891e1ab4e2a1391926868b65c90afe88acf77a1000000000001976a914a329862b4e3921dd5c695de2bc91ad442c3f7c8188ac954a0f00000000001976a914a4cd118924b295c9b3c7b2c3040fd9ca01690efc88acf28d0f00000000001976a914a5b01e5d39cff42e20825e677ce27b9d2654d3e288ac80b12100000000001976a914a5ca26b5970ebf5d0bc1d7de2d1629c8a71cd75588aceaa12300000000001976a914a9689485a7070f922f5d5595328224a61c4dc03488ac64a90f00000000001976a914a9782a584de62c08255a869d63aadf256ca3275d88ac40091000000000001976a914a9d3b103e29e79c462a87914db748b7be38ac15888ac43fdf000000000001976a914a66f44e745661a9e6716743e996baacfe58998c988ac6f631500000000001976a914aa3640722d96691f16ccef061f5ef37db0e5313188ac99211500000000001976a914ab2d108852c6183d69200e0b5e7a5230a69b66ff88acd61e1100000000001976a914ac4f7470e5f98b2f382bf596782245747cefa4d788ac88051900000000001976a914ad3f9c6d809c72dc9c5a5feb2309da05dee9b07288ace8901100000000001976a914ada7e91336cdb8f6dc9dd37ccdd2aed8383e4c9a88ac03d01000000000001976a914adc02ad418a16b48758675b06869e96e89cac04f88ace35c0f00000000001976a914afdbd2f33d2cbae5fb681a6070ad1324bdbe287e88acfe6eb700000000001976a914b1759385e8159beda781b32bd8b3ef73068f63a488ac88831200000000001976a914b18d919a90f71466e0d6e9acd615923f6d1d89e388ac04e52000000000001976a914b1aa9b83f1f13b3fd546ef7cb2ad267933ecd28b88ac2f440f00000000001976a914b29389dfdebb8ab22660ce5dc7f8c43e23df16c888ac5e3ae500000000001976a914b39c1c58aa84e24f83d519232c8f4b19df4d162388ac58215c00000000001976a914b3acf3de8bc12f3cd2b9733befb85b330725c98388ac9a531000000000001976a914b4a98cfc3881a7fa80d0c523015abb57ef4c970188acbe4a0f00000000001976a914b50a1db5c6ac1675ad9a5aa8aaecd1b6f11b4e2288acf1812500000000001976a914b6a07e6302e8eba1cf1d2f8d1cabcd62c3ed99c588acf3440f00000000001976a914b72c0e2ab1df6d40b9d1a60dff9ad4a886b1738088ac11421100000000001976a914ba149e41804ec2395065e7884a97743be37c53af88ac74480f00000000001976a914bad9827f4816749fc9ea4ceb4e4723dfd252f21788acc76d0f00000000001976a914bbc448e428d6839fecb38432159ab5054426c94788acf2580f00000000001976a914bbf0ec3a81f89c87cd4f2be81640dc287e44dd0e88ac87691400000000001976a914bd0a615498bb6a410b2015d2841486f2b338083388ac09460f00000000001976a914bdbfe02fce5987af4979db283bcb2dbf166f189f88acaeaa9800000000001976a914bee95a53b9220546b11ea155638377ad2a006f2d88aca94e0f00000000001976a914c1b8bcb2c362eeca23d66a58a8f607637d933ba888acaa26fb02000000001976a914c32293deb65cd20f4f3b86ea16340970d9d03a8c88ac3c343e00000000001976a914c551c81a29a3155d48dd82f24501904ea468665588acb8f21100000000001976a914c572a9b638fd8bc557c3622502b0ea807b133a6388aca58d0f00000000001976a914c585376c95bd6281eaeebcb41658c73324f539dd88ac7ed59800000000001976a914c62b05615611bd0a91eb146806b58e567cdcf02288acd0c60f00000000001976a914c818ccfdd741ea8eab294998de8bab5dec3f438688acb7dc1000000000001976a914c91ea0ac16c20c3161bd8809a8644cf4b654ab9888acd0aa8400000000001976a914c9e6ab854cd3bff4c42d92ff3b98a2be42b1e59588acd6af1100000000001976a914ca165dee31cfbaf69d9b9195c9b9125fa3b9668088aca3b30f00000000001976a914cb60596ad5705bb6da8d0a4f0f6ae490f2c2a4c188ac6c204d00000000001976a914cc3c84aedfbb5580e73785ec9a447224ba8da3f988ac4c5e1200000000001976a914cc77d0078f1915dbe49d9fa39e220c61e903f21288ac935c1000000000001976a914ce5b843315d32efc0e29f66b149a8b27699e23ca88ac9e6f0f00000000001976a914ce66245ace7f9e7024ff29f525a1f5bfd654039688ac0e153200000000001976a914cefedefa5af188c649adf5b017d8d1b5453ddb3488ac975d0100000000001976a914cf8b3dac2256b820c225f3aebf980f2964612f1e88ac93f32000000000001976a914cfc2ac5ba11358e4ad0359db5789b58b6c1fbc7588aca3660f00000000001976a914d0ba05460b145bfdd77e4233d6aeb99d213f455d88ac7fe70f00000000001976a914d3d4df1fec09276128ac17f9d4666dfee914c57e88ace7791400000000001976a914d5536f34987ed1a691d4351110e6039224535b0788ac3b6a0f00000000001976a914d9b299028bbf082a24885add6bfcb6ef3b61885c88ace5291600000000001976a914da816d9b363d4a7b7d1fe5004037d4b19301d34888ac65920f00000000001976a914dafa8e15a4452a1103fc37ab1db037e6d5271cc388acc2f51700000000001976a914dd5584fef1ab439a7865f7bca2f64d62929757bc88ac08eb1c00000000001976a914de3918025bfb4e92186a921372cccbce08d0c15288ac1f430f00000000001976a914deca1ad75e9fc9dc1288caae21f6e4d05278dc2088ac98660f00000000001976a914e00a32c667f7f6f1a7154b45464ebe64b247e8a988ac1bb01500000000001976a914e1d015d15169a3bf15c3a9492b4ec9a8355146ec88ac56440f00000000001976a914e1d0cd23e82794d4ae06328b2336cf5817cbfe9188ac318e2300000000001976a914e29c11748114a121bb9e1748a9180abb72c3e96988acdb639f00000000001976a914e3477bb5a8c5328191f12f70e506019e93c044d988ac61420f00000000001976a914e381a6d786a2cbf685249578b04d63d7b3e7c3ea88acdb4a0f00000000001976a914e52cd54d44d775c07f7061e9796d5bd501adff0688ac36051500000000001976a914e6c174312e3c284bb83821d0f9ff02e64e6907d788ac61ca0f00000000001976a914e989fb3c4e488bc83fe3e10b1439eecb2670698e88ac02980f00000000001976a914eb2376d99f066cf1ac716eb567cd4e026703804b88aca6572000000000001976a914eb9f9ef7357ee72161f4af50258688073fdebc8e88acb04e1000000000001976a914edd0d6886b406b3fe21fabca38462b575db3c90788ac665e0f00000000001976a914ee2e4bc0ca368e3167f80b69c6cf811b8b7ed25f88ac8776fd02000000001976a914ef89c76f188eab7503fd525113e2826a3b3766ac88ac62dc1000000000001976a914ef8a2a1c6557f24786e25cb081cf609401a1171088ac115e1000000000001976a914efd0c55e8ac56abd7da087f45cac46d26e24379688acffa64c00000000001976a914efe313ad9036e40aaf2c5083b17ff59a6bdc8e4588ac8c400100000000001976a914f177196be6604d4bc5879c4c6757e9b6ac5fd60188ac0490f805000000001976a914f3084f1a2ea0d0229e0ec70fc8c7e65c22939f6488ace8841000000000001976a914f33e3c28dcd9d27c5faeda1e70e9b481a98bc3b888ac2a5a0f00000000001976a914f408ce0b353955bdd67b89e5f298d3b51d56289e88acad561000000000001976a914f770d5e1698ef7c262e38c6e60c203efba66f43a88ac4f530f00000000001976a914f7a7c02a0578b6422a5836026fb8fdf7512fd17e88ac40869f00000000001976a914f80e48e7d77ef1fcd75afd5142dd43baac284cb688ac71d71000000000001976a914fb083da5dc317078cb3affd50b26a598ab7f0f9b88acbea70f00000000001976a914fc4b6acb75173e670ad96f7a1a9a7803194e842788ac584f1700000000001976a914fc915aabcff8fc57f461119a4a4ffb8e29e8e76f88acf5e40f00000000001976a914fd261754dfbcafcdd98e514865323ca008a7612688acf1460f00000000001976a914fef69c19885457502569e09f42010988282f219088acfb6012000000000017a91446fe3c45540a8b8bbbe163278077874f2e97319687144e11000000000017a914774b4f3d62be7cdcb2d31d41bdc37bc7b5809b2c87446506006365060000".into(), + "030000807082c403021522a61bdcddd8a2c273e7720387b56e35e258893d73e0e9599ff885a0d0dcc95e0000006b483045022100b400d5b8283ec73035f9e127551243276c9d1c2ff00835f511a90979e1ef0c2402204d03ba92b70bf48e82401fb43d16c337a25a4f8f77b7b418f7e2cdfa93b4700d012102c4e2c7ea9339105861918ff4c615d1725fd153f1716381ac1398edc88710449effffffff994eb397b6c930941685880b696b252fc6610ce476ac51458f9c62d195b5be20000000006b483045022100aaebcf1b17cebee223d8ea214ec764574e599cc10418145ae26430669c4952790220616b605be4eed095ed0ddf97fb021f61fd88100b3ceb0c048adbb09043aa03d6012103a33ccb55bf79f2da99372bd9de4996f1bb6dfe7f77a7eb2a4d3f59d8c2241ad9ffffffff025f9c5e14000000001976a91480501e59c206b6a315ba3d9b58b460d4bda0af4e88acfbab4906000000001976a91418ae4e28f414a29a171a922cae6fe4cad6370b3388ac000000004f65060000".into(), + "0400008085202f89012894c0ef42add1ea6826b5b85bcfbf18ed009d9dbb1fe6c432afef88c6d57a4b960000006a473044022046cf9bd944035c22f8a6f5b8cca3428a3df09ca9947ff60893ab6aca5363c40202207130c9fbf9417ab55fcde17e67faacc8deda5c9275cbe58ecf05ac302f97da9c012102114f54e7b73c390184ba3c6e0c1ce7dd423c8eca064528a8e26975710c04e1d6fefffffffdf501b2401203000000001976a914418841fd3602cf7df3e8e2e6f598e338565372bd88ac24395603000000001976a91428c7f0a005c97be7e07bb16b1c5c780f3fc00d6b88ac9b335803000000001976a914480279f8bc980610cca6b9793813858ec92a89b288aca68b0602000000001976a9141d967ea704e1560faa2e39260019b64e784f8c7a88ac0d01af01000000001976a9140e73a42e0b45e15e272b9f146910a7ef8b9f42cf88ace7c1f001000000001976a9146d2f9be3fd57a45aedd362b59d6917044800675488ace1abb001000000001976a914ca6fa82dad2ce6431513c8e00674a6856d2583fd88ac08b2b001000000001976a914c0dfcb4420129ae64467d2dae056aee111c8316b88acd6260002000000001976a9143426c3e615e17ce3d1c6ebd47ed20517acd32e9e88ac01d52502000000001976a914fbdd87fad747b761b0ed2cd5d7d50df3f0f7f4a188ac5c591107000000001976a91434de621adedaee0f720fee313f954e55c8ee833f88acdea89403000000001976a91428d87590033913e5e1e6ddad642cedf42f12965f88ac92088a03000000001976a914f8c0a5b929e08c6fc7d0bff3174f4c640b3f764c88ac4e7c7303000000001976a9144d0dfb7b219a76d46a46fb6001c8dac120af2e8b88ac391c0e02000000001976a914e4670f5e08bba2c22f24554bbc77020623506fa588ac643b4c22000000001976a91458545a626a1a786fb2f07f4330a4232030098f3588ac654c6d01000000001976a914154dd085c0c84abebd16cb8d391ab72bb93166a688acd7ff9401000000001976a9148ea201e57ced46eefb08d3e6f35e8431cbc7336388ac89f178070000000017a914d9d2a16961cede9faa9aa9ebd09b2fc145edd45e8703c60c02000000001976a914ea8e97d07bdab98df52afd8f6cd8984561717c9188ac062a5d01000000001976a9147e81f5ae5cd021d00aa9d2ec3dc37d3b07c8a09888ac9e58de04000000001976a9146d880c3db902c9c756cb098ae87c47e416c2f60188ac1af2b701000000001976a914038d213aadbcfcd5abc0d4e99aaf43784452e02188ac43289f03000000001976a914379e6e480f53abe778681430438aebb22abf24fa88ace25a2203000000001976a914834f3687ae80cd4b7723277f5569a50f7ca6f18888ac9fb1db06000000001976a91458f4407e9a9021ffba1e4a1f493a0462672e410988ac10989202000000001976a914e9bbda71c17016bc043b3e4bb6a0cf2a075f5c9388aca51ae103000000001976a914a8a8fc2361c0b5e197cc4b4b9976c2f38943be3b88ac9ea0cb02000000001976a914b29d138b25af1b9d8ea3fab08afc2aa9c1370e3f88ac90892702000000001976a9146ebb866016a616f3f5c2ef764f45ca89943f1d9088ac5d3a2803000000001976a914aff6edf074903e94ef2ee4bdb96ed4bef626080c88ac06e59601000000001976a9147f4af16accb1a9aaeafac50079a3f1086ad96dbd88acd953e005000000001976a914461d577de4b2c8b9af9dfcdb1cf184740b9adfea88acc5c20802000000001976a9145f03f7266c23a84ce4a3548db89d068d50fad70488ac1bf57b0d000000001976a914dbabed7a0fbcbb9492d3c6f05b76e2107a8eefe988aca86f8407000000001976a914a7d4a9d7cd1aa121374c0795670dcffa0ab08de888acc63e6502000000001976a914729f04c9b26fe2c6a9bfa2bf38478a586df0d3ee88ac8eb6d801000000001976a9142a43b9aad78a13f0869cff7426db3d769a7a224f88acbfdc7301000000001976a9146083e9e63aaa8ebc13209386734d912b44e0f4e788ac973ae101000000001976a914c2cc96a441f1c963ab34684c46d573f87972b07b88acab31c711000000001976a9142e30fe12f06dfce8b149366e71634b52bf000fc688acc87b0d03000000001976a9145d3742dab8fbaf367284e0b4c40bbc412aaeb6f888ac6e865f01000000001976a91426647a36c26cbf968f4f0315092aa7b71fb1408888acbbf19901000000001976a9145bce8570386de82a2503d89250c796033cfd07aa88ac04222602000000001976a914fd361619c16363e4a25313e83c36a791582e248388acfe2e7305000000001976a9143bb5d1bf1ca6b0cecac4949c4ee3436ab6f0a05d88ac7c9de101000000001976a914a8a17cfce57cc18960e032b853f6390b71d71cc888acbb7aaa01000000001976a914dc567a56fbf0c62f1dc5b9890eb93c243c504ec988ac49842602000000001976a914b64275182a8eafffc1c541e998629422d63170f588ac88f05701000000001976a914cbc3955eba62dfa4b0e2d07a80cbee09822ae63f88acced86902000000001976a914eaade91e5f2ae2b658c317c689a3d55358a8acc988ac56d5ab01000000001976a914c31983fa0c07c2c0d1ea21dde3932fd0288be17488acd8f5a601000000001976a9149e98ef17c5ce9a4e053e72be7b34ac1524de991188acfb712822000000001976a914eaa87ea78ae23c146b0317231a3b16cdc2037f8488acbe898f04000000001976a91465e4682c0c38bb9381ad85d777a6a7c0c94a07e888ac8f356f0c000000001976a9145fdb43217d40afd0682fe81da5f5156e2b1b901388ac9ea5d404000000001976a914e627a800f4b11992f47660e2effc7f6abdea9ba888acb08fbe01000000001976a91457e649414295e40b86b20c5643753609932edb6888acc83b5901000000001976a9145e472bbde322d1811e18bb0558ff1b46bfb60f0988ac9ac0ee01000000001976a914569e1fb1e59cd3dbd30e7153482d9f6217c41e7d88ac1b7b6201000000001976a9148dd8353101eca4ed0d256b119264d044e387e08388ac91b7f207000000001976a914192a5bb07666cc7c1ad90421cfca03978354177f88ac5f3f0607000000001976a914fab9ccc82f763ae2f9e4d4771854e40f9959e15088ac36421d12000000001976a914ef2386da03dfefd4743074993b892ac605e6f51988ac4338c401000000001976a914483c04e0da800c807e0999c16a96af879a97f03a88ac9c0ea703000000001976a9145c05a9e0f7a03bfb7ef3939807f25910cd4f0a1a88ac6cf6e30a000000001976a914cdd8b1c40d063a96b90dd49077a510b34b7d691688ac11ad8202000000001976a9147fb9363905cd1394b84c91a7880818b6f56a568e88acca352509000000001976a914b7bf86eda5bf2d5063fe642eedc37f4a3fc5270f88aca27e8a09000000001976a91469c270fc46a1756bd10a18c76f228f3e4b2f4b6f88ac57f32506000000001976a9147f591216d7379bca93c3702d80e6b04f5be8cbcb88ac238c4303000000001976a9149319ebadb555bf0db0a34e95911ac098f348572088acc06f5908000000001976a914312dd5f0b410b40b0b957e95bbef5fa38c5cd12788acadf5aa0b000000001976a914bf080733221ee6957abffa61810ade4c2e94576888ace12a7a11000000001976a9141726853f4457c08339141e531682abb172d7d12088acc5693a06000000001976a9143014600c690f4079b7c9c40706d21f6b1fce3d7e88ac4e334a7c360000001976a91475206d4839594467bea356d433def2ec887e59de88acab119b02000000001976a9143b5195a70226963bee5c88de4c317adba8bb317b88ac5dd66301000000001976a9142919ce4776ec0d18e58ca465dade0e484104ffc688ac3f391503000000001976a914240d7d76ec47f93e83fef49d5bfd3a9d1ef7a81488aca73f7201000000001976a914ec99b473cbc8c60c6e7b09aecd35a0e3d429ad8988ac61577101000000001976a914d07fef54cc8ac4f6e9659661fd4640b02836e5ff88aca1b66b03000000001976a9142ee2c6a15391bcab5c53db6e3322828df6cf778f88ac7524b70e000000001976a9147a38471b9f364022061c19e7d61ba3ef17d22dd288acd3a3b301000000001976a914b3396307a616559f9bc560b4a7a95c07d093357888ac25ebc50b000000001976a91435713cabb1c1a88d0d53e51a62bc13e3c86cfdd188acf441ff02000000001976a9144717d5dbcd29076c7168331917e0f4ce97f6c04688acfc067401000000001976a914f16d5ada3bd7bf642664b03256b8d82603976b0888ac1afef813000000001976a9148ec556ba1a9086d10482efb1ec34da9a58d6f7e788acbab5e002000000001976a914bf9b94ab9a5dc453acdf9fd1488b53006f7d45f488ac6144a201000000001976a9147972628a1bc244676f7781d0cff4e293ab5884df88acf48cd609000000001976a91480383a56dc7913bbf2dc5c9e3dcce1aed5fb409688ac998c3d02000000001976a914dcc9c29ef68531c88babfa49ace2ef46580c78f288ac0694cd05000000001976a914fa8efbb448ddd85b629d08369551ffbf60b5d8f288acc4149501000000001976a91453b1eb90495613089bbc62117879cfdd6a90a04888ac9ddf8b06000000001976a914f006da9beff8af781345674061980fe846ab1e8e88acd8b28711000000001976a9149adc3523f1e7b3d938753458f868dcb2c1db226888ac5e373402000000001976a914578dbb0c3955d0e5b3c0a810cd457b1864f143a788ac22a63e12000000001976a9145c192699235ac72eb869df75979bcbbb9e716cba88acad4a5b03000000001976a914d023fd44c787cc5a902b01ffb05e28090030661588ac5c1ded01000000001976a9149f438dbdb0ff1fe7bab5d501cfa01ad9a70032e988ac0d63b603000000001976a91498e75d639b3e663d903a6c296834aaa028e5372988ac0744cd02000000001976a9141162a9391516108ee210c818571f20596c7633ba88ac994ba402000000001976a914afb9f7211c1d876ef23347175f9418ea2c7283b888ac1d36a206000000001976a9144c0d393ee179cf9e99f70d3596e322389e79df0188acf8016d01000000001976a91417bd93cb4eadc01c591985202dda8e355401505588acb753a6ec000000001976a914a1fc1fbdd78ee407d72d8e273a18df43ef9bc55088ac036c2704000000001976a914aa3d19eb1399d3ec2da04ca7595619b28ad6e10188ac9d2e1e0b000000001976a914b21590c89995985a12aa3759228f7699d1f2b9f788ace1728d01000000001976a914f5d323b78fa6456dc1a6685b0d29d9ab3869473c88ac84aa5801000000001976a914f3f870a263b16da8686352bd0d349f294d6744c388ac0315a801000000001976a9148d3e5a3b770764a92a5ceba072940c7db2f74a6088acd90b723c000000001976a914352d04718845312bc8a16d1a354c56d8a3ec4eb888ac692a1503000000001976a91493204c56feff9170e5b3283791d31582bba0e19388ac7df3e003000000001976a9144dc263760aa3226e5a006863ee6f6290634df51388ac17ada154000000001976a914e8ffc4992979e52d426a1b5c1a6d3af769ecfa1288ac28a2ce04000000001976a9149f9e8d89d7ab3a4179174b0f280193300e16d31b88ac96444506000000001976a914d390e654d0f926cc5e5fc15cb3b3eebbac567b1888ac4f7d8418000000001976a9142d1af117a52863061429cd9bcede6f99fabcff7a88acf2d4fa05000000001976a91475928eb1002fff493be03e0add04e47679b4fc8588ac96b99501000000001976a91494f66de3304b99fc0edde7b9a006fdfd58ba283688ac29b08701000000001976a914ddd7b385984709d064f36b4e620db7d6ba58923a88ac0cda4306000000001976a914970e31b940e59f0c8120d3c5ef57d458bd4d0df688ac2c0c6f03000000001976a91496580646005e58648a1413907cc7342bb60ea1a188acccae8602000000001976a91464ebbd81e6f89a4f537ef87fa2cd77a3ec7b14e788ac31ee8d02000000001976a9148282bafc7b0e34e50b717be2ce33c5bc57c5650788acd43afc07000000001976a914541ff0757982133d39df82360116b6c6bffac9a188ac365fce02000000001976a91443bd338ae81eb5b77611e7f6f379b6046338369988ac65152103000000001976a914956ed6f5815510da0a95078888402538594d587488acaf738902000000001976a91430a45c9d6fae7e674729d7a08c02184b190dbc6388ac3b28d801000000001976a914a525fb06f91beb46f85f92210a280f65183ff49e88ac5898b101000000001976a914a239e5d35a28e8c706c1ec60e217744cc0da008788ac78fd9501000000001976a914ffe6906a70d46f723784b7c92860f4cf55a4461388ac943f6d01000000001976a914bc08dd59ffa159c3be98a0936f1a3cc5eb43b73f88acf1dc2b22000000001976a914670c34092e288745190afad98217e139c4a5fbd988acaa240402000000001976a9146b1e6bd7e815d1be3556397a1301f20a158d0bab88acc70f8401000000001976a914a23a8abade85043fcec2f52f036d82f0b2ebb83088ac6e7c9c04000000001976a914cae108dc674e91d540dfe8a5aad1fed4cf3febc188ac658a1702000000001976a9142eaac41759d814e86920bd0c985073cb181de35e88acfcd3d701000000001976a9149889f1bde33c08b67567bb40e611efff9c1ce6ac88ac14a56b3e000000001976a914bbeebae27f9ae70435ae35e5eb85202c196a71c588ac5df11f07000000001976a9146d68cc7c0fc6599b75105f0f29a37df4c84010d988ac80085f02000000001976a9144393d4d938241835cce09405f32487a2b7a04fc988ace0d63f02000000001976a914954b12e4823e58dd4c2556d063842b69dfade8c288ac3d301302000000001976a914169c010e553bd6332fac660cc20a882f42b0491b88ac23dd4b1f000000001976a9142289e57732a4b9bc8517a1cf62fcdca4fa63d50488ac3b1f9601000000001976a91450643610a322a5f9c3e7f04fa7750749832dafe488ac280d5801000000001976a914a363916cdf055d7d36ed2671b06037099e620bdf88ac02020003000000001976a91419c5d30ab9f35e7b61e22c9234289ee3e18b03d188acb6f5ca03000000001976a9145ac1c615d52409125e30356f61eb32d3f2a06e7788ac6081cd0a000000001976a9147d9ce0a8554d1e42a3d5b3e648bd41d51eda526488ac75416501000000001976a914c0bd2512daea0db489eb404566cddf37e8a33ab288acaec37f01000000001976a914c70f67747e64dc8fe5b5d964894df8ed96d213eb88aca5180808000000001976a914f3e69de2d2f00621962540c614153582eb7f661888ac41b74604000000001976a9148bd7ad78071ebb26e3bf9e0725f90a955523974988aca776be01000000001976a91461fbde05a94dfdb362aaa55237aa40695513ce8b88aca1e29f01000000001976a914b43c8c55bbfcd703874c58178393041a98ad7df488ac0f72eb36000000001976a914f17162ac4c750d8dfd0b4bacc1122e35678d757588ac0f730004000000001976a9149f9d487694c2c9beefc933fb00604d1ffdb39dc588ac62bac905000000001976a914863dec0f5b2123a1f6b5884571e815a076e2288488acfc984e06000000001976a914903f5c711569499470139ebcf22229517fbc2b4e88ac9f8d7401000000001976a914921dcef2cad253d4856c428adccf4abd2b36ced288ac6cad2a05000000001976a91437b6d2c348b1da7fd8e39b86aba652f1abb758be88acd72ded02000000001976a914070285a0b9536d9fe81b5efa4d175fcbf5306b5f88ac8005b602000000001976a9144cd268838f7b844f0c3dd7015410baad7ad6e9a988ac9bbfae01000000001976a914bdbf706b8367fc0da074b3009d1dc42f4623273888ac007fdc01000000001976a914121a36379c2f7410345d18246c97ee2b6687a04288ac9a1c0004000000001976a914e8c653841473ef88a284d7506621eb9be1bf679e88ace8242a03000000001976a914f57b69bb2006e8a44e97bdbb8b231a081ee0954a88ac023da501000000001976a9140477a55dc1195c8e9b954551ede3d241c5e228db88ac8fe2d302000000001976a914ee2c21c8d4a5e8863ad4d558c4bb4da2392414ef88ac13379502000000001976a91411a079f2dd08d89f273eb204fd920ebc5eac68bd88acb0a0a903000000001976a9145ebcd0610f2e582829b4efb4b6490fe5153c463088ac399b762d000000001976a914b46e215656edd12e62f0613af126a6cb6fcc67d888ac74353902000000001976a914d9487b322e40ea9a3df23511b05ac896f767b30788accfc0c908000000001976a9142db485c0412f7eba4b187da797d41ed61bd859f188ac53129e01000000001976a9145e631f5120322118593dd6a2643f7514e600901888acc69f5e01000000001976a914c980ef723ed0ca619a1624c6a962cc72aba83a2f88acedfa4802000000001976a914ab1ae074b7d5fb8d2be8b67806e2a33dd1594ea788ac54e80e02000000001976a914b65c930320386da8dfa2504edd49b27539a5831488ac73be2803000000001976a9143922667a97479582c83b9e34a199a4f658428f5688ac37420e02000000001976a9143f33076df5b772c1d38f50443d06be769822227988aced401502000000001976a914132b069ce844d08f0d43e5cc66df5956c9bfc0eb88ac8ab1ed01000000001976a914c16562add15cdc273be6ef8ead06692b9099920f88ac198b8e01000000001976a914039ebba2df6da69df52e5ae827e2e92d3df5c39e88acbaf99a02000000001976a914a6436a144045a17a2b60b9fa94409613f08c210288ac63d4fd02000000001976a914b63a4dad61db4098148248407948b2d0ac4dcf3b88acf5ed3102000000001976a91493850c28f2cac283a41636d38d2e6f9ab0ba4b0488ac4d594b03000000001976a9149ad06dc734cb71cc319a4fb2ede60f43ef90d76588ac2550780d000000001976a914d2a84ba3894e524946f5cb540c122382e1065fde88ac8c970f02000000001976a914226eea9d11449fbe6b18cb2d4f2d3aebbdae179988ac8fc6b301000000001976a9149f9dd1b32ac7bd1eddd64d250a38e21c96f25d5f88ac3d695003000000001976a91480d9a605957ed9cf1be35e23e6f3971b7dc4fecb88ac9ffef303000000001976a9149f0b4cfd64e900906ac67d6753da91a9a504562a88ac1ea41704000000001976a914570e8a4f5dbba6afe17cba4930ad6638959cf63b88ac91117d03000000001976a914c4ca3c8ca81cf07b29e469d36b0e0dcd4d5a410f88ac7a160702000000001976a91465d8780f0dd45f5eaa83800d26a4b7467cc3694f88acbb8a1806000000001976a914e280619b2eb95bbe7ce98eb0796706995cfac00988acb5ab0502000000001976a9142ada8d24ecd968abc95efb2ed233ee5100e9845b88ac6b495f02000000001976a914db3ab361234a07d6f162dc45a3733db1328ebcee88acbbff9001000000001976a914bb8a116e51d17a920e6bea9609fad47cb857c78f88ac5e1c6901000000001976a914c1daa5a5716f15a8acbf3c8114f954f6752d997088ac07253106000000001976a914bdfd29e35c6813a1b6dd4bb5421a76435138b36188ac34062521000000001976a9145ce689401e8e45e305b9e1a0de69c8fdb804632288ac9a647003000000001976a91461fb6e39d66a1745d7fe089fe4fe1385b19d491188acba1c3234000000001976a914e4a8bc5388ab9b53afccead02f2e8dd62ee370cc88ac53271103000000001976a9145c45c97f03197dd0e9b73126b451db2b64862f8088acfce19006000000001976a914f60384efb6d9b51fee029a282b659a5818e1de8688acf1a48803000000001976a914e050d82eec1bbc00e4003f8abec565af2902216b88ac71fef001000000001976a91415899b64bec1c3be766b08cfa3aa63eafb495f4788accae48601000000001976a9145475bbdf9496ccb9eb4c0e192309861f9bc0a7e488ac6511ee04000000001976a914663734caacf23f39635c77e008ff6b8f2e1d23fc88ac5fcf1d06000000001976a91438624c1d05b17481aaf1217a097a0e08556428ed88ac71e969010000000017a9144da447dd03a52c2071a4d7d9ed7f3917aadd918687211f1403000000001976a914c0820df323bf016f286669c8fa1c0d2002fcd20088ac8bb50806000000001976a91430bd0c35cb751445a15d775ca5567ae8ca0ccf4f88ac1e007401000000001976a9149041805c7b734b2b30728aaa1fe27788cf1a688a88ac31731e07000000001976a91400949f9567ceaf5c6cbdb0b9da76a6a3e184905288ac4ae5a904000000001976a91449751eadd640f582122948e98f8f94b8f0c3acf588acfda7ea04000000001976a914e3e3cd8d736e50982b82447e97fc826c1a4e58d388ac18197b02000000001976a9143d6ab5455bf93aa3128e3ba37c3dc4314138888b88ac1f8daf01000000001976a9142905ae99619c7c518d38860f8910fff8f563fd7a88ac1b258302000000001976a914f1ecaa24402ca993a7ec24f90c8d337224c196f888accf819701000000001976a9144594fc81aea8a26d4944ac67ac600b13a0de8a3e88acb31f1a42010000001976a9146593a7b3c4281760fc294a6f1408e6dd98f9b1dc88acfc8b8b01000000001976a91488e50397a0f53ce59e7de1a7e1697b86c1d4c0d388ac73f54602000000001976a9148bfe72099af6f2279d4073c76e93c80db49deef388acb26a2504000000001976a9141e4fe9375c8f0d93bfb9c69ca909714397781ce788ac6ae3ef01000000001976a9143c0ec1336c9e2ddb0122e0628c84179d393342d188ac56e10f06000000001976a914de893aa8eee6a8a8415213660c5fb3f9de74d48088ac17d17801000000001976a9142a54f07fe56973ca1d5d00c18f023be75593f14988acdcb52e02000000001976a914b4cd801246cf62b27ca58abcfc4c2dea992bff5e88ac44d4c120000000001976a914f1273143fb962f1b71983eb691a6d4ad08c2563488ac7849b503000000001976a9146f6b7966edb8c62fa7e31959a2a66c946eb836fb88ac229c33060000000017a9149c0d68ccb8a407ee0535cdd79ea70a27345c8b5e87048abf11000000001976a9146e88d3c3ae0f8a52e7e8a10d0bffe5e56c78826c88ac19b5c901000000001976a914a1644410d4b08d103321815200b68bdd17ed967c88ac14491a02000000001976a914e1788559e4912dc70e6954a0cdef336be56dfc0488ace5f44706000000001976a9143d0a824b276b2e8056698e3c99324a1b7897a27488acb17eca02000000001976a9142a065ffbc4aa4a14104f05463fa51249889c2e5288ac1fc2b501000000001976a91453757c1c47c78c6a4786cc934e24c9046319584888ac1452e903000000001976a914e5410005fe09917c8cb6efdf57708b001d44f42d88ace1a5a101000000001976a914c3019872a6df744ebf10a6ed3841632d25d1566488acf7327f0c000000001976a914b78fa5c9175b0b0bea8e47e55bd4fe14d2f56ba088ac0fba2005000000001976a914e26029c4dc113b6d6292ba029acbd38124d11b4588acb8aff704000000001976a914333d85b037996fd8cf8a71bdca0404ca00d8d57288acdbff6907000000001976a914ed92422b9ac97def1eda45f18999e675e6915fac88acc3b5ba05000000001976a914b40c4e756b7c5926bb212f71be8ddfc0db37805988ac7c4b4f03000000001976a9143e7c6c2bf78ee08cb8c63eeec6340c65db56b5bd88acb6195006000000001976a9141bda2b5aa3903ba32f7a36eaddcb40d2f97ab31d88ac0f529101000000001976a914d634bb5efa522e0a5db17f510792dda9ec3c26a188ac878ee401000000001976a914c19c0e4464c426b81e2a8e3009e66a7fbc253e9288acb4a82506000000001976a9145abbec34946f572e7d8b3cc1f7f0958b1b4e851988ac0950e901000000001976a91409fe8d6c4e2a1ac5c68977c1f6276391bf5572ef88aca9e1d602000000001976a914382b6ee7c648253d4370b4367001f32170aaebc188ac687b8d01000000001976a914d45f9dcb5eb1feb2747eb457b17d5beeda62658288ace70f1102000000001976a9142b708595c9ba189d74f59d8a3226167ce031c1dd88acdbbb943c000000001976a914dc72373a269c008d2627e7e4f2c0c24612cd762c88ac5bc08607000000001976a914c552cd5095bdb0fd1a08e2c766ad66e40906bd0688ac646d4802000000001976a91482e5bd3a928aefe49c493ab589d265f2a1aa042c88ac63e6d602000000001976a914c9a40b03ffd67725d4868e3a56236535397759ed88ac78f59901000000001976a914d571ba9e9ee4b198c3c6ae4d7b6d1491c26d5e3d88ac802aa103000000001976a9141d20ec7b1088787a0f18e6dc969f26462d588de488acbf0d42030000000017a914f5a703f1985548aa1e78ec52e0d753fba4a05a5e87fc576e02000000001976a914c1d8950aaaa11e2d60d2269fb0fe5cfadcc82b4988ac38888507000000001976a914f016fe702c86c7f59770a27beb712c806349766e88ac33e8d101000000001976a914c5ca9e8986b3c131aca38b238b2475c4d490941c88acdf748302000000001976a91488ffc5eeeedbab5423598d853cf1e75ace84fe4988acfe3b7925000000001976a9146cb18821cb8a36d695a0f5105bfe11106757e01788ac4e72d701000000001976a9146904d2fce2e0e5dee4fecfe78e288a06f7a6ebe488ac35445908000000001976a914ada4a2729573d97ca99df7fdb2d04a738614101e88ac40fb0d18000000001976a91490d8631221504737e4ee554d7f2e762d6b3fb13a88ac74737701000000001976a9144611c5efb76c89ab8a59cfcacdec07ae587ded2288ac7fac310e000000001976a914b37af9b47f25e5a6d740b31906f3590946817df288ac0039c501000000001976a914e6172e2bb7c82327841af4711cc3d9995b3a46f088ac676e8c01000000001976a914f31f62e918f4e1b23f67be9aaa869117d537653088ac72270403000000001976a91410e97d5a54ce4b26c149556dce07eb2efb1f72d088acd8376715000000001976a9142b3e288a550c6565f3ded3dc6e61191a13c852b088ac641e2502000000001976a914e66b76901805aefa63e08c2cfe90668841adb11388aca0c74602000000001976a914d752052b85948b6dd1a500ef826ef1f5e81e159588ac0ca0e102000000001976a914fce69f1660975a2261685b24cb3e5d35f313f90588aca8f14f07000000001976a91415ac20ace90c93a5b213027c3943c57a6abeff5a88ac010f5409000000001976a914a0ec9118133f2b92cb9020f99caedb0b56c1af5f88acb0e21402000000001976a91488838d5e1894d8e1b5ff890ba7517c1e806ae74e88ac854b0402000000001976a9142278e3c6eacb76b64638063464b9050a31f45d8788acbd167a02000000001976a9146d17d3934f6581c89eab4829bd2471ec2dfef4fe88ac21984604000000001976a914fd233fb3472d47db66f3af4830a87937ca8955a488ac5c520403000000001976a914adff9dfa6040e29207ea9c15486ae71b6bf7c1f888ac132b6c01000000001976a9144f8a2ff488f41535e507b6576423739feb70d58388ac5e336801000000001976a91423f1e8c6921931e373693dc9e7e57703528b524688acb6e28404000000001976a914f6b6e2e0bbc1cebb7caba85c03817ac8701d00d888ac47046704000000001976a91479234b57b7c4efa88c2ff9599b0865cc231a90d488acc4c4980c000000001976a9149fe248e542925856009ee1a314138dac6f601f8588acf0602302000000001976a914434d0b50bb1135e84d33d7f08f3a765b21d8d74888ac10e5a00c000000001976a9141e16c0ec7fd7fe63a560bc6bc681533214c3d52888ac290cea01000000001976a914e5d8eb2e46196be3defa9267b5f416b70c299e8588aca9a24805000000001976a914320a1a6e87e3e2037b786c3ef9fe2620c832e25488acf3fe9503000000001976a914ebbe4fee1b4f0c17c864a9a1b6434f2fe48cfb0788acc9fd4e05000000001976a91432ed7ffc6dfbd61f9ceadd9730faebfd734239be88ac18289d02000000001976a914f7c808d47952825d935084121dc1176245be75e288acc7f97108000000001976a914e689da4dfb082a8829311c4b4d543dc16301130e88acedf27f01000000001976a914c490c88dcb4bdbe0920d74652469cb047ed9e05e88acdf5cba02000000001976a9146a462b620f0d3f950fec2a3dddd34df2c1fc044c88aca8459a01000000001976a914d0a9ccf5cc41eb1837eaaf297a929ef4f4d127ad88ac9450eb3b000000001976a914287c714b1ee5d86c0ca78d400bac7308234b1d4488acfb025003000000001976a914e11f5f6eec9d903a1f0babe8e17285957828d5da88ac055fb401000000001976a9145a860447ce11b0570e8f41299e954ed5c18f59a488acf651d902000000001976a914b9c29bb285e96007b55cf679a91512fe4a49465888accb318001000000001976a9141452742c8a9cc9f603e54961206506579fdf1f7a88ac74845f03000000001976a914f310f67812d1c5fd097c75752e437d08e242d65d88ac17545703000000001976a914853fc1896b13adfaa65d24f4fe120fe25d59e91388ac58a9cc01000000001976a9148a2fa21c14e1685c2e67e770a1381319a87981bf88aca141a209000000001976a914235549e20c7f7a5718a7d2c48d6c565d77e208d588ac91825f02000000001976a914f55e89223539139237e662e3993cb44a2e1a690b88acf78afe03000000001976a914fae2fb0f07983ea08913e65af36830729f0e285688ac67182d02000000001976a91473f18376973a3a22dbfe93bcec9063759437079c88ac9eba7801000000001976a914c46696293469f3d5f18dec5277683ae77803606188acac876101000000001976a91499ba8e8a63317bb25b3fbe2f4f81abca56dc984288acbc1a6604000000001976a91495692d7f95965a0b692eb8d62df1fb23976fad7b88ac78ed5102000000001976a914e934fb3ca2babd8e7c616db2af87c4d6b0d28e6588ac91010307000000001976a91459b3ff96dbb4435497471d375a274d06b5b26a3088acbf104706000000001976a914e6e3e2f9badac85b85c258ece75333160ed48e5788ac85726001000000001976a914fe3baf987506dd86d8010f48ceda42386589c23688ac01ff6f01000000001976a914d48f6e656600c633569430ea2cdf38a860e7d46e88acef9fb50a000000001976a914d0afdbb5b0217178cbe6b9f218fa08948657e74388ac864b4d04000000001976a9140d892a0214a250e8987594dd5c0cec9dd94fddfe88ac987b2f03000000001976a914b04267460798077381a737a5cbd8f09aa3b8af0788ac653f2603000000001976a914a77a6872abf12fd81fcae505c92e6d3d6a48107788ac71f97e01000000001976a9147fe8e0978172c2e50ae9455462922d17eafc9c6988ac3150d902000000001976a9142e681e7d3a60ce87d39b7b58eee04895cfc7a12088acd3a7ce02000000001976a9147a4a1538ad200ce1eb434f7598bbf4f34211b80488ac52499103000000001976a9145b2d5d5c461db93aed2f7632664f88a2b1c1e69188ac9bd86801000000001976a9141a6ef54e75f8c4711b285f931a5a0c38da4173d488ac71822703000000001976a914ab3b57a080fca006e00cd877a4f8105c53b5d4a888acb5953d02000000001976a914480b8327fc1eef6a51aa7a62a6779046295b6e6f88ac1f899c03000000001976a91413484df57050fc66ef8b7ab7fca3ab53150baab688ac7ad78521000000001976a914dfed7bf9ecbbe8313f00d0c395a9481667021f5b88ac7a518d01000000001976a91422040608af61e64e1eb931f8cc06b79bc15ed2c188ace9186e01000000001976a91496559330ec6b63b22ecce25b5f093fd9b80b85cf88ac689d7601000000001976a9146f8224aab872aeb254b37066b7a5066f4961666b88ac48c19301000000001976a914d192d56ad6797d4a34ada0ab30e79e6c3a8b7f0388ac7aad7e0e000000001976a914dc5696c3bee6e5c4034bce4d86240f5adaf43a1088ac24e2f206000000001976a9144530579443271a9cafbfc1af102dd0d9d42601a788ac48d43106000000001976a914007d6d83fc46d58c6e06315c762e27eead2381b488ac7762d905000000001976a914b35b7352fe46305d06762e3c62da3965844c9e2788ac79116501000000001976a9149d433981bceed2a770a7376f7dd7ee76ac3de68788acb60e2c02000000001976a9144194dbe859fca391998cb1c950f2978c8ad5777c88ac76e3c813000000001976a91423f64b2934f7362dcd6e5eb0ddc5472c4e3ab62388ac2bec1306000000001976a91473cd52549213ac5ff901f08c02ade3cd636f2d5388ac37aa6501000000001976a914baf48fad5903940b72bedb2b5f522538341001b488ac89d21008000000001976a914264565a8bcb6cb01e7190a39e31a55bbc70dac6488ac58badb01000000001976a914f280de5885f6b1989214ccb6e71a5890d2628d0388acd94f7401000000001976a914a1fea87673a3762f8191d3ff48455b616ebc419888ac5fb1dd1b000000001976a914f80accba243877c07d6f973c0ed3fb9120d6e3dc88ac76c5b201000000001976a914d0a0495e4e45abed38b513c498c366e55951c1be88ac05dee601000000001976a914d99f12f3772f87a57ca2f39ff582446b157b35b988acfea29749010000001976a914360bf771e354c1a4e4b337cac2bf2b3b232bd12888ac2d682107000000001976a914320ee55fad377035c8e553d78e95da56a8deac3f88ac7a8e0c02000000001976a91425f4d5beb86e4c88e8ff7dbdd98415f8e1d21b7888ac3ac46111000000001976a914e053f013cfbbb85c5243d28b0a120ed9ff87398e88aca1e1b301000000001976a914b2ad7495edaeeb4ca47168f79851519f58fd6f8988acd26cd006000000001976a9144a35a13f909607145ebaa7a37c2b0268563f661388ac820c3606000000001976a91443ec56ef5b1fa810b6345fa290bb2e419343ee5a88ac1006f601000000001976a914707971c9144518ea3938575dfc2ccfb67f20a3ad88ac01e6cb06000000001976a914595a5bb8e17f8cf5224968a886dbf2101e996dcf88ac18eabd05000000001976a91450cd3bdfcaede714de329746f1df45c2fc4dcaf788ac4bac0302000000001976a9147050974081c46f8265776524722da49e5c6a0f0988ac5c84ff02000000001976a9143041363ee5f18acdfe32fa03cc6361bc9754c58188accc8d98a5000000001976a9142e9020e4a4258431ea87d4636bbb932ea386e8c588ac4a4b0d02000000001976a914637e46061bcff20b4917e97ce34b6b15daa5991588ac527b6f01000000001976a914c3891e9dd098630ddeb4030dce374155c008c18988acfc3b5920000000001976a91492f5fbe393fb947a98e8284598df9036b5810e1f88ac93186b03000000001976a914437130b085c2ce34b711c4056642214291f7b1f788ac62b76604000000001976a914e2f92c63d90f9fbffed77d510289b915516fc28b88ac195cda01000000001976a9140e1fa8260f810aa6274243cf632d31f32429dbc888ac958b7501000000001976a914523f00d5edc1f0fce708c0b7256ea554df8aabcc88ac7bb20a03000000001976a914117a0c75fbb952fae0a7ef96f15e8e658430149488ac27e37407000000001976a9143256891b2e4ff39da258becc4b9a4413ff9c431088acff165227000000001976a91462c702f9544290d660b291cd4794cd5b9f51e66988ac4e9bc302000000001976a9142be056892d1b7cc76def0db9e43dfc279dedc53488ac82830303000000001976a914c8eea8e03bf554498cdaefbad6244a425c1ea58188acbeb60202000000001976a91405d2ff8fe0114a15c77b23fb1586055c474ea14388accbf03107000000001976a914106362f0372ed416320324841d6e473116ffaab888ac80c56302000000001976a914979d9045e896a3601224b8df249bfd02d724823588acd9057d01000000001976a914fc2d2ee9e9087e77aa47baf797d92970055e4a6f88ac8472f0040000000017a9145af7d290fb6c30cc14dfb6f9c0600452ea81565d878f031c02000000001976a914f7eaee96344417f688599b816743c34a681ab7cd88acfaafa801000000001976a9142ab7deed3c1b043f3739b351666a4415bb3cbe4488ac2b4e6801000000001976a9143a77c30a8f5a33d80d67bbb215605b2a557b315688ac77117701000000001976a914882d62697dc2d41f2ff25a3e2243690efc29f9c588ac24c75801000000001976a914286f0258c5df8b43c6e7223274966b70d4afe03188acbdde9b03000000001976a9146b49a3aaf0ef871fe963360867e69b4e5e5afdb888ac13a3c601000000001976a914e69972b6bc2d4cdfa430476681a525b2e3236d8b88ac19f6d901000000001976a914a433c232e1292c2c909aec0be5e34c1b2be5c50288ac66392402000000001976a9141fd0517d1e936d57bba2e45eecaf4c36f33f06c988ac75700d03000000001976a914c3fa27cd62c91e89ffb21b66c9d1d45ce5005b6588ac88fd7d01000000001976a91457cb3a15f71511155e04aa9fc8fc7bda4af9caad88ac2eb9d102000000001976a914666fff6a11a34492d3aae092edd531dac31d623988ac3a938807000000001976a9145a556c9ae60c24265480d41ae80b7bdfc221af1f88ac2cd44702000000001976a9149a30b06efcbb8f42355f2436c4d85bc61bca336c88ac2ce79601000000001976a91410d58f24616940c8a9808866e406d725fa3790f088ac5dc30c08000000001976a9142328724113b60851e1066e198e69f320bd2c753b88acf17b9201000000001976a9145e6c84b60cd0f1f1544dc3ba87110610bf5b06ab88acda3c6d02000000001976a91446270d68dff69a931ca2f62589d7115c3d5f868688acc9b3e304000000001976a91464b183eeb99d972e34359a9905892aff37228e8e88acd2dd0302000000001976a914055df9784280c3e485a42ddc7bdf79b27559bb5c88acbc2a1c02000000001976a9141382e3dc97d2c21d2f7c60fe90f056f71599ada788ac5f0e9c04000000001976a9141ee0edbfb287c286dbbd2f3ddd45879eaeb64a9388ac6ba69802000000001976a914bd5b49e915c71d6487fa4da6f390f91577a9c47188acf20c0908000000001976a914580c76c3e0059331632decd691cf6729f9a9075988acbd707203000000001976a914972535ae031fa43d51dbf8bab559a2a27c69d2d888acd3e82303000000001976a914a01fecd643872b411e8c911dcc2028c45fef699088ac948ba403000000001976a9145bf1103e3c1ef5ee2f767a7d10ce9cea2cae40e788acf0a88c01000000001976a9149ffefa7272f401711d2b4174a74e55df33e7f19e88acdfdadf01000000001976a91485c2d77dc62456371285373d07d22a0356e8546b88ac02db0708000000001976a9140d3e9f3a4c41ba7ec802fb5d45253a943eb1514e88acbcdd3502000000001976a914267e9d81d4a897ca382d275e467cd51758f451a088ac130e9d03000000001976a914bbe70bf9c1dc8ecdf53a8b9c316a06512b6fc8ee88ac8f98af010000000017a91405de71acdba61cb2d5fe6687fdda1368ffe727008715edb405000000001976a9143cc5f5e612e32b925d3f91f0bba4faefb29f02b288ac17181b05000000001976a91430c2c822498611d26fd007348787167c35d427f488acc3d69c01000000001976a9147d27e0fac74b1ac40179561b664df4055a235ead88ac325e2c04000000001976a914d4f21994ee7b6db09a6d16f3a70df7bd143c6dac88ace9029336000000001976a914130ed1ef510a96f7ffa54d93732d815e2aec842188aca026d702000000001976a914343692079185b2f7465c8076ee944d736f82a8d688ac04ad2702000000001976a91421ec5ff71ffaf59bf70c4ec56e2fb0730262ff6d88acaf6ace01000000001976a914702275b84197ba328184040a3aaf89ff05d717aa88ac6c6b2303000000001976a91435ea0260fdd27d3ff0649d7147f633b3371cea6588ac902273010000000017a9141e29f3337c5ce85fa9d957dbb336ec9664b103a987af3d4f04000000001976a9149758af9c60b794c6c05e61c737a7266453d22d4b88acc1eae601000000001976a914df97ed0617a5f1680dada7b0b6d96621e0b67d6988ac11000e03000000001976a914edeb0b8ac3e3f8ed1b45bb7b7a0cbe4dfee282de88acab8c2913000000001976a914c75f3f273f8a7344dbe75848a7244d6cc982697188acf248eb08000000001976a9140209ed102e967e04776483b07550dced39d02d0888acf73cbd02000000001976a9143427d064e6f5cc223f7649b0633e77fca666caee88ac5339fb03000000001976a914d14220b147427921861f244a929e858ac3e8441588ace6289702000000001976a9143a8d250f6610a5643ccada659e4af4b75b40e0e488ac7c460102000000001976a914ccffdbc34b0bdd15fab26ad03a90d49cf87017f688ac41502cb4000000001976a914720646458ebb021c6b208b46bbc0851d779a9d8088acbe497801000000001976a914f9c8b8d4b5d95028bdb0cdd5bef39e3288257f4b88ac8441bd02000000001976a914914ca375e719b445ca2ee8802b003f1050e6781d88ac94345d01000000001976a914a17a960e94054d748a76d9b7acacb5bef677ef9688acd7cab403000000001976a914ba298c7cb0b83dd267c96d1dbf53fb9088302eba88ac9be3c501000000001976a914ce2d996d81fb8a9bad71b83c6f89a99fba3ccf9c88ac66a2f30b000000001976a9143cf50bff30c3eba9dbcc17f2ad66cb84fe37018588ac983b920a000000001976a914409bd82fbcd9ba7a0bbe7fab36c1e9f5e489dbe388acb0912302000000001976a914d1cadbba6ec2b97ed31449afdfd9b87c25ce47da88ac334e8903000000001976a9140dd80be06a3aecf00516f239ca082cc56ffadfcc88acf1be2502000000001976a9145f4928336fb4eb94d21ea051f1ecf21300435f6688ac1f8a1202000000001976a914b882762e6f374794c8a2c2bc7f116c5469ba6ba988aca9476001000000001976a914b4fc7367bb82f961029af3b4ac5d1f29aacb96eb88ac9f04a802000000001976a9147548c16655a367993cb92c05f541ad6edd4c52ea88ac2bd95406000000001976a9141ab00581913d40794ea3f1a3e136f3d9fc3de78f88ac5f47cd03000000001976a9141d91c5641755657d8e00804294101b16b6484ee488acbe3d0a02000000001976a9143109f3dec5e8a39491c6efed3df29db388e3396188ac88a20208000000001976a9140ab5509a8ae43755137f432c2577411c277368d188ac6129db01000000001976a91489354ba206f6452d375118eaa28b20d2eda9cd1788ac62449201000000001976a91488cec9838e4279beea7b6dee4ff57143c1f8140788ac9f453002000000001976a914d739a216eee9ae67b1fce7efabb7d5ce7cde01a988ac11d39401000000001976a9140e8fec8dcff0e30925b2a42045a040471deec3a088ac484d1403000000001976a914511893184135c018fdfb66f41f528b31e9033b6588ac9ff65a01000000001976a9142c237395c9900f6447a93b55aa3b7f85e2a67bb388aca1340c02000000001976a914d457832f17cbf32c8b463023dd8d373d91495eea88acbefa640f000000001976a914c1492b91cabe1f9d28042f8c5a8f571e3b58be8d88ace8d31d02000000001976a91410d8ba6adb5069ae797564afb926f73095a35fb488ac11595302000000001976a91491d9c456af84692255425f7c7961c8ba661145f188acca01ef03000000001976a9149c0db54dee7d76f8e7fbcd0c1104ccb487c5fba188ac1026b803000000001976a9149a288533a3aad9b4d82d44d40e58e0ee3ea7cb9088ac64a5ac03000000001976a9140a7127aa1f9c69b6a52b324786d59e3bef86f33888ace03c4c03000000001976a914baed06ad098822ef3c588168cf9539a6e952393788ac22f89f11000000001976a91401e841a1a35e3366d148e2d518f044754da4389a88ac72e26501000000001976a914de68393c8908531973a56c484f7fead7cd41349788ac6af6dd01000000001976a914a07b9e1ff9eaa88ee3d8839815c8c3dce9fca7e188ac1d2d1402000000001976a914423c144baa2a440e078f84a1335bd275444a9b2288ac83ca2d06000000001976a91416a7a2b396388ff124b5f195dd6b94d030da7cda88ac5e48c901000000001976a914c51e53740cfaf730bff26358d02c3e6211de5f3188ac51105110000000001976a9146c7278f63883b03383a0edcaec89d6b44992303a88acc2025103000000001976a914f1bfe1fa773abf0aa26a1426a8e0e44404a751c188ac2e8d5d02000000001976a91421faf8b85b4f9b4d16224d4daa4624d5df9e9abd88acfabc8d01000000001976a914b9da74145df0b6e32010c0beaee019ac664ffd8588ac5f888001000000001976a914fd44e27d8a182de7492eca40adc9dcbf6f38a7c788ac20579905000000001976a9145f30ad3b63f67f035cfdd1eef10fa8ab4a79194c88ac7fb39102000000001976a9145ad008daec42d1f3e9e8d9fcd098fa17ca22772c88ac33142e03000000001976a91430ef065ed897d7e3dbbfdd6e22ca5a54974ec96388aca4e92603000000001976a9141dd3e545394c62e030e70928aa4a2ce20c3f8d1988acf192ec02000000001976a914c73181882a45e97ed2b970c3b798a5610154f1db88ac7ffa2002000000001976a9144f95d518615b0d2a7afd0a12cf5ce29bcc6c3ebf88ac3986800f000000001976a9141a5a628b1336ecc407e11d82503c4d3abbb8aad588ac1892d907000000001976a914efabac8cf6dbb96c18df09c904ed6c4455068e3c88acc0921503000000001976a914e411f2be46f8e9c82edebe5694ad854b544c3f1e88acf02fae03000000001976a914f5f586a96b6ecbd3b25ea5816e2bb0f0a6cc29ac88ac431bd502000000001976a9142b87994108346bbdede99a59a005ddc16b04804688acb7517504000000001976a91405eb42dbfb4c2e6719182e98edf88ad867864a5988ac6e564802000000001976a914841bdef35d081aef9ba473c5bd01201faeb445ab88ac8cd18505000000001976a914058d8da2adbec9ce71d92405fb005b8fa2f7ad1f88ac7e410f03000000001976a914f6e7893bf63a60c12898726aabf5eb0de6e9d10588ac57998501000000001976a9149a1b5f361e3ebd0414ebfb7255bb6c99cdaa58cb88acc0c42e02000000001976a9142889edbcb46a174ae37d5b49d92ea689c62ad73988ac5f8c1802000000001976a9140d150e527e2fc8bd9a4ed51ad528467f11df416d88ac2eb17a01000000001976a9148500d30157860026aaaaa8dfe99873d70c78e12588ac332a7501000000001976a9143b605b26c2925d320d2f84b2fd3baf656f32f8ec88ac7f6506009e6506000000000000000000000000".into(), + "030000807082c4030138b43fb1d4044d1486e52a24babcedd0ac1729fa964f5201a73b5b85e0ea1b93bc0100006b483045022100be7b7bd33675ed440d0491158c97f201abdc9d8fb9ccb9225137dd37e1e5f171022009a9b68899e25581889147511301f2a7d33045e2096a1cd520ff5c73099e389b012102a19db372699fd6d5ffe13fbca169efd3059c28781db86ab07646d43976012737fefffffffdf50131b70c03000000001976a914418841fd3602cf7df3e8e2e6f598e338565372bd88aca0941503000000001976a91428c7f0a005c97be7e07bb16b1c5c780f3fc00d6b88ac01401303000000001976a914480279f8bc980610cca6b9793813858ec92a89b288ac6087f701000000001976a9141d967ea704e1560faa2e39260019b64e784f8c7a88ace0273802000000001976a914ca6fa82dad2ce6431513c8e00674a6856d2583fd88acb8ebda01000000001976a9146d2f9be3fd57a45aedd362b59d6917044800675488aca90afd02000000001976a91411bcce75e97c54d2513f98c74a57a09f28602c9f88acf88e9c01000000001976a914c0dfcb4420129ae64467d2dae056aee111c8316b88ac48aac908000000001976a9147f9577321a839e6868f646fc3459b00394ac188e88ac7a01f901000000001976a9143426c3e615e17ce3d1c6ebd47ed20517acd32e9e88ace3df0f02000000001976a914fbdd87fad747b761b0ed2cd5d7d50df3f0f7f4a188ac9392c906000000001976a91434de621adedaee0f720fee313f954e55c8ee833f88ac2b758803000000001976a91428d87590033913e5e1e6ddad642cedf42f12965f88ac7de64c02000000001976a914e4670f5e08bba2c22f24554bbc77020623506fa588ac50136f18000000001976a91458545a626a1a786fb2f07f4330a4232030098f3588ac23395c01000000001976a914154dd085c0c84abebd16cb8d391ab72bb93166a688acfb101f03000000001976a9148ea201e57ced46eefb08d3e6f35e8431cbc7336388ac91c047070000000017a914d9d2a16961cede9faa9aa9ebd09b2fc145edd45e878a43f301000000001976a914a789eed2705a5c7d988cbc5f6de047683365f07b88aced471d06000000001976a9149eec88622cc1525b7595a09ea167ec6533b5cbdb88ac7b21ff01000000001976a914ea8e97d07bdab98df52afd8f6cd8984561717c9188ac45e2b704000000001976a9146d880c3db902c9c756cb098ae87c47e416c2f60188ac213fb001000000001976a914038d213aadbcfcd5abc0d4e99aaf43784452e02188acc28c7503000000001976a914379e6e480f53abe778681430438aebb22abf24fa88ac09c02d03000000001976a914834f3687ae80cd4b7723277f5569a50f7ca6f18888ac5f37b502000000001976a914b29d138b25af1b9d8ea3fab08afc2aa9c1370e3f88acc4ea1602000000001976a9146ebb866016a616f3f5c2ef764f45ca89943f1d9088ac92b7fc02000000001976a914aff6edf074903e94ef2ee4bdb96ed4bef626080c88ac88bf8c01000000001976a9147f4af16accb1a9aaeafac50079a3f1086ad96dbd88ac6964d505000000001976a914461d577de4b2c8b9af9dfcdb1cf184740b9adfea88ac6621e801000000001976a9145f03f7266c23a84ce4a3548db89d068d50fad70488acd36dda01000000001976a9146db4778ee3ab28a320a479123db4b94804ea847388ac4acb660d000000001976a914dbabed7a0fbcbb9492d3c6f05b76e2107a8eefe988ac48a65a07000000001976a914a7d4a9d7cd1aa121374c0795670dcffa0ab08de888ac4f8e6502000000001976a914729f04c9b26fe2c6a9bfa2bf38478a586df0d3ee88ac8a32c501000000001976a9142a43b9aad78a13f0869cff7426db3d769a7a224f88acb715ef03000000001976a914f903b1686b7e6d893a7e6b1db5c31f102186335688acb241d501000000001976a914c2cc96a441f1c963ab34684c46d573f87972b07b88ac6f099a12000000001976a9142e30fe12f06dfce8b149366e71634b52bf000fc688ac1526e202000000001976a9145d3742dab8fbaf367284e0b4c40bbc412aaeb6f888ac26d05601000000001976a91426647a36c26cbf968f4f0315092aa7b71fb1408888ac1b778701000000001976a9145bce8570386de82a2503d89250c796033cfd07aa88ac4c2c1802000000001976a914fd361619c16363e4a25313e83c36a791582e248388ac9a852d05000000001976a9143bb5d1bf1ca6b0cecac4949c4ee3436ab6f0a05d88ac7288e401000000001976a914a8a17cfce57cc18960e032b853f6390b71d71cc888ac0da50e03000000001976a9145a78701cd37ee6049fcfbdd6959102af86cbc43a88acdface601000000001976a914b39744132b8406d9e6ad5910eb7acc27b466266b88ac381d9d01000000001976a914dc567a56fbf0c62f1dc5b9890eb93c243c504ec988ac44820802000000001976a914b64275182a8eafffc1c541e998629422d63170f588acf0f14a01000000001976a914cbc3955eba62dfa4b0e2d07a80cbee09822ae63f88ac8f2a5202000000001976a914eaade91e5f2ae2b658c317c689a3d55358a8acc988acdf5ed301000000001976a914c31983fa0c07c2c0d1ea21dde3932fd0288be17488ac84070023000000001976a914eaa87ea78ae23c146b0317231a3b16cdc2037f8488ac95936a04000000001976a91465e4682c0c38bb9381ad85d777a6a7c0c94a07e888ac3431c10b000000001976a9145fdb43217d40afd0682fe81da5f5156e2b1b901388ac1361a204000000001976a914e627a800f4b11992f47660e2effc7f6abdea9ba888acf426d501000000001976a91457e649414295e40b86b20c5643753609932edb6888accce55201000000001976a9145e472bbde322d1811e18bb0558ff1b46bfb60f0988acec9dd507000000001976a914192a5bb07666cc7c1ad90421cfca03978354177f88acabfab701000000001976a914483c04e0da800c807e0999c16a96af879a97f03a88acef448803000000001976a9145c05a9e0f7a03bfb7ef3939807f25910cd4f0a1a88ac63136406000000001976a9149111b1f9fed23b39414ddc3fb947b9b372ec5c6d88ac07b2250a000000001976a914cdd8b1c40d063a96b90dd49077a510b34b7d691688ac6a15bc02000000001976a9147fb9363905cd1394b84c91a7880818b6f56a568e88ac8dd1f708000000001976a914b7bf86eda5bf2d5063fe642eedc37f4a3fc5270f88ac80c82a09000000001976a91469c270fc46a1756bd10a18c76f228f3e4b2f4b6f88ac84fb2c06000000001976a9147f591216d7379bca93c3702d80e6b04f5be8cbcb88ac82370c08000000001976a914312dd5f0b410b40b0b957e95bbef5fa38c5cd12788acbc92eb10000000001976a9141726853f4457c08339141e531682abb172d7d12088ac4c48f902000000001976a9143b5195a70226963bee5c88de4c317adba8bb317b88ac0d5d5a01000000001976a9142919ce4776ec0d18e58ca465dade0e484104ffc688ac16c7f802000000001976a914240d7d76ec47f93e83fef49d5bfd3a9d1ef7a81488ac8a055601000000001976a91490894ca060c5b3f1da5b3e81672407b9e2cfac5088ac19c46a01000000001976a914d07fef54cc8ac4f6e9659661fd4640b02836e5ff88ac6a076001000000001976a914ec99b473cbc8c60c6e7b09aecd35a0e3d429ad8988ac6b8db401000000001976a914b3396307a616559f9bc560b4a7a95c07d093357888ac49440b03000000001976a9143679baaf12801b9bfebc92b706b9e1262e7c94a888ac0821a417000000001976a91435713cabb1c1a88d0d53e51a62bc13e3c86cfdd188ac5e2a2c08000000001976a914bd465182a3612d6924130537d516a7241a72b62688ac9274f502000000001976a9142888d536180acbd8d47a339a6153a431bfab119288accd58f802000000001976a9144717d5dbcd29076c7168331917e0f4ce97f6c04688ac318a2303000000001976a91437e90b29cb614d44009a7b50f75f44745aef4c7688ac516b1a12000000001976a9148ec556ba1a9086d10482efb1ec34da9a58d6f7e788acc1c1ce02000000001976a914bf9b94ab9a5dc453acdf9fd1488b53006f7d45f488ac2687e101000000001976a9147972628a1bc244676f7781d0cff4e293ab5884df88acd99f4709000000001976a91480383a56dc7913bbf2dc5c9e3dcce1aed5fb409688ac96024602000000001976a914dcc9c29ef68531c88babfa49ace2ef46580c78f288ac20a05c01000000001976a914162fcf25113879fe9cd5c4778186d5df32ff502f88ac47148f05000000001976a914fa8efbb448ddd85b629d08369551ffbf60b5d8f288aca0d99001000000001976a91453b1eb90495613089bbc62117879cfdd6a90a04888ac9bf92b11000000001976a9149adc3523f1e7b3d938753458f868dcb2c1db226888aca40b3313000000001976a91450d6c34cd77739b4a0c32cf28bd5ec938406f65888ac8d75193e000000001976a9146376f06a9bdb30c0801869624c365d948240adbb88ac5cf41302000000001976a914578dbb0c3955d0e5b3c0a810cd457b1864f143a788ac8a0c9011000000001976a9145c192699235ac72eb869df75979bcbbb9e716cba88acfe6ffe02000000001976a914d023fd44c787cc5a902b01ffb05e28090030661588ac3c8ee901000000001976a9149f438dbdb0ff1fe7bab5d501cfa01ad9a70032e988aca030ba02000000001976a91498e75d639b3e663d903a6c296834aaa028e5372988acf9a27d20000000001976a91428dc8981d3c8c06e3521168a3f2907a4cab4356888ac56c65601000000001976a91493fbf7aba1a92a1ff079a5d7f0bd884810ebba2f88ac6df30b03000000001976a9141162a9391516108ee210c818571f20596c7633ba88acf3e67606000000001976a9144c0d393ee179cf9e99f70d3596e322389e79df0188ac118bba06010000001976a914a1fc1fbdd78ee407d72d8e273a18df43ef9bc55088ac9f551604000000001976a914aa3d19eb1399d3ec2da04ca7595619b28ad6e10188aca2614401000000001976a914a18fb5847f758aa2429211ee4865a0e7ef3bf86c88ac3440d70a000000001976a914b21590c89995985a12aa3759228f7699d1f2b9f788aca32f8001000000001976a914f5d323b78fa6456dc1a6685b0d29d9ab3869473c88ac7f117c01000000001976a9148d3e5a3b770764a92a5ceba072940c7db2f74a6088ac7617fa02000000001976a91493204c56feff9170e5b3283791d31582bba0e19388acacd7b503000000001976a9144dc263760aa3226e5a006863ee6f6290634df51388acda4c1651000000001976a914e8ffc4992979e52d426a1b5c1a6d3af769ecfa1288ac75539704000000001976a9149f9e8d89d7ab3a4179174b0f280193300e16d31b88ac36122c06000000001976a914d390e654d0f926cc5e5fc15cb3b3eebbac567b1888acdaa2ae03000000001976a9145650f9aeedac9bdd01afe7138c31a0638dffc77888acd3bf8601000000001976a914ddd7b385984709d064f36b4e620db7d6ba58923a88ac90964806000000001976a914970e31b940e59f0c8120d3c5ef57d458bd4d0df688ac717a6703000000001976a91496580646005e58648a1413907cc7342bb60ea1a188ac46c10502000000001976a91464ebbd81e6f89a4f537ef87fa2cd77a3ec7b14e788ac835ef902000000001976a9148282bafc7b0e34e50b717be2ce33c5bc57c5650788acc899c007000000001976a914541ff0757982133d39df82360116b6c6bffac9a188ac1eccbe02000000001976a91443bd338ae81eb5b77611e7f6f379b6046338369988ac3bfce802000000001976a91430a45c9d6fae7e674729d7a08c02184b190dbc6388ac0134c501000000001976a914a525fb06f91beb46f85f92210a280f65183ff49e88ac4411a801000000001976a914a239e5d35a28e8c706c1ec60e217744cc0da008788acc3568d01000000001976a914ffe6906a70d46f723784b7c92860f4cf55a4461388aca16aea07000000001976a914031e02a3a6e76b1428fcf8dc3db546b67de078aa88ac81399701000000001976a914bc08dd59ffa159c3be98a0936f1a3cc5eb43b73f88ac139bdc19000000001976a914670c34092e288745190afad98217e139c4a5fbd988acb0ef8a03000000001976a914f5592bf064426a255dee9744e898872356c41fbe88ac745aec01000000001976a9146b1e6bd7e815d1be3556397a1301f20a158d0bab88ac0eef7501000000001976a914a23a8abade85043fcec2f52f036d82f0b2ebb83088ac50ee7a04000000001976a914cae108dc674e91d540dfe8a5aad1fed4cf3febc188ac83c82702000000001976a9142eaac41759d814e86920bd0c985073cb181de35e88acfdbb9b03000000001976a9144393d4d938241835cce09405f32487a2b7a04fc988ac7dc62602000000001976a914954b12e4823e58dd4c2556d063842b69dfade8c288ac6b4ff601000000001976a914169c010e553bd6332fac660cc20a882f42b0491b88ac8bfa9a1d000000001976a9142289e57732a4b9bc8517a1cf62fcdca4fa63d50488ac1c538a01000000001976a91450643610a322a5f9c3e7f04fa7750749832dafe488ac8a1ce502000000001976a91419c5d30ab9f35e7b61e22c9234289ee3e18b03d188ace57cbb03000000001976a9145ac1c615d52409125e30356f61eb32d3f2a06e7788ac26586601000000001976a914c70f67747e64dc8fe5b5d964894df8ed96d213eb88ac8289f708000000001976a914f3e69de2d2f00621962540c614153582eb7f661888ac3476f801000000001976a9148bd7ad78071ebb26e3bf9e0725f90a955523974988ac6152bc01000000001976a91461fbde05a94dfdb362aaa55237aa40695513ce8b88acd8d29101000000001976a914b43c8c55bbfcd703874c58178393041a98ad7df488acd8cce635000000001976a914f17162ac4c750d8dfd0b4bacc1122e35678d757588ac9498fb03000000001976a9149f9d487694c2c9beefc933fb00604d1ffdb39dc588ac7210dc03000000001976a914bd42f674423f4f2d9fdad3d7397e3d51e269868188ac717fe704000000001976a914863dec0f5b2123a1f6b5884571e815a076e2288488ace18ed901000000001976a9148aefd63836846fd3a29785e9fed55a39a370e5bb88ac17bf7901000000001976a914921dcef2cad253d4856c428adccf4abd2b36ced288ac1ec52405000000001976a91437b6d2c348b1da7fd8e39b86aba652f1abb758be88acf26cec02000000001976a914070285a0b9536d9fe81b5efa4d175fcbf5306b5f88ac9f8aa402000000001976a9144cd268838f7b844f0c3dd7015410baad7ad6e9a988ac0b9ba201000000001976a914bdbf706b8367fc0da074b3009d1dc42f4623273888ac0cf9cc01000000001976a914121a36379c2f7410345d18246c97ee2b6687a04288ac7d011c03000000001976a91460f763ee2e594fe165f5fac97f63034448c94bf588ac61314701000000001976a914e165917799586a70da6f10a6aa0600512540b8d788acbdcce503000000001976a914e8c653841473ef88a284d7506621eb9be1bf679e88ac8a939e01000000001976a9140477a55dc1195c8e9b954551ede3d241c5e228db88ac088edc02000000001976a91411a079f2dd08d89f273eb204fd920ebc5eac68bd88acf59f4402000000001976a914ee2c21c8d4a5e8863ad4d558c4bb4da2392414ef88ac4412e52b000000001976a914b46e215656edd12e62f0613af126a6cb6fcc67d888ac02d7dc01000000001976a914d9487b322e40ea9a3df23511b05ac896f767b30788ac4b1cd101000000001976a9145e631f5120322118593dd6a2643f7514e600901888acd9e14201000000001976a914fb67d5101976dd36e8f51b8697f6aff788c3219e88ac1cceab01000000001976a914b5a85aecc8f9578137db4750fa72d447d116a49d88acafdf5b01000000001976a914c980ef723ed0ca619a1624c6a962cc72aba83a2f88ac11073402000000001976a914ab1ae074b7d5fb8d2be8b67806e2a33dd1594ea788acd2816401000000001976a9145e30dcfa973bb33862821376b1530d00dbb0475388ac2359f91f000000001976a9143591920edba33c3f65dd02c920a4e6fb63886bae88ac11330102000000001976a914b65c930320386da8dfa2504edd49b27539a5831488acf9611803000000001976a9143922667a97479582c83b9e34a199a4f658428f5688aca5b60402000000001976a9143f33076df5b772c1d38f50443d06be769822227988ac56bd0f02000000001976a914132b069ce844d08f0d43e5cc66df5956c9bfc0eb88ac6c1add01000000001976a914c16562add15cdc273be6ef8ead06692b9099920f88ac5c7b7401000000001976a914039ebba2df6da69df52e5ae827e2e92d3df5c39e88ac0ecb2d02000000001976a91493850c28f2cac283a41636d38d2e6f9ab0ba4b0488ac4030e002000000001976a9141e3d533be44b3a5f3f0862bbca24cedae0b3421188ac4cb9a10f000000001976a914d2a84ba3894e524946f5cb540c122382e1065fde88ac6a3b0403000000001976a914cf3de80a00c76b5ef024811912c709dfce03336088acf35af901000000001976a914226eea9d11449fbe6b18cb2d4f2d3aebbdae179988acd875c001000000001976a9149f9dd1b32ac7bd1eddd64d250a38e21c96f25d5f88ac5de50e03000000001976a91480d9a605957ed9cf1be35e23e6f3971b7dc4fecb88ac6ea9dd03000000001976a9149f0b4cfd64e900906ac67d6753da91a9a504562a88ac33caec03000000001976a914570e8a4f5dbba6afe17cba4930ad6638959cf63b88acf5d64301000000001976a9141d42dda766317787979fc1703c5d7bd0a4ef2d2888acf2f7cc01000000001976a91465d8780f0dd45f5eaa83800d26a4b7467cc3694f88ac02cccb06000000001976a91420fdb1b1cc3269a0f649e9bdf0d901e87c28e7ea88ace70af401000000001976a9142ada8d24ecd968abc95efb2ed233ee5100e9845b88ac82304402000000001976a914db3ab361234a07d6f162dc45a3733db1328ebcee88ac1d328301000000001976a914bb8a116e51d17a920e6bea9609fad47cb857c78f88acf6a7e806000000001976a914bdfd29e35c6813a1b6dd4bb5421a76435138b36188acaeea4903000000001976a91461fb6e39d66a1745d7fe089fe4fe1385b19d491188ac28fa7401000000001976a914d01d2f563a633c79f25c9441a502959f42e2cc1c88ac80077b31000000001976a914e4a8bc5388ab9b53afccead02f2e8dd62ee370cc88aceaf30003000000001976a9145c45c97f03197dd0e9b73126b451db2b64862f8088acb64c5d06000000001976a914f60384efb6d9b51fee029a282b659a5818e1de8688ac7a0e6e01000000001976a914abe6aef654e503509023f1943d74102bd47e24e188acfc038e03000000001976a9140c688e95beef63c6f39689b7d484802e2c5bd28d88ac77ff8e01000000001976a914afca9adba83ffdd1c73c1b083c5fd957615f264788acbf5ff501000000001976a91415899b64bec1c3be766b08cfa3aa63eafb495f4788acea4cbe04000000001976a914663734caacf23f39635c77e008ff6b8f2e1d23fc88acfd697701000000001976a9147293a0f0bd0cd0b0f0462ecfec1221cc22e167dc88aca3fc80010000000017a9144da447dd03a52c2071a4d7d9ed7f3917aadd918687feeec001000000001976a9149041805c7b734b2b30728aaa1fe27788cf1a688a88ace131e206000000001976a91400949f9567ceaf5c6cbdb0b9da76a6a3e184905288acab4b9a04000000001976a91449751eadd640f582122948e98f8f94b8f0c3acf588acc0880506000000001976a9149e51693403eef99213b3a9dac719bc51b8e09ed788ac2428ce04000000001976a914e3e3cd8d736e50982b82447e97fc826c1a4e58d388acaf6ec003000000001976a91499e870ed951690211a0c58d62ac25ffeb562ac3f88ac3d72b901000000001976a9143d6ab5455bf93aa3128e3ba37c3dc4314138888b88ac2d4da001000000001976a9142905ae99619c7c518d38860f8910fff8f563fd7a88ac6fda9401000000001976a9144594fc81aea8a26d4944ac67ac600b13a0de8a3e88acc479c16b010000001976a9146593a7b3c4281760fc294a6f1408e6dd98f9b1dc88ac26717303000000001976a914283dedd27bad0932df7c8ff49c4a86d917c1b26888ac9bab5302000000001976a9148bfe72099af6f2279d4073c76e93c80db49deef388ac8e72f303000000001976a9141e4fe9375c8f0d93bfb9c69ca909714397781ce788aca1bb7001000000001976a9143c0ec1336c9e2ddb0122e0628c84179d393342d188ac9cecfc01000000001976a91488964e01d9d500ab7fed60bcb40623140192678788ac30220c03000000001976a91435e2d9f0168c1f9fd31300d40f33081e6e6ef12f88acf0097201000000001976a9142a54f07fe56973ca1d5d00c18f023be75593f14988acb8661702000000001976a914b4cd801246cf62b27ca58abcfc4c2dea992bff5e88ac79c05221000000001976a914f1273143fb962f1b71983eb691a6d4ad08c2563488ac70a56d03000000001976a9146f6b7966edb8c62fa7e31959a2a66c946eb836fb88acefbc1c2a000000001976a9146e88d3c3ae0f8a52e7e8a10d0bffe5e56c78826c88ac044fbf01000000001976a914a1644410d4b08d103321815200b68bdd17ed967c88acb55b1206000000001976a914dd4934ea626240877e8547f9f9c75294a7e1077888ac958ef501000000001976a914e1788559e4912dc70e6954a0cdef336be56dfc0488ac6872b801000000001976a9142a065ffbc4aa4a14104f05463fa51249889c2e5288ac6d15ab01000000001976a91453757c1c47c78c6a4786cc934e24c9046319584888ac92e0e903000000001976a914e5410005fe09917c8cb6efdf57708b001d44f42d88ac4a3bad0b000000001976a914b78fa5c9175b0b0bea8e47e55bd4fe14d2f56ba088acd86de204000000001976a914e26029c4dc113b6d6292ba029acbd38124d11b4588ac11a7de04000000001976a914333d85b037996fd8cf8a71bdca0404ca00d8d57288ac85024007000000001976a914ed92422b9ac97def1eda45f18999e675e6915fac88acbf667705000000001976a914b40c4e756b7c5926bb212f71be8ddfc0db37805988ac62613903000000001976a9143e7c6c2bf78ee08cb8c63eeec6340c65db56b5bd88ac86813206000000001976a9141bda2b5aa3903ba32f7a36eaddcb40d2f97ab31d88ac033f8401000000001976a914d634bb5efa522e0a5db17f510792dda9ec3c26a188acd2f7dd01000000001976a914c19c0e4464c426b81e2a8e3009e66a7fbc253e9288acbd54fe05000000001976a9145abbec34946f572e7d8b3cc1f7f0958b1b4e851988ac4f94c602000000001976a914382b6ee7c648253d4370b4367001f32170aaebc188ace80f8c01000000001976a914d45f9dcb5eb1feb2747eb457b17d5beeda62658288ac203d3b03000000001976a91401adb5e1c4378e422b05218ea9d81d03f5da1dba88ac6cd2f401000000001976a9142b708595c9ba189d74f59d8a3226167ce031c1dd88ac82520107000000001976a914c552cd5095bdb0fd1a08e2c766ad66e40906bd0688ac0e05a202000000001976a91482e5bd3a928aefe49c493ab589d265f2a1aa042c88ac5f97d011000000001976a914c9a40b03ffd67725d4868e3a56236535397759ed88aca213c801000000001976a91498656f7477790e2a964044094eb035fea3de387888ac7bbd8e01000000001976a914d571ba9e9ee4b198c3c6ae4d7b6d1491c26d5e3d88acf4902704000000001976a9141d20ec7b1088787a0f18e6dc969f26462d588de488ac92979503000000001976a9145e8bf6dba4c66b3342394f76335d0a1921b6267b88ac11713703000000001976a914c1d8950aaaa11e2d60d2269fb0fe5cfadcc82b4988ac04687d06000000001976a914f016fe702c86c7f59770a27beb712c806349766e88acb6decb01000000001976a91425322f40e27554cfc1e98051ec65d25ed6a2e2d688acd7f86e01000000001976a914e7345a19b63d0d5955f30040c7a6cc5c1e011a9388ac97c7cb01000000001976a914c5ca9e8986b3c131aca38b238b2475c4d490941c88ac388f4302000000001976a91488ffc5eeeedbab5423598d853cf1e75ace84fe4988acfc0a7a24000000001976a9146cb18821cb8a36d695a0f5105bfe11106757e01788ac45e1a103000000001976a914413ccbf834478ccfe1912a687681574eeb53602788ac3745c601000000001976a9146904d2fce2e0e5dee4fecfe78e288a06f7a6ebe488acfd3f7107000000001976a914ada4a2729573d97ca99df7fdb2d04a738614101e88ac9f203417000000001976a91490d8631221504737e4ee554d7f2e762d6b3fb13a88acdd8d880e000000001976a914b37af9b47f25e5a6d740b31906f3590946817df288ac298fdf0d000000001976a9149b015a335b756de5659e31fee2394fd5fb1d766288ac3ebab801000000001976a914e6172e2bb7c82327841af4711cc3d9995b3a46f088accdd48501000000001976a914f31f62e918f4e1b23f67be9aaa869117d537653088ac8dacf701000000001976a914e66b76901805aefa63e08c2cfe90668841adb11388ac598bf501000000001976a914d752052b85948b6dd1a500ef826ef1f5e81e159588ac6d7b4201000000001976a91438b786b80de47a1805e820b80ee9e8ccb2eed04888ac46f80404000000001976a914fce69f1660975a2261685b24cb3e5d35f313f90588ac4fb84901000000001976a9143689c50135c0a482976b9c47f8379798b95a677888acdf43f508000000001976a914a0ec9118133f2b92cb9020f99caedb0b56c1af5f88ac7f4b6c02000000001976a914af7501d6c432fa4496606c9f2913c695fb3fa57188ac8e000802000000001976a91488838d5e1894d8e1b5ff890ba7517c1e806ae74e88ac68f8f601000000001976a9142278e3c6eacb76b64638063464b9050a31f45d8788ac96ae6f02000000001976a9146d17d3934f6581c89eab4829bd2471ec2dfef4fe88ac00220b09000000001976a9142a94d8810fc64b890dd11b2818da47f4b9e0bfec88ac4376ce02000000001976a914adff9dfa6040e29207ea9c15486ae71b6bf7c1f888ac048a5401000000001976a9144f8a2ff488f41535e507b6576423739feb70d58388acb490ed04000000001976a914f6b6e2e0bbc1cebb7caba85c03817ac8701d00d888acc8f5c104000000001976a91479234b57b7c4efa88c2ff9599b0865cc231a90d488acf561340e000000001976a9149fe248e542925856009ee1a314138dac6f601f8588ac3d631802000000001976a914434d0b50bb1135e84d33d7f08f3a765b21d8d74888ac54b7d10a000000001976a9141e16c0ec7fd7fe63a560bc6bc681533214c3d52888acd850f501000000001976a9140e9334c107d98f453923888258ff2c1181fd3bce88ac78f5d501000000001976a914e5d8eb2e46196be3defa9267b5f416b70c299e8588ac40411305000000001976a914320a1a6e87e3e2037b786c3ef9fe2620c832e25488ac4e0a8803000000001976a914ebbe4fee1b4f0c17c864a9a1b6434f2fe48cfb0788ac51597a02000000001976a914f7c808d47952825d935084121dc1176245be75e288acf12ac509000000001976a914e689da4dfb082a8829311c4b4d543dc16301130e88ac9fb07206000000001976a9146ed610dee994b75d572d71bd5e8727dff69f3e2c88ac4cd06f01000000001976a914c490c88dcb4bdbe0920d74652469cb047ed9e05e88ac22d2a002000000001976a9146a462b620f0d3f950fec2a3dddd34df2c1fc044c88ac59859101000000001976a914d0a9ccf5cc41eb1837eaaf297a929ef4f4d127ad88ac5fe53d3a000000001976a914287c714b1ee5d86c0ca78d400bac7308234b1d4488acd2bfbf03000000001976a914e11f5f6eec9d903a1f0babe8e17285957828d5da88ac28e37301000000001976a9145a860447ce11b0570e8f41299e954ed5c18f59a488ac98e0bf02000000001976a914b9c29bb285e96007b55cf679a91512fe4a49465888ac31684601000000001976a9145816d614b58a06cde2c0007b819a8f2d069e36aa88ac5c3e5201000000001976a914507a9bb968697e826712dbedc5b594df2659367a88ac24f33e01000000001976a91472273611e74e852779c4498cde2f02206947d9b088ac26830e03000000001976a914853fc1896b13adfaa65d24f4fe120fe25d59e91388ac2c7dc301000000001976a9148a2fa21c14e1685c2e67e770a1381319a87981bf88acb6875309000000001976a914235549e20c7f7a5718a7d2c48d6c565d77e208d588ac7f55a701000000001976a914b1b39ebe16fcccabb877277b53d1fef58c35aa7188ac92108c02000000001976a914f55e89223539139237e662e3993cb44a2e1a690b88acfddba203000000001976a914fae2fb0f07983ea08913e65af36830729f0e285688ac3f1c1802000000001976a91473f18376973a3a22dbfe93bcec9063759437079c88acb4186901000000001976a914c46696293469f3d5f18dec5277683ae77803606188acfe826a01000000001976a91499ba8e8a63317bb25b3fbe2f4f81abca56dc984288ac436e2e06000000001976a91439c2c9fba1d295e0197c5beefdb609d6e1698de488ac8ccc4402000000001976a914e934fb3ca2babd8e7c616db2af87c4d6b0d28e6588acbc10b706000000001976a91459b3ff96dbb4435497471d375a274d06b5b26a3088acd3dc4b06000000001976a91470636efc71acf71d28883cb09df24b03e2336aa588ac30283c01000000001976a9141f8ce375742e3d67cd5f051845505a0b28dca1eb88ac43bb4f01000000001976a914d48f6e656600c633569430ea2cdf38a860e7d46e88ac45f3700b000000001976a914d0afdbb5b0217178cbe6b9f218fa08948657e74388acf52c3004000000001976a9140d892a0214a250e8987594dd5c0cec9dd94fddfe88ac51091903000000001976a914b04267460798077381a737a5cbd8f09aa3b8af0788acf9373d01000000001976a9148b3c1633431f2353db003226cc44393641c785b888ac50d08101000000001976a9147fe8e0978172c2e50ae9455462922d17eafc9c6988ac6735cd02000000001976a9142e681e7d3a60ce87d39b7b58eee04895cfc7a12088aca6c9b202000000001976a9147a4a1538ad200ce1eb434f7598bbf4f34211b80488ac5d5d1f04000000001976a9145b2d5d5c461db93aed2f7632664f88a2b1c1e69188ac00066801000000001976a9148adacd2b0f6ae8169371bf04e2b52266eabbf22b88ac61ec5c01000000001976a9141a6ef54e75f8c4711b285f931a5a0c38da4173d488ace7f1cf05000000001976a91413484df57050fc66ef8b7ab7fca3ab53150baab688ac89e15120000000001976a914dfed7bf9ecbbe8313f00d0c395a9481667021f5b88acdaa35101000000001976a91422040608af61e64e1eb931f8cc06b79bc15ed2c188ac5ee6f011000000001976a914cf8b7396964ab758c29eb4b74222541f482d077488ac60967e01000000001976a9146f8224aab872aeb254b37066b7a5066f4961666b88acafc46901000000001976a914cc34fc2840a9dbcd886378e756ca4daff7f71fa288ac3bde8801000000001976a914d192d56ad6797d4a34ada0ab30e79e6c3a8b7f0388acf1cddc0e000000001976a914dc5696c3bee6e5c4034bce4d86240f5adaf43a1088ac7f91a706000000001976a9144530579443271a9cafbfc1af102dd0d9d42601a788ac0288ac05000000001976a914b35b7352fe46305d06762e3c62da3965844c9e2788ac70654601000000001976a9149d433981bceed2a770a7376f7dd7ee76ac3de68788acda5c1202000000001976a9144194dbe859fca391998cb1c950f2978c8ad5777c88ac38a39311000000001976a91423f64b2934f7362dcd6e5eb0ddc5472c4e3ab62388acae97db01000000001976a914baf48fad5903940b72bedb2b5f522538341001b488ac9910c207000000001976a914264565a8bcb6cb01e7190a39e31a55bbc70dac6488ace1d0fd05000000001976a9140e25dfca4acee5fc7c1abc91bed5390a002ef7c688acb0a30302000000001976a914f280de5885f6b1989214ccb6e71a5890d2628d0388ac9c906601000000001976a914a1fea87673a3762f8191d3ff48455b616ebc419888accaa3871b000000001976a914f80accba243877c07d6f973c0ed3fb9120d6e3dc88ac9c4cce01000000001976a914d99f12f3772f87a57ca2f39ff582446b157b35b988ac19c6ee78010000001976a914360bf771e354c1a4e4b337cac2bf2b3b232bd12888acd6544503000000001976a91449f7ec52573180aa36fb87dfb390ad411b0d145588ac2a260f02000000001976a91425f4d5beb86e4c88e8ff7dbdd98415f8e1d21b7888ac132cbf13000000001976a914e053f013cfbbb85c5243d28b0a120ed9ff87398e88acd904a301000000001976a914b2ad7495edaeeb4ca47168f79851519f58fd6f8988ac0621d905000000001976a9149bb93fe45625e71ec08fe3b7ef8aaf5c2e9c1c3d88acb0a71506000000001976a91443ec56ef5b1fa810b6345fa290bb2e419343ee5a88ac48888e06000000001976a914595a5bb8e17f8cf5224968a886dbf2101e996dcf88ac3e6a8205000000001976a91450cd3bdfcaede714de329746f1df45c2fc4dcaf788acb817f701000000001976a9147050974081c46f8265776524722da49e5c6a0f0988ac96baee02000000001976a9143041363ee5f18acdfe32fa03cc6361bc9754c58188ac71876901000000001976a91468e0c930651376570236150cc9320278cde2a0fd88acbffcfab4000000001976a9142e9020e4a4258431ea87d4636bbb932ea386e8c588ac19220002000000001976a914637e46061bcff20b4917e97ce34b6b15daa5991588ac7d926701000000001976a914c3891e9dd098630ddeb4030dce374155c008c18988ac97747324000000001976a91492f5fbe393fb947a98e8284598df9036b5810e1f88ac974da303000000001976a914437130b085c2ce34b711c4056642214291f7b1f788ac705f3204000000001976a914e2f92c63d90f9fbffed77d510289b915516fc28b88acf815d101000000001976a9140e1fa8260f810aa6274243cf632d31f32429dbc888ac07a64d01000000001976a914523f00d5edc1f0fce708c0b7256ea554df8aabcc88ac5987c208000000001976a9143256891b2e4ff39da258becc4b9a4413ff9c431088acfef9be02000000001976a9142be056892d1b7cc76def0db9e43dfc279dedc53488acdf6af201000000001976a91405d2ff8fe0114a15c77b23fb1586055c474ea14388ac3f0af806000000001976a914106362f0372ed416320324841d6e473116ffaab888ac87537303000000001976a914979d9045e896a3601224b8df249bfd02d724823588ace9696c01000000001976a914fc2d2ee9e9087e77aa47baf797d92970055e4a6f88acc064b8040000000017a9145af7d290fb6c30cc14dfb6f9c0600452ea81565d87167ffd01000000001976a914f7eaee96344417f688599b816743c34a681ab7cd88ac6ffa9301000000001976a9142ab7deed3c1b043f3739b351666a4415bb3cbe4488ac4d1d4e01000000001976a9143a77c30a8f5a33d80d67bbb215605b2a557b315688ac66484c01000000001976a914882d62697dc2d41f2ff25a3e2243690efc29f9c588ac237d4b01000000001976a914286f0258c5df8b43c6e7223274966b70d4afe03188acdd3f5a03000000001976a9146b49a3aaf0ef871fe963360867e69b4e5e5afdb888acfc83ee0b000000001976a914b8c71d8c0f9de96773a0b774470180908ccc529688aca4fab501000000001976a914e69972b6bc2d4cdfa430476681a525b2e3236d8b88ac1bf30a02000000001976a914a433c232e1292c2c909aec0be5e34c1b2be5c50288ac05af1502000000001976a9141fd0517d1e936d57bba2e45eecaf4c36f33f06c988ac26fe8b01000000001976a914d258b344df6728fa3268040c6468c7e0b442b22688ac6d125606000000001976a91457cb3a15f71511155e04aa9fc8fc7bda4af9caad88acc571ab02000000001976a914666fff6a11a34492d3aae092edd531dac31d623988ac17a94108000000001976a9145a556c9ae60c24265480d41ae80b7bdfc221af1f88ac86eb2802000000001976a9149a30b06efcbb8f42355f2436c4d85bc61bca336c88ace6ce8903000000001976a914f065c2beac8ee416ec1606d0a66fa6c18ffc699d88ac05e75501000000001976a91410d58f24616940c8a9808866e406d725fa3790f088acf153a707000000001976a9142328724113b60851e1066e198e69f320bd2c753b88ac69848201000000001976a9145e6c84b60cd0f1f1544dc3ba87110610bf5b06ab88ac8fcc5d02000000001976a91446270d68dff69a931ca2f62589d7115c3d5f868688ac84bead04000000001976a91464b183eeb99d972e34359a9905892aff37228e8e88ac7302fc01000000001976a914055df9784280c3e485a42ddc7bdf79b27559bb5c88ac8e2a4a03000000001976a91431753538b6607df41862f5ff1806faef573ff2d688ac77a62202000000001976a9141382e3dc97d2c21d2f7c60fe90f056f71599ada788ac204b5401000000001976a91492f38c7a20fb766e3da310b9f42107d4297a40d588ac14965004000000001976a9141ee0edbfb287c286dbbd2f3ddd45879eaeb64a9388ace33a8f02000000001976a914bd5b49e915c71d6487fa4da6f390f91577a9c47188acf51ec607000000001976a914580c76c3e0059331632decd691cf6729f9a9075988acbf306d03000000001976a914972535ae031fa43d51dbf8bab559a2a27c69d2d888ac29d61103000000001976a914a01fecd643872b411e8c911dcc2028c45fef699088ac0e399106000000001976a9142898ef6001b84e152a294e86be1d9d8fcc2d088688ac9d827801000000001976a9149ffefa7272f401711d2b4174a74e55df33e7f19e88accaf9eb07000000001976a9140d3e9f3a4c41ba7ec802fb5d45253a943eb1514e88acc9e04f01000000001976a914716181eddf00d5af09e3798c7efb35990932d3d288aca5961802000000001976a914267e9d81d4a897ca382d275e467cd51758f451a088acb03a4401000000001976a9145404f46586f6a21897d75398cba253c4f09b790988acea870003000000001976a914ae9a890e2fbce4447388f9cf5e10a1c4fd2c86f488acda623f01000000001976a91480f5ab65455061f47632c3364c920893dd05fbed88ac1fe8f1010000000017a91405de71acdba61cb2d5fe6687fdda1368ffe7270087787d6505000000001976a9143cc5f5e612e32b925d3f91f0bba4faefb29f02b288acfaec1305000000001976a91430c2c822498611d26fd007348787167c35d427f488ac98368b01000000001976a9147d27e0fac74b1ac40179561b664df4055a235ead88ac7b74f803000000001976a914d4f21994ee7b6db09a6d16f3a70df7bd143c6dac88ac004a0b28000000001976a914130ed1ef510a96f7ffa54d93732d815e2aec842188ac2f3d9801000000001976a9145fe0da7c036cc690f0b8f5a91801b078258a5b6e88ac870ab001000000001976a91434407a25dde33adc4457d44098f6e206cc18622888ac79239c02000000001976a914343692079185b2f7465c8076ee944d736f82a8d688ac2f2a1502000000001976a91421ec5ff71ffaf59bf70c4ec56e2fb0730262ff6d88acbe1bcd01000000001976a914702275b84197ba328184040a3aaf89ff05d717aa88aced3f2b03000000001976a914c342908aeeb826d95278ca1b167720a070ed4fdb88ac575c6a010000000017a9141e29f3337c5ce85fa9d957dbb336ec9664b103a987de563804000000001976a9149758af9c60b794c6c05e61c737a7266453d22d4b88ac8a12da01000000001976a914df97ed0617a5f1680dada7b0b6d96621e0b67d6988acf83ad515000000001976a914c75f3f273f8a7344dbe75848a7244d6cc982697188ac650b6e0a000000001976a9140209ed102e967e04776483b07550dced39d02d0888ac698b9402000000001976a9143427d064e6f5cc223f7649b0633e77fca666caee88acb5175101000000001976a9148995db0e78d204bb493f74c86fcbe19d3c91c9bf88ac2c27f805000000001976a914b9cc5fee230edcfc9f00edc588067e2e110e782588ac02dfd003000000001976a914d14220b147427921861f244a929e858ac3e8441588ac1a117d02000000001976a9143a8d250f6610a5643ccada659e4af4b75b40e0e488ac2432ee01000000001976a914ccffdbc34b0bdd15fab26ad03a90d49cf87017f688ac622995a7000000001976a914720646458ebb021c6b208b46bbc0851d779a9d8088ac921b6801000000001976a914f9c8b8d4b5d95028bdb0cdd5bef39e3288257f4b88ac337eff21000000001976a91489299dece11409986c4804518861e57e0c444b3e88acd3876202000000001976a914914ca375e719b445ca2ee8802b003f1050e6781d88ac665f4b01000000001976a914a17a960e94054d748a76d9b7acacb5bef677ef9688ac9aa3ad07000000001976a9146d9979d9a6db038313acdf74449307751bee7f0388ac6d556401000000001976a9146be17e31ba3a73f609437e0f93e733ce2d41b7cd88ac5926ac01000000001976a914ce2d996d81fb8a9bad71b83c6f89a99fba3ccf9c88aceca22a0c000000001976a9143cf50bff30c3eba9dbcc17f2ad66cb84fe37018588ac4d2c720a000000001976a914409bd82fbcd9ba7a0bbe7fab36c1e9f5e489dbe388ac14311602000000001976a914d1cadbba6ec2b97ed31449afdfd9b87c25ce47da88ac012f6f03000000001976a9140dd80be06a3aecf00516f239ca082cc56ffadfcc88ac75b01c02000000001976a9145f4928336fb4eb94d21ea051f1ecf21300435f6688ac2be97e85010000001976a9144bd5ae0f68f3116af13529b2507ad90d0a84f5a488ac68ed7002000000001976a9147548c16655a367993cb92c05f541ad6edd4c52ea88ac3cde3c06000000001976a91468f8c97983b48db84b924c007c4f7ef6a39dbb1388acbca29f03000000001976a9141d91c5641755657d8e00804294101b16b6484ee488ac35883f01000000001976a9142a2f4213b27228844c0abf139e31330b257e9c8e88acb0e2f401000000001976a9143109f3dec5e8a39491c6efed3df29db388e3396188ac1fff7901000000001976a91414dd142109a648011acdd1c32873a5183f9bd8a188acf3003208000000001976a9140ab5509a8ae43755137f432c2577411c277368d188ac94b6ec01000000001976a91489354ba206f6452d375118eaa28b20d2eda9cd1788acadc28e01000000001976a91488cec9838e4279beea7b6dee4ff57143c1f8140788ac8a2b2403000000001976a914a15220644236c3ae482f05472e0b1142c3bfd71c88acac6cf807000000001976a914fc5d60504a76cfa8360ac956f2f8ac87ede470d588ac8babd407000000001976a914c5a5133a3ffda8bdf2a332868d9506fed37ea07388ace86a6f01000000001976a914cb2544a40b08e233901fe0308b4bcf42fb6d7bd688ac3bb74601000000001976a91478e0cc28b27fe460855d53eba7b10d00fb0fe84c88ac0f368b01000000001976a9140e8fec8dcff0e30925b2a42045a040471deec3a088acaca61302000000001976a914d457832f17cbf32c8b463023dd8d373d91495eea88ac43aa3b01000000001976a914f77b63ea17fdf4d2d16e9b4b461dbf5079904a1388acb9f30602000000001976a91410d8ba6adb5069ae797564afb926f73095a35fb488ac7c791204000000001976a9140a7127aa1f9c69b6a52b324786d59e3bef86f33888ac50312f03000000001976a914aa8165e2be35b337e41183ac94cab398689773eb88ac05a71811000000001976a91401e841a1a35e3366d148e2d518f044754da4389a88ace2c24e01000000001976a914de68393c8908531973a56c484f7fead7cd41349788ac8f550103000000001976a9147dbfb5d46296212f1041e1448ff013c72703469788ac9284c301000000001976a914a07b9e1ff9eaa88ee3d8839815c8c3dce9fca7e188ac68a7fd01000000001976a914423c144baa2a440e078f84a1335bd275444a9b2288acff151a0c000000001976a9142dc7775bd76d6f84c0792c3c780f53cf7d84ec1c88ac17796202000000001976a914c51e53740cfaf730bff26358d02c3e6211de5f3188ac05b4e10f000000001976a9146c7278f63883b03383a0edcaec89d6b44992303a88ac0c9c4a01000000001976a9145a636a1389634ae2dfafeb8c9418940f6a99dbdf88aced5c4a03000000001976a914f1bfe1fa773abf0aa26a1426a8e0e44404a751c188ac99c54c02000000001976a91421faf8b85b4f9b4d16224d4daa4624d5df9e9abd88ac03708501000000001976a914b9da74145df0b6e32010c0beaee019ac664ffd8588ac7d2f9a05000000001976a9145f30ad3b63f67f035cfdd1eef10fa8ab4a79194c88acb4048102000000001976a9145ad008daec42d1f3e9e8d9fcd098fa17ca22772c88ac468e7903000000001976a91430ef065ed897d7e3dbbfdd6e22ca5a54974ec96388acbdde3f01000000001976a91482b175296af77e954f4603005265fb2a3c0d88fd88ac994a1e03000000001976a9141dd3e545394c62e030e70928aa4a2ce20c3f8d1988ac7d17de02000000001976a914c73181882a45e97ed2b970c3b798a5610154f1db88ac4e374b02000000001976a9144f95d518615b0d2a7afd0a12cf5ce29bcc6c3ebf88ac71f73e0f000000001976a9141a5a628b1336ecc407e11d82503c4d3abbb8aad588ac36921e07000000001976a914efabac8cf6dbb96c18df09c904ed6c4455068e3c88acd9bc8e03000000001976a914f5f586a96b6ecbd3b25ea5816e2bb0f0a6cc29ac88accc959d02000000001976a9142b87994108346bbdede99a59a005ddc16b04804688acf93c9d01000000001976a914dc4d9c3a59bf65cd147b6fafe86a41365623b96e88acad6b5104000000001976a91405eb42dbfb4c2e6719182e98edf88ad867864a5988ac00fa4002000000001976a914841bdef35d081aef9ba473c5bd01201faeb445ab88acf7932206000000001976a914058d8da2adbec9ce71d92405fb005b8fa2f7ad1f88ac44bb9801000000001976a9149a1b5f361e3ebd0414ebfb7255bb6c99cdaa58cb88acd1640b02000000001976a9142889edbcb46a174ae37d5b49d92ea689c62ad73988acd1b1fb01000000001976a9140d150e527e2fc8bd9a4ed51ad528467f11df416d88ac3d194d01000000001976a9143b605b26c2925d320d2f84b2fd3baf656f32f8ec88ac726506007f65060000".into(), + "0400008085202f8901dd82186dd518491dc793f4462c64664b478333015a0f7c18852c5f13f981474f010000006a4730440220186351c7b97213db32b5119de7a2188d2b38022bec4dc3d9336ecb71688a0c040220434f491e37d79a9804acbbeb3b64d093d03264b51f9b40092b1f89be1ae6a855012102010a560c7325827df0212bca20f5cf6556b1345991b6b64b469c616e758230a5ffffffff020a0acf01000000001976a914b11cc427c3e1517ba49f4bde2c3cabfc1ca871d888ac874b00d9000000001976a914c8b56e00740e62449a053c15bdd4809f720b5cb588ac00000000a26506000000000000000000000000".into(), + "030000807082c40301fae7d139e4469fde499b15b8d73a1449fa60002b1939d3aa52bfa35fea2d4b28280000006a4730440220191a63406c15031d2841020a38e99236732a5e7f9901e76719bbaecbce380ab302204cd4b51ed7ac9bf7aa426195beaa8c9bc614b05337a3159d6b538169dbfe82370121021057319f530d97412f40f76c8183109b7948ead50179b9ed3e1e43cdc8dbc5a6feffffff02a9a43500000000001976a914eeaf06a51b139b079c7357ddacc5cf64463ef21388ac6d569202000000001976a914d8bf63402dc79ef4b612a023df412e6c00f646e088acf46406001365060000".into(), + "030000807082c40302933d0974760011bea74f1a6d7cbcc22e53afe77c0d6d3d091193ed8fee65a55e000000006a473044022076c4134bd6e4dd63f3feb72b8408d252cb40bc33aac3de4e0d01d40447e345d602200877b4848a5c54a910d093daad524440b902f79134cb82a01dd3e78d9f07859e0121038b9a767c2eb28bf50f740ff8a5a2ce8a98f06a8f304a022a1d1da72a0caae621feffffff2db0eb5126c15b16c39ff392af0d0bd7b73578385c12cde0fda06351fb5fedde1b0000006b483045022100f33da57c33e5e7d7f95667031a70f109fa3920015324822ef23239edc4bb8bc102202921d6274f1783d504eac213b1b4718479563de52ad64df1d9a107de393b3e16012102759d7334760476409fc0069cfa94afdc762a5bc9f3f54d22eba6c9e0d1abf834feffffffa562f60f00000000001976a9140169e16df86c20f44bb8dd8c06d4a7697115ab7d88ac014b1f00000000001976a914051e9efbad8d4d330eb1c5b9a70bbe6632398cd788ac27b21000000000001976a914055978a4d4376cf7638ad6a609447738c333866688ac0fa33101000000001976a914076a91ab38e50790be602c202cb6de57e9bb66c088acac930f00000000001976a91407bbdadd3c32da2ca740fa28ca0d493fac84e50388acd2dc0f00000000001976a9140a1a7497e560c27b55cb9b71d96300a0a7b4108f88ac11301000000000001976a9140cf187c608ef81ebe97065a04c69043677bba72588ac26e80f00000000001976a9140d299fa9dad355110b7634a00bc2ffc4415c410688ac6a324e00000000001976a9140d6f986a043d108fdaa0d35e87a265d78243a93788acbe1f1000000000001976a9140e20ea5ea2cf1f585cd4280be9e4db5dcfff6a3188acce460f00000000001976a9140f3be6e8494fa0ba7b93b1bd515b39aa90b9342688aca3df8f00000000001976a91412fda64e5551a560a8536bc62d8146a12debfd2c88ac00821300000000001976a914139bf6a5462e28098fba88207dd370025138ef1788ac98e10f00000000001976a91414e5167871c3773b5076c4c2ad70cdd7dccdfcfd88ac53e2b700000000001976a914180612a5ef23f0f55f9820c863e4ba02e740de7488acf2b39800000000001976a9141807c9a2673bddee95d80a51648276d45f9aeb7488accc782e04000000001976a9141a2a23c32912f88e9da94cac0967fcfa74331fb088acb6996c1a000000001976a914260629da1342300b34431ad0f16dae62919f483588acab981100000000001976a9141a524a7074f4759e3692ae387cfd75e8a6bddc2b88acbf420f00000000001976a9141bd43b7937942fbe73a0482052214e8021dab80988aca6be9a00000000001976a9141e4445763ede3ce050a5e04e4715640a023ba18b88ac7b640f00000000001976a9141f9a82ab53c87eb7b535e4ebdd1d313950c4865088ac9fd00f00000000001976a914252dfb373ab53b5348e5bf1ca1cd9ac29c4a2be688accd960f00000000001976a914267c9b1e996280de5895384d61caeabebd759aa788ac6b5d0f00000000001976a91427205ce584a5c88f42ec1d0902a7565290fef50d88acdfd70f00000000001976a914288fdf416fe347aec8b7c9360a74111edb88495388acf5480f00000000001976a91428f57ced8f551127a2b5aea120019e2a16e7b87888acef4d0f00000000001976a9142c8b85c29dbcb682f2a5eae855cb88a9cecee1e388ac389a0f00000000001976a9142e66c36d62899ff93ec4b5154fb6f74a38dba6d188acec591000000000001976a91431408f4e6dd34068acbac5a9885b0cd96111d3d888aca4470f00000000001976a91433964e1687e1e2954deadd9fd98c2c7525a0af6f88ac8db4e600000000001976a91435e95c202ec6f7f5cbd44e5ee68fc4ae73196a5388ac71eb4200000000001976a91438d411a1b9a61b8aaf94f383fc21fd6460c9bbc188ace1480f00000000001976a91439e58377a250838deaeeaae867a3506baf306e0988ac96430f00000000001976a9143a6c664b55521578b32eba41c2862768aeb2df1d88ac44520f00000000001976a9143baecf697f9cf6b39b06c4dbe65a9f81e346df8e88ac98a70f00000000001976a9143ce6733782b4f3e9a2c26b8a5f8dd262224ba20888acb59a1e00000000001976a9143dd7a0ffaebeb0684ccd5cdfab4524c5bd38420e88ac3c720f00000000001976a9144123315b1e193ba4969544bbec8b760c9ae91b0a88ac47ff0f00000000001976a91441f77a0f303386208a256dab2abb1f0b4e688e5588acd77d0f00000000001976a91444bf7e47d6483a74bce5dae833963fbaed594d9588acb3b40100000000001976a91444e4b9f602c81fa9ff7bfe7020cd2210717d36ce88ac3f001100000000001976a91445d5d91bb61d2bb1ccf80df6d0c949a3ca50183888acf3540f00000000001976a9144ab5b2f96e2e78ab8347bfde32560d8bf01ddbdd88ac34d91000000000001976a9144c3846065f524b4bafd7c60e755859b35c8a78e088aced780f00000000001976a9144e8f2d9d77247c2199c18212d9fdaca21a2be07a88ac8e690f00000000001976a914523a60777fe7da02726dba9322027e96f2ff67e088ac4bac0f00000000001976a91452b4ad0f3f54efc8d5c9ab93d1616bd156d691d688ac39a30f00000000001976a914540c5694de9e35fc676dd9354d3eb603736f0b7488ac1c0c1000000000001976a91455a066e81993d982fcd6e64c8617956a7278336c88acbf480f00000000001976a91456505994bd7ca32e7a1f3ceb39c3e1f959c06f3888ac2a5a1000000000001976a91456c84191e11fa58d124a8ecfe1ca7c6902c10df888acf2771100000000001976a9145733e14e3aff4f9cb82c63d7a48133c5ef25a13a88ac60a20f00000000001976a914578e9212a2e23b4c98e8f4fc7c4380f5a778ccae88ac96b18506000000001976a9145bea7667ef3147e147bd2e1109edb61ffe53652588ac4b80f605000000001976a9145edcd9656e882514bfc3525137a283e29fe0740e88ac8fd60f00000000001976a91460b7e4304fa0fda9c310ad7349decf9d27fd6b9188ac62600100000000001976a91462ef016da6d4613499ea026aab2c6ffe578feb6788ac0a9e3700000000001976a91464f9f503fd5d2bca70c347ab4ab5a2c0b0dca88588ac6fde0f00000000001976a914677032a8abaf2cd32aee9f00f231c63de7f3569c88acf7503101000000001976a91469b61f8bf138301951317d0704e83c5dc603f27a88ac8cbb9900000000001976a9146a073fcebb5dc3a44764d6370a11552ad13a950288ac72a80f00000000001976a9147014fc7a765f1ebfa898b993f1836e25e33996b488ac9d470f00000000001976a91470ec6b3caf6396c2cef0590a6b7d5aff5108f62b88ac46412600000000001976a9147263d0801e840fbf908016faa0ecc6a0beb2605c88ac2a5f0f00000000001976a91472acb143750fb6d4c6253c0b9ffaa7893d69dca988ac8b031100000000001976a91473b288f89b728faf3ca2036ae67ae320e95d50e988acd2f10f00000000001976a9147427a849f936ee264c131ea97e7da94066f94af088ac46011000000000001976a914766238a92ab0c69740c05c20806fb3395df3352688ac4c550f00000000001976a9147a584f1d402f5e3fc194108d644bbefe7d91072588ac79a89900000000001976a9147cd6b18acb5e27e65c41acecfe87506f5179836f88acfa5e2100000000001976a9148177e0a5ad88f1d10d7a3e74aeaaea890169602a88acb1a20f00000000001976a914834abddf03d50b7777061d7b2df8aa85b9fd6bc588ac3c4f1200000000001976a914836f6217a7c3e1d3a4428060041cded94d812e5b88ac1bb10f00000000001976a9148581d7fe9fabb655c0e7aaed276f744e17bde6ac88acd0ec1000000000001976a9148697db8f3d23af4eea5be02b05e18f1b3ac5e0b788ac41750f00000000001976a91486c3c02f0f8d368eeb608bd7809b3b41c055e19288ac4dcb0f00000000001976a914876bb1d0a4a715bad322212bdb5b393bae06e91388ac27bd0f00000000001976a91487bbe138362df053e99394c357ca5f4e7d9551cf88ac274e0f00000000001976a91487f1670afff660153365c1414c7792e1eee6698b88ac6be00f00000000001976a914891fcc38e0bc595e3e0b0bf5774aa32c0669e32588ac0f884f00000000001976a91489916cbbe6073df5666eeec45b325127c7b23d5288ac98ce5100000000001976a9148d9b2ed05a38825a55cc1b0a12d0ff6f08694af588ac274e0f00000000001976a9148e14723bf77b7a69877fe044feb001cf4f52de0788ac1eff1200000000001976a9148e9a2df8a28c0310ee5b8b70234d655f6dc63f9c88acfe460f00000000001976a9148f6d9d6dd1d28ce02cd791596dce114bc564b7d688ac364c0f00000000001976a91490b56ff13873f24b921f191860e390da09afd7d288ac69891100000000001976a914919c98f84a002734a4317e9c354f9e14ff341b1088ac839f0f00000000001976a9149274fc5bc447f20d904931ee7c3f8cd381a967cb88ac8e641601000000001976a91493e5b808e0598a9a594ceef0fb38cfc8e7fd536a88acc7420f00000000001976a91496aebfcfe16724ccc18e5a52fec047a0c5ddfe9388acd6b20f00000000001976a91498ad4db7a9be834f266ca65afc84decafcf5fda088ac4dde0f00000000001976a91499190ab5b10234c4e4e5a7d5873ec0d5a74371cc88ac47430f00000000001976a9149a015c39be49a3ebd01440772bd14e129da31d8388ac9039fa05000000001976a9149b70c75d2ba3f609d73180811369bce744b56b4e88acf2490f00000000001976a9149bc8c02335c3631055475d74df4a23c3ad58296088aca49e1700000000001976a9149d8828fe0bad2f0d25a41a1c001e360aa79a747c88ac1a633d00000000001976a9149f3c9ec24b891e1ab4e2a1391926868b65c90afe88acd374fd05000000001976a914a27fbaab942a1d708d98c2124801d7fd78e7bc7e88acda580f00000000001976a914a30e987ad3af0ebaf497199faf371d3436b5a2e188acec05c504000000001976a914a3830b4b6c0519f7286ea8c1de18e2316cbfbdb388ace7ed2300000000001976a914a9689485a7070f922f5d5595328224a61c4dc03488ac3fdf1000000000001976a914a9782a584de62c08255a869d63aadf256ca3275d88acd80c1a00000000001976a914ad3f9c6d809c72dc9c5a5feb2309da05dee9b07288ac22c51000000000001976a914ada9be458c31462376069e5268853fa2b6bdeff988acf05f0f00000000001976a914afdac56c1dbb12c4d3d468038b36429cfc2c9eec88ac86a11000000000001976a914b072bf167d7265e69ef8b2a6e32e29bc88a0f8fb88ac92531100000000001976a914b18d919a90f71466e0d6e9acd615923f6d1d89e388aca5e40c03000000001976a914b1e69b1c257a7ad185901555121ff72a0d8d23aa88acc5913101000000001976a914b36cd2adc304e768e97af4b71105b94fac3566e288ac3d81e800000000001976a914b39c1c58aa84e24f83d519232c8f4b19df4d162388acdb490f00000000001976a914b55497a08a42e7f3468b22d3e970884154462ffe88acf4400100000000001976a914b597856b6595a4fb8705d7fb7ba29f3b4bc6ea8b88ace1dc0700000000001976a914b61a25c84ec59a879133576de9c68f609197437888ac76ec2500000000001976a914b6a07e6302e8eba1cf1d2f8d1cabcd62c3ed99c588ac58e91000000000001976a914bb7956d1471f4af8ad73fe077e8b43074fddb6b288ac6f440f00000000001976a914bc42f9ac6ba420e9027802b58994e2aa7125ce0788acce401000000000001976a914bf04cef7b751e93c45cf5688b7ea3e970391bc6e88ac57900f00000000001976a914c103f2b3ec0d4517fee856c61a870e75144967b788acf4b31000000000001976a914c1e6d5856e9fe6834b6f8f6eb2e17d86ce09b52988ac84d7fc02000000001976a914c25f96d0bd254df26e870ebdd687b954a61d750788ac75660a00000000001976a914c2aae3c9239072b5ef9b6298eebc56208f507a7888acd86a1100000000001976a914c2bf47c7442b13efdfe8e5041bcd5dc3f06cfc7e88ac94940f00000000001976a914c3582445f04959dd887127e341c57694feb8454c88ac2bd71100000000001976a914c381496b80a0dd352f6b1998a61081d817a8801d88acf1b91100000000001976a914c44da14c810237ae9a86c2338910c571efd8802788ac5a143a00000000001976a914c551c81a29a3155d48dd82f24501904ea468665588ac63a30f00000000001976a914c735bbd1fc258d39abe7483f5608256995778b7d88ac72f04c00000000001976a914c87f97f1aaa6ba109e4ed8dabed31bdaf2f416a788ac5e261f00000000001976a914c989a3e1b743a394983e49702545c0ad00a709ac88acc4bc8500000000001976a914c9e6ab854cd3bff4c42d92ff3b98a2be42b1e59588acea371000000000001976a914cad5838984091084f6304305053b9b2f2208dc7588ac809a0f00000000001976a914cad737b9b0677d45317c2a6cc3a0dac2ab89753e88ac074d1100000000001976a914cbd12a5cba7701959b2e8f6190061b652ac1642088acf1d15400000000001976a914cc3c84aedfbb5580e73785ec9a447224ba8da3f988acee440f00000000001976a914ce14b903a70cccb86e09ebddb082389134e0ffe388acb9d40f00000000001976a914cf30145521f2aef4e792cd0aeaf24147496d33cc88ac50784c00000000001976a914d05057881c605db68b6364f68fc26801da1058e888ac8aa54c00000000001976a914d56d237b10781ed68f3ada178c7c5da51317e8a688acaf4b1800000000001976a914dd5584fef1ab439a7865f7bca2f64d62929757bc88acf1891c00000000001976a914de3918025bfb4e92186a921372cccbce08d0c15288accc430f00000000001976a914de519a26703a655cb4ebd7ce6b0118e42c2dfa5888ac67840f00000000001976a914df5ab8d09929c740454dd28ea86a5ba9dce891b088acb5450f00000000001976a914df6ec00942533dbbb7a32abf77e6e72df53be80088ac1c9a1000000000001976a914dfffefbfd61760cc54439d56a18c5f60ff355f8788ac11da0f00000000001976a914e1822629e1e48fc71d9b8b7bb70acc72efb49cb188ac67831600000000001976a914e1d015d15169a3bf15c3a9492b4ec9a8355146ec88acce7e0f00000000001976a914e1ede9a67f1565fd8eef5438ba102fdde1a437ed88ac9e5d0f00000000001976a914e287581e403ccfb300f2cd4e86b82088511740c588ac48d62400000000001976a914e29c11748114a121bb9e1748a9180abb72c3e96988acae500f00000000001976a914e338dc19eb4aaaa28801d9149e62987edc517b3f88ac0c840f00000000001976a914e347b4e68554c18c24228caef7fa6cbd6d24141b88ac24580f00000000001976a914e8bc90f4875134b5bd6cc36ad098fc02a86fe89088ac2e520f00000000001976a914e9c93bd384f9462607753b822890d98b26c3be8688acf6361000000000001976a914ea08453aa6cef34fa8de6a57a68a088f119a50e788ac3fdf1000000000001976a914eb2376d99f066cf1ac716eb567cd4e026703804b88ac98dd0f00000000001976a914eeebd92f87eefc4220b56dc3259cd7bd7755248788ac9ecf1000000000001976a914f52220601aae51f5e6db37eefc7520d4af44435b88acb8b6f805000000001976a914f53bc45ffcbba6c516f32a6a809386fe13edf06c88acb6670f00000000001976a914f8bb96c092f6b2738458fc419931e927c5c916d188ac454b4c00000000001976a914fd450f7a15049cff109d9c7e8364421abbddfee888ac97ea0f00000000001976a914fe992ed3ea31c97130f7cffe6c42ae8522a1306b88ac295d0f00000000001976a914fec6c76e1a9b9964eb422ccdd261d49781ee1b7b88ac152e10000000000017a9141ed378d5d94e0be0160c717f14cc8c8609b06aa48729430f000000000017a914ba09f5351a984d12133e4e4a5ae16a8d3c39aa638795640600b464060000".into(), + ], + ) +} diff --git a/verification/src/accept_block.rs b/verification/src/accept_block.rs index 9af99f2d..8230b450 100644 --- a/verification/src/accept_block.rs +++ b/verification/src/accept_block.rs @@ -180,7 +180,7 @@ impl<'a> BlockCoinbaseMinerReward<'a> { let prevout = store.transaction_output(&input.previous_output, tx_idx); let (sum, overflow) = incoming.overflowing_add(prevout.map(|o| o.value).unwrap_or(0)); if overflow { - return Err(Error::ReferencedInputsSumOverflow) + return Err(Error::Transaction(tx_idx, TransactionError::Overspend)); } incoming = sum; } @@ -191,17 +191,52 @@ impl<'a> BlockCoinbaseMinerReward<'a> { .sum::(); incoming = match incoming.overflowing_add(join_split_public_new) { - (_, true) => return Err(Error::ReferencedInputsSumOverflow), + (_, true) => return Err(Error::Transaction(tx_idx, TransactionError::Overspend)), (incoming, _) => incoming, }; + if let Some(ref sapling) = tx.raw.sapling { + if sapling.balancing_value > 0 { + let balancing_value = sapling.balancing_value as u64; + + incoming = match incoming.overflowing_add(balancing_value) { + (_, true) => return Err(Error::Transaction(tx_idx, TransactionError::Overspend)), + (incoming, _) => incoming, + }; + } + } + // (2) Total sum of all outputs - let spends = tx.raw.total_spends(); + let mut spends = tx.raw.total_spends(); + + let join_split_public_old = tx.raw.join_split.iter() + .flat_map(|js| &js.descriptions) + .map(|d| d.value_pub_old) + .sum::(); + + spends = match spends.overflowing_add(join_split_public_old) { + (_, true) => return Err(Error::Transaction(tx_idx, TransactionError::Overspend)), + (spends, _) => spends, + }; + + if let Some(ref sapling) = tx.raw.sapling { + if sapling.balancing_value < 0 { + let balancing_value = match sapling.balancing_value.checked_neg() { + Some(balancing_value) => balancing_value as u64, + None => return Err(Error::Transaction(tx_idx, TransactionError::Overspend)), + }; + + spends = match spends.overflowing_add(balancing_value) { + (_, true) => return Err(Error::Transaction(tx_idx, TransactionError::Overspend)), + (spends, _) => spends, + }; + } + } // Difference between (1) and (2) let (difference, overflow) = incoming.overflowing_sub(spends); if overflow { - return Err(Error::Transaction(tx_idx, TransactionError::Overspend)) + return Err(Error::Transaction(tx_idx, TransactionError::Overspend)); } // Adding to total fees (with possible overflow) @@ -355,10 +390,13 @@ impl<'a> BlockSaplingRoot<'a> { mod tests { extern crate test_data; + use std::collections::HashMap; + use chain::{OutPoint, TransactionOutput}; use db::BlockChainDatabase; - use storage::SaplingTreeState; + use network::{ConsensusParams, Network}; + use storage::{SaplingTreeState, TransactionOutputProvider}; use {Error, CanonBlock}; - use super::{BlockCoinbaseScript, BlockSaplingRoot}; + use super::{BlockCoinbaseScript, BlockSaplingRoot, BlockCoinbaseMinerReward}; #[test] fn test_block_coinbase_script() { @@ -423,4 +461,30 @@ mod tests { actual: "0000000000000000000000000000000000000000000000000000000000000000".into(), })); } + + #[test] + fn test_coinbase_overspend_b419221() { + struct Store(HashMap); + + impl TransactionOutputProvider for Store { + fn transaction_output(&self, outpoint: &OutPoint, _transaction_index: usize) -> Option { + self.0.get(outpoint).cloned() + } + + fn is_spent(&self, _outpoint: &OutPoint) -> bool { + false + } + } + + let (block, donors) = test_data::block_h419221_with_donors(); + let store = Store(donors.into_iter().flat_map(|donor| { + let hash = donor.hash(); + donor.outputs.into_iter().enumerate().map(move |(index, output)| (OutPoint { + hash: hash.clone(), + index: index as u32, + }, output)) + }).collect()); + let consensus = ConsensusParams::new(Network::Mainnet); + assert_eq!(BlockCoinbaseMinerReward::new(CanonBlock::new(&block.into()), &store, &consensus, 419221).check(), Ok(())); + } } diff --git a/verification/src/error.rs b/verification/src/error.rs index 4388aabd..97fa03fd 100644 --- a/verification/src/error.rs +++ b/verification/src/error.rs @@ -49,8 +49,6 @@ pub enum Error { TransactionFeeAndRewardOverflow, /// Sum of the transaction fees in block exceeds u64::max TransactionFeesOverflow, - /// Sum of all referenced outputs in block transactions resulted in the overflow - ReferencedInputsSumOverflow, /// Non-canonical tranasctions ordering within block NonCanonicalTransactionOrdering, /// Database error From a0c6a510bf72b43a328cbdcb40c1cbd5aafc4684 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 15 Apr 2019 13:04:10 +0300 Subject: [PATCH 02/12] use different errors for inputs/outputs overflow --- verification/src/accept_block.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/verification/src/accept_block.rs b/verification/src/accept_block.rs index 8230b450..cab6db67 100644 --- a/verification/src/accept_block.rs +++ b/verification/src/accept_block.rs @@ -180,7 +180,7 @@ impl<'a> BlockCoinbaseMinerReward<'a> { let prevout = store.transaction_output(&input.previous_output, tx_idx); let (sum, overflow) = incoming.overflowing_add(prevout.map(|o| o.value).unwrap_or(0)); if overflow { - return Err(Error::Transaction(tx_idx, TransactionError::Overspend)); + return Err(Error::Transaction(tx_idx, TransactionError::InputValueOverflow)); } incoming = sum; } @@ -191,7 +191,7 @@ impl<'a> BlockCoinbaseMinerReward<'a> { .sum::(); incoming = match incoming.overflowing_add(join_split_public_new) { - (_, true) => return Err(Error::Transaction(tx_idx, TransactionError::Overspend)), + (_, true) => return Err(Error::Transaction(tx_idx, TransactionError::InputValueOverflow)), (incoming, _) => incoming, }; @@ -200,7 +200,7 @@ impl<'a> BlockCoinbaseMinerReward<'a> { let balancing_value = sapling.balancing_value as u64; incoming = match incoming.overflowing_add(balancing_value) { - (_, true) => return Err(Error::Transaction(tx_idx, TransactionError::Overspend)), + (_, true) => return Err(Error::Transaction(tx_idx, TransactionError::InputValueOverflow)), (incoming, _) => incoming, }; } @@ -215,7 +215,7 @@ impl<'a> BlockCoinbaseMinerReward<'a> { .sum::(); spends = match spends.overflowing_add(join_split_public_old) { - (_, true) => return Err(Error::Transaction(tx_idx, TransactionError::Overspend)), + (_, true) => return Err(Error::Transaction(tx_idx, TransactionError::OutputValueOverflow)), (spends, _) => spends, }; @@ -223,11 +223,11 @@ impl<'a> BlockCoinbaseMinerReward<'a> { if sapling.balancing_value < 0 { let balancing_value = match sapling.balancing_value.checked_neg() { Some(balancing_value) => balancing_value as u64, - None => return Err(Error::Transaction(tx_idx, TransactionError::Overspend)), + None => return Err(Error::Transaction(tx_idx, TransactionError::OutputValueOverflow)), }; spends = match spends.overflowing_add(balancing_value) { - (_, true) => return Err(Error::Transaction(tx_idx, TransactionError::Overspend)), + (_, true) => return Err(Error::Transaction(tx_idx, TransactionError::OutputValueOverflow)), (spends, _) => spends, }; } From 7cc25b7e6c48267ca476f36143e2b4a0964cec20 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 15 Apr 2019 19:03:55 +0300 Subject: [PATCH 03/12] unify fee calculation --- miner/src/fee.rs | 58 +++---------- verification/src/accept_block.rs | 71 ++-------------- verification/src/accept_transaction.rs | 26 +----- verification/src/error.rs | 23 ++++++ verification/src/fee.rs | 110 +++++++++++++++++++++++++ verification/src/lib.rs | 4 +- 6 files changed, 157 insertions(+), 135 deletions(-) create mode 100644 verification/src/fee.rs diff --git a/miner/src/fee.rs b/miner/src/fee.rs index 66278935..1fc8d172 100644 --- a/miner/src/fee.rs +++ b/miner/src/fee.rs @@ -1,6 +1,7 @@ use chain::Transaction; use ser::Serializable; use storage::{TransactionOutputProvider, DuplexTransactionOutputProvider}; +use verification::checked_transaction_fee; use MemoryPool; /// Transaction fee calculator for memory pool @@ -32,43 +33,12 @@ impl MemoryPoolFeeCalculator for NonZeroFeeCalculator { } } -/// Compute miner fee for given transaction. +/// Compute miner fee for given (memory pool) transaction. /// -/// It could return a wrong value (that should have overflow/underflow) if either outputs sum, -/// inputs sum or their difference overflows/underflows. But since it is used for prioritizing -/// verified transactions && verification checks that values are correct, the call is safe. -pub fn transaction_fee(store: &TransactionOutputProvider, transaction: &Transaction) -> u64 { - let mut inputs_sum = transaction.inputs.iter().map(|input| - store.transaction_output(&input.previous_output, ::std::usize::MAX) - .expect("transaction must be verified by caller") - .value) - .fold(0u64, |acc, value| acc.saturating_add(value)); - if let Some(ref join_split) = transaction.join_split { - let js_value_pub_new = join_split.descriptions.iter() - .fold(0u64, |acc, jsd| acc.saturating_add(jsd.value_pub_new)); - inputs_sum = inputs_sum.saturating_add(js_value_pub_new); - } - if let Some(ref sapling) = transaction.sapling { - if sapling.balancing_value > 0 { - inputs_sum = inputs_sum.saturating_add(sapling.balancing_value as u64); - } - } - - let mut outputs_sum = transaction.outputs.iter().map(|output| output.value) - .fold(0u64, |acc, value| acc.saturating_add(value)); - if let Some(ref join_split) = transaction.join_split { - let js_value_pub_old = join_split.descriptions.iter() - .fold(0u64, |acc, jsd| acc.saturating_add(jsd.value_pub_old)); - outputs_sum = outputs_sum.saturating_add(js_value_pub_old); - } - if let Some(ref sapling) = transaction.sapling { - if sapling.balancing_value < 0 { - inputs_sum = inputs_sum.saturating_add(sapling.balancing_value - .checked_neg().unwrap_or(::std::i64::MAX) as u64); - } - } - - inputs_sum.saturating_sub(outputs_sum) +/// If any error occurs during computation, zero fee is returned. Normally, zero fee +/// transactions are not accepted to the memory pool. +pub fn transaction_fee(store: &TransactionOutputProvider, tx: &Transaction) -> u64 { + checked_transaction_fee(store, ::std::usize::MAX, tx).unwrap_or(0) } pub fn transaction_fee_rate(store: &TransactionOutputProvider, tx: &Transaction) -> u64 { @@ -77,15 +47,13 @@ pub fn transaction_fee_rate(store: &TransactionOutputProvider, tx: &Transaction) #[cfg(test)] mod tests { - extern crate test_data; - use std::sync::Arc; - use storage::{AsSubstore}; + use storage::AsSubstore; use db::BlockChainDatabase; - use super::*; + use super::transaction_fee_rate; #[test] - fn test_transaction_fee() { + fn transaction_fee_rate_works() { let b0 = test_data::block_builder().header().nonce(1.into()).build() .transaction() .output().value(1_000_000).build() @@ -104,11 +72,9 @@ mod tests { let tx2 = b1.transactions[0].clone(); let db = Arc::new(BlockChainDatabase::init_test_chain(vec![b0.into(), b1.into()])); + let store = db.as_transaction_output_provider(); - assert_eq!(transaction_fee(db.as_transaction_output_provider(), &tx0), 0); - assert_eq!(transaction_fee(db.as_transaction_output_provider(), &tx2), 500_000); - - assert_eq!(transaction_fee_rate(db.as_transaction_output_provider(), &tx0), 0); - assert_eq!(transaction_fee_rate(db.as_transaction_output_provider(), &tx2), 4_901); + assert_eq!(transaction_fee_rate(store, &tx0), 0); + assert_eq!(transaction_fee_rate(store, &tx2), 4_901); } } diff --git a/verification/src/accept_block.rs b/verification/src/accept_block.rs index cab6db67..e9aaf9b2 100644 --- a/verification/src/accept_block.rs +++ b/verification/src/accept_block.rs @@ -6,8 +6,9 @@ use script::{self, Builder}; use sigops::transaction_sigops; use deployments::BlockDeployments; use canon::CanonBlock; -use error::{Error, TransactionError}; +use error::Error; use timestamp::median_timestamp; +use fee::checked_transaction_fee; /// Flexible verification of ordered block pub struct BlockAcceptor<'a> { @@ -174,73 +175,11 @@ impl<'a> BlockCoinbaseMinerReward<'a> { let mut fees: u64 = 0; for (tx_idx, tx) in self.block.transactions.iter().enumerate().skip(1) { - // (1) Total sum of all referenced outputs - let mut incoming: u64 = 0; - for input in tx.raw.inputs.iter() { - let prevout = store.transaction_output(&input.previous_output, tx_idx); - let (sum, overflow) = incoming.overflowing_add(prevout.map(|o| o.value).unwrap_or(0)); - if overflow { - return Err(Error::Transaction(tx_idx, TransactionError::InputValueOverflow)); - } - incoming = sum; - } - - let join_split_public_new = tx.raw.join_split.iter() - .flat_map(|js| &js.descriptions) - .map(|d| d.value_pub_new) - .sum::(); - - incoming = match incoming.overflowing_add(join_split_public_new) { - (_, true) => return Err(Error::Transaction(tx_idx, TransactionError::InputValueOverflow)), - (incoming, _) => incoming, - }; - - if let Some(ref sapling) = tx.raw.sapling { - if sapling.balancing_value > 0 { - let balancing_value = sapling.balancing_value as u64; - - incoming = match incoming.overflowing_add(balancing_value) { - (_, true) => return Err(Error::Transaction(tx_idx, TransactionError::InputValueOverflow)), - (incoming, _) => incoming, - }; - } - } - - // (2) Total sum of all outputs - let mut spends = tx.raw.total_spends(); - - let join_split_public_old = tx.raw.join_split.iter() - .flat_map(|js| &js.descriptions) - .map(|d| d.value_pub_old) - .sum::(); - - spends = match spends.overflowing_add(join_split_public_old) { - (_, true) => return Err(Error::Transaction(tx_idx, TransactionError::OutputValueOverflow)), - (spends, _) => spends, - }; - - if let Some(ref sapling) = tx.raw.sapling { - if sapling.balancing_value < 0 { - let balancing_value = match sapling.balancing_value.checked_neg() { - Some(balancing_value) => balancing_value as u64, - None => return Err(Error::Transaction(tx_idx, TransactionError::OutputValueOverflow)), - }; - - spends = match spends.overflowing_add(balancing_value) { - (_, true) => return Err(Error::Transaction(tx_idx, TransactionError::OutputValueOverflow)), - (spends, _) => spends, - }; - } - } - - // Difference between (1) and (2) - let (difference, overflow) = incoming.overflowing_sub(spends); - if overflow { - return Err(Error::Transaction(tx_idx, TransactionError::Overspend)); - } + let tx_fee = checked_transaction_fee(&store, tx_idx, &tx.raw) + .map_err(|fee_err| Error::Transaction(tx_idx, fee_err.into()))?; // Adding to total fees (with possible overflow) - let (sum, overflow) = fees.overflowing_add(difference); + let (sum, overflow) = fees.overflowing_add(tx_fee); if overflow { return Err(Error::TransactionFeesOverflow) } diff --git a/verification/src/accept_transaction.rs b/verification/src/accept_transaction.rs index 71daf77a..4d0eb06f 100644 --- a/verification/src/accept_transaction.rs +++ b/verification/src/accept_transaction.rs @@ -12,7 +12,7 @@ use chain::{OVERWINTER_TX_VERSION, SAPLING_TX_VERSION, OVERWINTER_TX_VERSION_GRO use constants::COINBASE_MATURITY; use error::TransactionError; use primitives::hash::H256; -use VerificationLevel; +use {checked_transaction_fee, VerificationLevel}; use tree_cache::TreeCache; pub struct TransactionAcceptor<'a> { @@ -22,7 +22,6 @@ pub struct TransactionAcceptor<'a> { pub bip30: TransactionBip30<'a>, pub missing_inputs: TransactionMissingInputs<'a>, pub maturity: TransactionMaturity<'a>, - pub overspent: TransactionOverspent<'a>, pub double_spent: TransactionDoubleSpend<'a>, pub eval: TransactionEval<'a>, pub join_split: JoinSplitVerification<'a>, @@ -54,7 +53,6 @@ impl<'a> TransactionAcceptor<'a> { bip30: TransactionBip30::new_for_sync(transaction, meta_store), missing_inputs: TransactionMissingInputs::new(transaction, output_store, transaction_index), maturity: TransactionMaturity::new(transaction, meta_store, height), - overspent: TransactionOverspent::new(transaction, output_store), double_spent: TransactionDoubleSpend::new(transaction, output_store), eval: TransactionEval::new(transaction, output_store, consensus, verification_level, height, time, deployments), join_split: JoinSplitVerification::new(consensus, transaction, nullifier_tracker, tree_state_provider), @@ -74,7 +72,6 @@ impl<'a> TransactionAcceptor<'a> { self.bip30.check()?; self.missing_inputs.check()?; self.maturity.check()?; - self.overspent.check()?; self.double_spent.check()?; // to make sure we're using the sighash-cache, let's make all sighash-related @@ -274,24 +271,9 @@ impl<'a> TransactionOverspent<'a> { return Ok(()); } - let available_public = self.transaction.raw.inputs.iter() - .map(|input| self.store.transaction_output(&input.previous_output, usize::max_value()).map(|o| o.value).unwrap_or(0)) - .sum::(); - - let available_join_split = self.transaction.raw.join_split.iter() - .flat_map(|js| &js.descriptions) - .map(|d| d.value_pub_new) - .sum::(); - - let total_available = available_public + available_join_split; - - let spends = self.transaction.raw.total_spends(); - - if spends > total_available { - Err(TransactionError::Overspend) - } else { - Ok(()) - } + checked_transaction_fee(&self.store, ::std::usize::MAX, &self.transaction.raw) + .map_err(Into::into) + .map(|_| ()) } } diff --git a/verification/src/error.rs b/verification/src/error.rs index 97fa03fd..091575c0 100644 --- a/verification/src/error.rs +++ b/verification/src/error.rs @@ -154,3 +154,26 @@ pub enum TransactionError { UnknownAnchor(H256), } +#[derive(Debug, PartialEq)] +/// Fee calculation error. +pub enum FeeError { + /// Transaction misses input. + MissingInput(usize), + /// Inputs sum overflow. + InputsOverflow, + /// Outputs sum overflow. + OutputsOverflow, + /// Negative fee. + NegativeFee, +} + +impl From for TransactionError { + fn from(error: FeeError) -> TransactionError { + match error { + FeeError::MissingInput(idx) => TransactionError::Input(idx), + FeeError::InputsOverflow => TransactionError::InputValueOverflow, + FeeError::OutputsOverflow => TransactionError::OutputValueOverflow, + FeeError::NegativeFee => TransactionError::Overspend, + } + } +} diff --git a/verification/src/fee.rs b/verification/src/fee.rs new file mode 100644 index 00000000..ac31c0d8 --- /dev/null +++ b/verification/src/fee.rs @@ -0,0 +1,110 @@ +use chain::Transaction; +use storage::TransactionOutputProvider; +use FeeError; + +/// Compute miner fee for given transaction. +/// +/// Returns None if overflow/underflow happens during computation. Missed prevout +/// is treated as 0-value. +pub fn checked_transaction_fee(store: &TransactionOutputProvider, tx_idx: usize, tx: &Transaction) -> Result { + // (1) Total sum of all transparent + shielded inputs + let mut incoming: u64 = 0; + for (input_idx, input) in tx.inputs.iter().enumerate() { + let prevout = match store.transaction_output(&input.previous_output, tx_idx) { + Some(prevout) => prevout, + None => return Err(FeeError::MissingInput(input_idx)), + }; + incoming = match incoming.checked_add(prevout.value) { + Some(incoming) => incoming, + None => return Err(FeeError::InputsOverflow), + }; + } + + if let Some(ref join_split) = tx.join_split { + for js_desc in &join_split.descriptions { + incoming = match incoming.checked_add(js_desc.value_pub_new) { + Some(incoming) => incoming, + None => return Err(FeeError::InputsOverflow), + }; + } + } + + if let Some(ref sapling) = tx.sapling { + if sapling.balancing_value > 0 { + let balancing_value = sapling.balancing_value as u64; + + incoming = match incoming.checked_add(balancing_value) { + Some(incoming) => incoming, + None => return Err(FeeError::InputsOverflow), + }; + } + } + + // (2) Total sum of all outputs + let mut spends = tx.total_spends(); + + if let Some(ref join_split) = tx.join_split { + for js_desc in &join_split.descriptions { + spends = match spends.checked_add(js_desc.value_pub_old) { + Some(spends) => spends, + None => return Err(FeeError::OutputsOverflow), + }; + } + } + + if let Some(ref sapling) = tx.sapling { + if sapling.balancing_value < 0 { + let balancing_value = match sapling.balancing_value.checked_neg() { + Some(balancing_value) => balancing_value as u64, + None => return Err(FeeError::OutputsOverflow), + }; + + spends = match spends.checked_add(balancing_value) { + Some(spends) => spends, + None => return Err(FeeError::OutputsOverflow), + }; + } + } + + // (3) Fee is the difference between (1) and (2) + match incoming.checked_sub(spends) { + Some(fee) => Ok(fee), + None => Err(FeeError::NegativeFee), + } +} + +#[cfg(test)] +mod tests { + extern crate test_data; + + use std::sync::Arc; + use storage::AsSubstore; + use db::BlockChainDatabase; + use super::*; + + #[test] + fn test_transaction_fee() { + let b0 = test_data::block_builder().header().nonce(1.into()).build() + .transaction() + .output().value(1_000_000).build() + .output().value(2_000_000).build() + .build() + .build(); + let tx0 = b0.transactions[0].clone(); + let tx0_hash = tx0.hash(); + let b1 = test_data::block_builder().header().parent(b0.hash().clone()).nonce(2.into()).build() + .transaction() + .input().hash(tx0_hash.clone()).index(0).build() + .input().hash(tx0_hash).index(1).build() + .output().value(2_500_000).build() + .build() + .build(); + let tx2 = b1.transactions[0].clone(); + + let db = Arc::new(BlockChainDatabase::init_test_chain(vec![b0.into(), b1.into()])); + let store = db.as_transaction_output_provider(); + + assert_eq!(checked_transaction_fee(store, ::std::usize::MAX, &tx0), Err(FeeError::NegativeFee)); + assert_eq!(checked_transaction_fee(store, ::std::usize::MAX, &tx2), Ok(500_000)); + } +} diff --git a/verification/src/lib.rs b/verification/src/lib.rs index 57a17ae3..b3e42017 100644 --- a/verification/src/lib.rs +++ b/verification/src/lib.rs @@ -85,6 +85,7 @@ mod canon; mod deployments; mod equihash; mod error; +mod fee; mod sapling; mod sigops; mod sprout; @@ -122,7 +123,8 @@ pub use verify_header::HeaderVerifier; pub use verify_transaction::{TransactionVerifier, MemoryPoolTransactionVerifier}; pub use chain_verifier::BackwardsCompatibleChainVerifier; -pub use error::{Error, TransactionError}; +pub use error::{Error, TransactionError, FeeError}; +pub use fee::checked_transaction_fee; pub use sigops::transaction_sigops; pub use timestamp::{median_timestamp, median_timestamp_inclusive}; pub use work::{work_required, is_valid_proof_of_work, is_valid_proof_of_work_hash}; From dde96e7ecdbf46273220e06eb31d4f993846dbcd Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 15 Apr 2019 19:10:32 +0300 Subject: [PATCH 04/12] removed redundant FeeError --- verification/src/accept_block.rs | 2 +- verification/src/accept_transaction.rs | 1 - verification/src/error.rs | 24 ------------------------ verification/src/fee.rs | 22 +++++++++++----------- verification/src/lib.rs | 2 +- 5 files changed, 13 insertions(+), 38 deletions(-) diff --git a/verification/src/accept_block.rs b/verification/src/accept_block.rs index e9aaf9b2..f9771f34 100644 --- a/verification/src/accept_block.rs +++ b/verification/src/accept_block.rs @@ -176,7 +176,7 @@ impl<'a> BlockCoinbaseMinerReward<'a> { for (tx_idx, tx) in self.block.transactions.iter().enumerate().skip(1) { let tx_fee = checked_transaction_fee(&store, tx_idx, &tx.raw) - .map_err(|fee_err| Error::Transaction(tx_idx, fee_err.into()))?; + .map_err(|tx_err| Error::Transaction(tx_idx, tx_err))?; // Adding to total fees (with possible overflow) let (sum, overflow) = fees.overflowing_add(tx_fee); diff --git a/verification/src/accept_transaction.rs b/verification/src/accept_transaction.rs index 4d0eb06f..de4c8323 100644 --- a/verification/src/accept_transaction.rs +++ b/verification/src/accept_transaction.rs @@ -272,7 +272,6 @@ impl<'a> TransactionOverspent<'a> { } checked_transaction_fee(&self.store, ::std::usize::MAX, &self.transaction.raw) - .map_err(Into::into) .map(|_| ()) } } diff --git a/verification/src/error.rs b/verification/src/error.rs index 091575c0..e88931f8 100644 --- a/verification/src/error.rs +++ b/verification/src/error.rs @@ -153,27 +153,3 @@ pub enum TransactionError { /// Unknown anchor used in join split UnknownAnchor(H256), } - -#[derive(Debug, PartialEq)] -/// Fee calculation error. -pub enum FeeError { - /// Transaction misses input. - MissingInput(usize), - /// Inputs sum overflow. - InputsOverflow, - /// Outputs sum overflow. - OutputsOverflow, - /// Negative fee. - NegativeFee, -} - -impl From for TransactionError { - fn from(error: FeeError) -> TransactionError { - match error { - FeeError::MissingInput(idx) => TransactionError::Input(idx), - FeeError::InputsOverflow => TransactionError::InputValueOverflow, - FeeError::OutputsOverflow => TransactionError::OutputValueOverflow, - FeeError::NegativeFee => TransactionError::Overspend, - } - } -} diff --git a/verification/src/fee.rs b/verification/src/fee.rs index ac31c0d8..ea4011d5 100644 --- a/verification/src/fee.rs +++ b/verification/src/fee.rs @@ -1,22 +1,22 @@ use chain::Transaction; use storage::TransactionOutputProvider; -use FeeError; +use TransactionError; /// Compute miner fee for given transaction. /// /// Returns None if overflow/underflow happens during computation. Missed prevout /// is treated as 0-value. -pub fn checked_transaction_fee(store: &TransactionOutputProvider, tx_idx: usize, tx: &Transaction) -> Result { +pub fn checked_transaction_fee(store: &TransactionOutputProvider, tx_idx: usize, tx: &Transaction) -> Result { // (1) Total sum of all transparent + shielded inputs let mut incoming: u64 = 0; for (input_idx, input) in tx.inputs.iter().enumerate() { let prevout = match store.transaction_output(&input.previous_output, tx_idx) { Some(prevout) => prevout, - None => return Err(FeeError::MissingInput(input_idx)), + None => return Err(TransactionError::Input(input_idx)), }; incoming = match incoming.checked_add(prevout.value) { Some(incoming) => incoming, - None => return Err(FeeError::InputsOverflow), + None => return Err(TransactionError::InputValueOverflow), }; } @@ -24,7 +24,7 @@ pub fn checked_transaction_fee(store: &TransactionOutputProvider, tx_idx: usize, for js_desc in &join_split.descriptions { incoming = match incoming.checked_add(js_desc.value_pub_new) { Some(incoming) => incoming, - None => return Err(FeeError::InputsOverflow), + None => return Err(TransactionError::InputValueOverflow), }; } } @@ -35,7 +35,7 @@ pub fn checked_transaction_fee(store: &TransactionOutputProvider, tx_idx: usize, incoming = match incoming.checked_add(balancing_value) { Some(incoming) => incoming, - None => return Err(FeeError::InputsOverflow), + None => return Err(TransactionError::InputValueOverflow), }; } } @@ -47,7 +47,7 @@ pub fn checked_transaction_fee(store: &TransactionOutputProvider, tx_idx: usize, for js_desc in &join_split.descriptions { spends = match spends.checked_add(js_desc.value_pub_old) { Some(spends) => spends, - None => return Err(FeeError::OutputsOverflow), + None => return Err(TransactionError::OutputValueOverflow), }; } } @@ -56,12 +56,12 @@ pub fn checked_transaction_fee(store: &TransactionOutputProvider, tx_idx: usize, if sapling.balancing_value < 0 { let balancing_value = match sapling.balancing_value.checked_neg() { Some(balancing_value) => balancing_value as u64, - None => return Err(FeeError::OutputsOverflow), + None => return Err(TransactionError::OutputValueOverflow), }; spends = match spends.checked_add(balancing_value) { Some(spends) => spends, - None => return Err(FeeError::OutputsOverflow), + None => return Err(TransactionError::OutputValueOverflow), }; } } @@ -69,7 +69,7 @@ pub fn checked_transaction_fee(store: &TransactionOutputProvider, tx_idx: usize, // (3) Fee is the difference between (1) and (2) match incoming.checked_sub(spends) { Some(fee) => Ok(fee), - None => Err(FeeError::NegativeFee), + None => Err(TransactionError::Overspend), } } @@ -104,7 +104,7 @@ mod tests { let db = Arc::new(BlockChainDatabase::init_test_chain(vec![b0.into(), b1.into()])); let store = db.as_transaction_output_provider(); - assert_eq!(checked_transaction_fee(store, ::std::usize::MAX, &tx0), Err(FeeError::NegativeFee)); + assert_eq!(checked_transaction_fee(store, ::std::usize::MAX, &tx0), Err(TransactionError::Overspend)); assert_eq!(checked_transaction_fee(store, ::std::usize::MAX, &tx2), Ok(500_000)); } } diff --git a/verification/src/lib.rs b/verification/src/lib.rs index b3e42017..815f64eb 100644 --- a/verification/src/lib.rs +++ b/verification/src/lib.rs @@ -123,7 +123,7 @@ pub use verify_header::HeaderVerifier; pub use verify_transaction::{TransactionVerifier, MemoryPoolTransactionVerifier}; pub use chain_verifier::BackwardsCompatibleChainVerifier; -pub use error::{Error, TransactionError, FeeError}; +pub use error::{Error, TransactionError}; pub use fee::checked_transaction_fee; pub use sigops::transaction_sigops; pub use timestamp::{median_timestamp, median_timestamp_inclusive}; From 2bb21e6370b88d0663cf0ec6bfe26b1cd7a112cf Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 16 Apr 2019 10:20:52 +0300 Subject: [PATCH 05/12] Option -> SighashCache { Option<> } --- script/src/interpreter.rs | 10 +- script/src/sign.rs | 258 ++++++++++++++----------- script/src/verify.rs | 2 +- verification/src/accept_transaction.rs | 4 +- verification/src/sapling.rs | 2 +- 5 files changed, 157 insertions(+), 119 deletions(-) diff --git a/script/src/interpreter.rs b/script/src/interpreter.rs index 93f655b5..b7c34cbe 100644 --- a/script/src/interpreter.rs +++ b/script/src/interpreter.rs @@ -1822,7 +1822,7 @@ mod tests { input_index: 0, input_amount: 0, consensus_branch_id: 0, - cache: None, + cache: Default::default(), }; let input: Script = "47304402202cb265bf10707bf49346c3515dd3d16fc454618c58ec0a0ff448a676c54ff71302206c6624d762a1fcef4618284ead8f08678ac05b13c84235f1654e6ad168233e8201410414e301b2328f17442c0b8310d787bf3d8a404cfbd0704f135b6ad4b2d3ee751310f981926e53a6e8c39bd7d3fefd576c543cce493cbac06388f2651d1aacbfcd".into(); let output: Script = "76a914df3bd30160e6c6145baaf2c88a8844c13a00d1d588ac".into(); @@ -1841,7 +1841,7 @@ mod tests { input_index: 0, input_amount: 0, consensus_branch_id: 0, - cache: None, + cache: Default::default(), }; let input: Script = "00483045022100deeb1f13b5927b5e32d877f3c42a4b028e2e0ce5010fdb4e7f7b5e2921c1dcd2022068631cb285e8c1be9f061d2968a18c3163b780656f30a049effee640e80d9bff01483045022100ee80e164622c64507d243bd949217d666d8b16486e153ac6a1f8e04c351b71a502203691bef46236ca2b4f5e60a82a853a33d6712d6a1e7bf9a65e575aeb7328db8c014cc9524104a882d414e478039cd5b52a92ffb13dd5e6bd4515497439dffd691a0f12af9575fa349b5694ed3155b136f09e63975a1700c9f4d4df849323dac06cf3bd6458cd41046ce31db9bdd543e72fe3039a1f1c047dab87037c36a669ff90e28da1848f640de68c2fe913d363a51154a0c62d7adea1b822d05035077418267b1a1379790187410411ffd36c70776538d079fbae117dc38effafb33304af83ce4894589747aee1ef992f63280567f52f5ba870678b4ab4ff6c8ea600bd217870a8b4f1f09f3a8e8353ae".into(); let output: Script = "a9141a8b0026343166625c7475f01e48b5ede8c0252e87".into(); @@ -1860,7 +1860,7 @@ mod tests { input_index: 0, input_amount: 0, consensus_branch_id: 0, - cache: None, + cache: Default::default(), }; let input: Script = "483045022052ffc1929a2d8bd365c6a2a4e3421711b4b1e1b8781698ca9075807b4227abcb0221009984107ddb9e3813782b095d0d84361ed4c76e5edaf6561d252ae162c2341cfb01".into(); let output: Script = "410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac".into(); @@ -1879,7 +1879,7 @@ mod tests { input_index: 0, input_amount: 0, consensus_branch_id: 0, - cache: None, + cache: Default::default(), }; let input: Script = "4b3048022200002b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378db4b14e736602220000334a96676e58b1bb01784cb7c556dd8ce1c220171904da22e18fe1e7d1510db5014104d0fe07ff74c9ef5b00fed1104fad43ecf72dbab9e60733e4f56eacf24b20cf3b8cd945bcabcc73ba0158bf9ce769d43e94bd58c5c7e331a188922b3fe9ca1f5a".into(); let output: Script = "76a9147a2a3b481ca80c4ba7939c54d9278e50189d94f988ac".into(); @@ -1898,7 +1898,7 @@ mod tests { input_index: 0, input_amount: 0, consensus_branch_id: 0, - cache: None, + cache: Default::default(), }; let input: Script = "483045022100d92e4b61452d91a473a43cde4b469a472467c0ba0cbd5ebba0834e4f4762810402204802b76b7783db57ac1f61d2992799810e173e91055938750815b6d8a675902e014f".into(); let output: Script = "76009f69905160a56b210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71ad6c".into(); diff --git a/script/src/sign.rs b/script/src/sign.rs index 70d49a71..deaa471c 100644 --- a/script/src/sign.rs +++ b/script/src/sign.rs @@ -26,12 +26,12 @@ impl From for u32 { /// Signature portions cache. #[derive(Debug, Default, PartialEq)] pub struct SighashCache { - pub hash_prevouts: H256, - pub hash_sequence: H256, - pub hash_outputs: H256, - pub hash_join_split: H256, - pub hash_sapling_spends: H256, - pub hash_sapling_outputs: H256, + pub hash_prevouts: Option, + pub hash_sequence: Option, + pub hash_outputs: Option, + pub hash_join_split: Option, + pub hash_sapling_spends: Option, + pub hash_sapling_outputs: Option, } #[cfg_attr(feature="cargo-clippy", allow(doc_markdown))] @@ -151,7 +151,7 @@ impl TransactionInputSigner { /// Pass None as input_index to compute transparent input signature pub fn signature_hash( &self, - cache: &mut Option, + cache: &mut SighashCache, input_index: Option, input_amount: u64, script_pubkey: &Script, @@ -248,7 +248,7 @@ impl TransactionInputSigner { /// Overwinter/sapling version of the signature. fn signature_hash_post_overwinter( &self, - cache: &mut Option, + cache: &mut SighashCache, input_index: Option, input_amount: u64, script_pubkey: &Script, @@ -258,35 +258,35 @@ impl TransactionInputSigner { sapling: bool, ) -> H256 { // compute signature portions that can be reused for other inputs - let hash_prevouts = cache.as_ref().map(|c| c.hash_prevouts) - .unwrap_or_else(|| compute_hash_prevouts(sighash, &self.inputs)); - let hash_sequence = cache.as_ref().map(|c| c.hash_sequence) - .unwrap_or_else(|| compute_hash_sequence(sighash, &self.inputs)); - let hash_outputs = compute_hash_outputs(cache, sighash, input_index, &self.outputs); - let hash_join_split = cache.as_ref().map(|c| c.hash_join_split) - .unwrap_or_else(|| compute_hash_join_split(self.join_split.as_ref())); - let hash_sapling_spends = if sapling { - cache.as_ref().map(|c| c.hash_sapling_spends) - .unwrap_or_else(|| compute_hash_sapling_spends(self.sapling.as_ref())) - } else { - 0u8.into() - }; - let hash_sapling_outputs = if sapling { - cache.as_ref().map(|c| c.hash_sapling_outputs) - .unwrap_or_else(|| compute_hash_sapling_outputs(self.sapling.as_ref())) - } else { - 0u8.into() - }; + // + // compute_* decides if it wants to use cached value + // compute_* decides if it wants to cache computed value + let (hash_prevouts, cache_hash_prevouts) = compute_hash_prevouts(cache, sighash, &self.inputs); + let (hash_sequence, cache_hash_sequence) = compute_hash_sequence(cache, sighash, &self.inputs); + let (hash_outputs, cache_hash_outputs) = compute_hash_outputs(cache, sighash, input_index, &self.outputs); + let (hash_join_split, cache_hash_join_split) = compute_hash_join_split(cache, self.join_split.as_ref()); + let (hash_sapling_spends, cache_hash_sapling_spends) = compute_hash_sapling_spends(cache, sapling, self.sapling.as_ref()); + let (hash_sapling_outputs, cache_hash_sapling_outputs) = compute_hash_sapling_outputs(cache, sapling, self.sapling.as_ref()); // update cache - *cache = Some(SighashCache { - hash_prevouts, - hash_sequence, - hash_outputs, - hash_join_split, - hash_sapling_spends, - hash_sapling_outputs, - }); + if cache_hash_prevouts { + cache.hash_prevouts = Some(hash_prevouts); + } + if cache_hash_sequence { + cache.hash_sequence = Some(hash_sequence); + } + if cache_hash_outputs { + cache.hash_outputs = Some(hash_outputs); + } + if cache_hash_join_split { + cache.hash_join_split = Some(hash_join_split); + } + if cache_hash_sapling_spends { + cache.hash_sapling_spends = Some(hash_sapling_spends); + } + if cache_hash_sapling_outputs { + cache.hash_sapling_outputs = Some(hash_sapling_outputs); + } let mut personalization = [0u8; 16]; personalization[..12].copy_from_slice(b"ZcashSigHash"); @@ -341,85 +341,101 @@ impl TransactionInputSigner { } } -fn compute_hash_prevouts(sighash: Sighash, inputs: &[UnsignedTransactionInput]) -> H256 { +fn compute_hash_prevouts( + cache: &SighashCache, + sighash: Sighash, + inputs: &[UnsignedTransactionInput], +) -> (H256, bool) { const PERSONALIZATION: &'static [u8; 16] = b"ZcashPrevoutHash"; match sighash.anyone_can_pay { - false => { + false => (cache.hash_prevouts.unwrap_or_else(|| { let mut stream = Stream::default(); for input in inputs { stream.append(&input.previous_output); } blake2b_personal(PERSONALIZATION, &stream.out()) - }, - true => 0u8.into(), + }), true), + true => (0u8.into(), false), } } -fn compute_hash_sequence(sighash: Sighash, inputs: &[UnsignedTransactionInput]) -> H256 { +fn compute_hash_sequence( + cache: &SighashCache, + sighash: Sighash, + inputs: &[UnsignedTransactionInput], +) -> (H256, bool) { const PERSONALIZATION: &'static [u8; 16] = b"ZcashSequencHash"; match sighash.base { - SighashBase::All if !sighash.anyone_can_pay => { + SighashBase::All if !sighash.anyone_can_pay => (cache.hash_sequence.unwrap_or_else(|| { let mut stream = Stream::default(); for input in inputs { stream.append(&input.sequence); } blake2b_personal(PERSONALIZATION, &stream.out()) - }, - _ => 0u8.into(), + }), true), + _ => (0u8.into(), false), } } fn compute_hash_outputs( - cache: &mut Option, + cache: &SighashCache, sighash: Sighash, input_index: Option, outputs: &[TransactionOutput] -) -> H256 { +) -> (H256, bool) { const PERSONALIZATION: &'static [u8; 16] = b"ZcashOutputsHash"; match (sighash.base, input_index) { - (SighashBase::All, _) => { - cache.as_ref().map(|c| c.hash_outputs) - .unwrap_or_else(|| { - let mut stream = Stream::default(); - for output in outputs { - stream.append(output); - } - blake2b_personal(PERSONALIZATION, &stream.out()) - }) - }, + (SighashBase::All, _) => (cache.hash_outputs.unwrap_or_else(|| { + let mut stream = Stream::default(); + for output in outputs { + stream.append(output); + } + blake2b_personal(PERSONALIZATION, &stream.out()) + }), true), (SighashBase::Single, Some(input_index)) if input_index < outputs.len() => { let mut stream = Stream::default(); stream.append(&outputs[input_index]); - blake2b_personal(PERSONALIZATION, &stream.out()) + (blake2b_personal(PERSONALIZATION, &stream.out()), false) }, - _ => 0u8.into(), + _ => (0u8.into(), false), } } -fn compute_hash_join_split(join_split: Option<&JoinSplit>) -> H256 { +fn compute_hash_join_split( + cache: &SighashCache, + join_split: Option<&JoinSplit>, +) -> (H256, bool) { const PERSONALIZATION: &'static [u8; 16] = b"ZcashJSplitsHash"; match join_split { - Some(join_split) if !join_split.descriptions.is_empty() => { + Some(join_split) if !join_split.descriptions.is_empty() => (cache.hash_join_split.unwrap_or_else(|| { let mut stream = Stream::default(); for description in &join_split.descriptions { stream.append(description); } stream.append(&join_split.pubkey); blake2b_personal(PERSONALIZATION, &stream.out()) - }, - _ => 0u8.into(), + }), true), + _ => (0u8.into(), false), } } -fn compute_hash_sapling_spends(sapling: Option<&Sapling>) -> H256 { +fn compute_hash_sapling_spends( + cache: &SighashCache, + is_sapling: bool, + sapling: Option<&Sapling>, +) -> (H256, bool) { const PERSONALIZATION: &'static [u8; 16] = b"ZcashSSpendsHash"; + if !is_sapling { + return (0u8.into(), false); + } + match sapling { - Some(sapling) if !sapling.spends.is_empty() => { + Some(sapling) if !sapling.spends.is_empty() => (cache.hash_sapling_spends.unwrap_or_else(|| { let mut stream = Stream::default(); for spend in &sapling.spends { stream.append(&spend.value_commitment); @@ -429,23 +445,31 @@ fn compute_hash_sapling_spends(sapling: Option<&Sapling>) -> H256 { stream.append(&spend.zkproof); } blake2b_personal(PERSONALIZATION, &stream.out()) - }, - _ => 0u8.into(), + }), true), + _ => (0u8.into(), false), } } -fn compute_hash_sapling_outputs(sapling: Option<&Sapling>) -> H256 { +fn compute_hash_sapling_outputs( + cache: &SighashCache, + is_sapling: bool, + sapling: Option<&Sapling>, +) -> (H256, bool) { const PERSONALIZATION: &'static [u8; 16] = b"ZcashSOutputHash"; + if !is_sapling { + return (0u8.into(), false); + } + match sapling { - Some(sapling) if !sapling.outputs.is_empty() => { + Some(sapling) if !sapling.outputs.is_empty() => (cache.hash_sapling_outputs.unwrap_or_else(|| { let mut stream = Stream::default(); for output in &sapling.outputs { stream.append(output); } blake2b_personal(PERSONALIZATION, &stream.out()) - }, - _ => 0u8.into(), + }), true), + _ => (0u8.into(), false), } } @@ -504,7 +528,7 @@ mod tests { sapling: None, }; - let mut cache = None; + let mut cache = Default::default(); let hash = input_signer.signature_hash(&mut cache, Some(0), 0, &previous_output, SighashBase::All.into(), 0); assert_eq!(hash, expected_signature_hash); } @@ -534,7 +558,7 @@ mod tests { let expected: H256 = result.parse().unwrap(); let expected = expected.reversed(); - let mut cache = None; + let mut cache = Default::default(); let input_index = if input_index as u64 == ::std::u64::MAX { None } else { Some(input_index) }; let hash = signer.signature_hash(&mut cache, input_index, 0, &script, hash_type as u32, consensus_branch_id); if expected != hash { @@ -566,50 +590,64 @@ mod tests { } } - // tx#1 from block#419201 #[test] fn test_sighash_cache_works_correctly() { - // tx#1 from block#419201 - // https://zcash.blockexplorer.com/api/rawblock/00000000014d117faa2ea701b24261d364a6c6a62e5bc4bc27335eb9b3c1e2a8 - let spend_tx: Transaction = "0400008085202f8901ddd8ddd4a8713d9443e11f1a79adb737e974bece08608b6b04017d9b436b9399010000006a473044022004cd1a5a48b015213fa0810028d98271d219aa4a7293928dea01946f9e3db53102206837946d92757a460c8d7a2d64872890abbfa3f572cd1e8fabf5a7ca8997de26012102fa947bb7cfa50aa6e83f6296d95334d7f55cd43e9c873404f2550f6ba006d5aaffffffff00000000009465060021f65ff9ffffffff0001b40f6b0d76653bc236b045c7dd16e0e8e1a8fa6fa9f7d5120c1e7289aee78d67f9d8ae3d9707dcb30064b8f7afcc2fc1aca8918f263c58da6c7806cfad133d11fedf35174cf837149b3f2559a70055398c6ab3eba99a3335f4d360488d88266fb533abea66784d930ff8f1574eca66374d4fa559f462d65c0d8af6e7b2770dfb804f9d29388b651b2af0d9d21ad3cd6aebeeb8ecdc98b208aa027e6dcc8f27a13d643d650934faa98a809fa0c61ea3f796f96565cbee80a176a9258621ec3574ce5e7f6ceb70db4bd36f2feb983648f8405ebda405645f005f455f3dd96ea7d5081ba13a6a90cae0aebc7ec7a4589058bf67dc35c511de423d0c29d95febeaf08a32f4e123f39d4bb964d836eeaf2eb825c68c0f7ce62ab8f048f61a28998c1f0340d9660c849b86d8f639955d5d2e15458875dd547c86fd96a74c48b3eb6fc3d31ffdfa24d78afdbb0dbfa8200e87562668293bbab1ca9fc67af4f7369f0e12b6e6e07388189381b38059737ddc3322cbbfd6d1bce912d2bacdfa66f3f22835fc0f3f213e6abef8eacfaffd4c204296900e1651d1c9cbf981364629250b4bafaa5e4d1cbba21a03f6270ddbcaed684caceea5b2870a856a11835923277e5648db0d92ad60f280c7dbb1c2820000fa3117f3e1e0a08cec9f3dfeaf2b6d8d714e072b674d5dbb53420dc9cf67c8f0010665119a8200dd774f0107b17a7398706cf10eecd219f252b2d6813f4d8a672a1dca61f68c75cefb9f2ba7653bdae0b2faa6e76afe9ff62d2ecdfc72d497210517a66f2bf39f402991e5608e754c551e75bd26e7f33474b68d690d5285bd949182731fc49b43d4673aaca20d665c0d0b6ad7cced361c91b06e114e46495400738ef9d528744267fd47d3239e4548a6a3ff6e43b6ca821f32fc261f2c649674a4dc2bac93d177e0c44f4c78694f50ce374978599de6aefbd37e892de81d8d6012675f31daa75fb35bbb339754355e67ea4cf0b8c67d573af3f4f3382b3f408682a50d58767a796ca1ed3dca4227f9d107dc08c0e53134d4fa6e06792182873aa895f3373c388b0c9c7d4a2c065b6f1cd807ef3f4d7b2737eeb90ebb557c859ff17b898d350d8cffc7ca1e08dcb9baba5a336f17e6eba7a2425da8c43caefbd7fa58390e78ad083c36720336fe824983d1fa17402e89d3c224e994248c88ec2f547aeb48227705fd8a4ac3f9f30b139b4e30bb0b82af9bae87800c875c19d6fbd45a26763d056bef6899e031442185ae50a5ed24b006a852f8ce3d55b2d2d9f4179797f93bbffd8905cc9ce69cebc8a17e1e8b8eb5e1e675620c70b22de348969b993246520c00bfa467125a2528a829120b3f64d2c1f58f45cb31a1d15ba368d7df55aa65e65ad4a8f5bc63e06396d40964b3aff6084a83f567b186b1c70072dfebc873638409".into(); - assert_eq!(spend_tx.hash().reversed(), "66e2e3dfb9c51eb961004e0eb8bfd3820239c4f11614b65a1fffb60e01858580".into()); - assert_eq!(spend_tx.inputs[0].previous_output.hash.reversed(), "99936b439b7d01046b8b6008cebe74e937b7ad791a1fe143943d71a8d4ddd8dd".into()); - assert_eq!(spend_tx.inputs[0].previous_output.index, 1); + let test_cases: Vec<(Transaction, Transaction, usize)> = vec![ + ( + // tx#1 from block#419201 + // https://zcash.blockexplorer.com/api/rawblock/00000000014d117faa2ea701b24261d364a6c6a62e5bc4bc27335eb9b3c1e2a8 + "0400008085202f8901ddd8ddd4a8713d9443e11f1a79adb737e974bece08608b6b04017d9b436b9399010000006a473044022004cd1a5a48b015213fa0810028d98271d219aa4a7293928dea01946f9e3db53102206837946d92757a460c8d7a2d64872890abbfa3f572cd1e8fabf5a7ca8997de26012102fa947bb7cfa50aa6e83f6296d95334d7f55cd43e9c873404f2550f6ba006d5aaffffffff00000000009465060021f65ff9ffffffff0001b40f6b0d76653bc236b045c7dd16e0e8e1a8fa6fa9f7d5120c1e7289aee78d67f9d8ae3d9707dcb30064b8f7afcc2fc1aca8918f263c58da6c7806cfad133d11fedf35174cf837149b3f2559a70055398c6ab3eba99a3335f4d360488d88266fb533abea66784d930ff8f1574eca66374d4fa559f462d65c0d8af6e7b2770dfb804f9d29388b651b2af0d9d21ad3cd6aebeeb8ecdc98b208aa027e6dcc8f27a13d643d650934faa98a809fa0c61ea3f796f96565cbee80a176a9258621ec3574ce5e7f6ceb70db4bd36f2feb983648f8405ebda405645f005f455f3dd96ea7d5081ba13a6a90cae0aebc7ec7a4589058bf67dc35c511de423d0c29d95febeaf08a32f4e123f39d4bb964d836eeaf2eb825c68c0f7ce62ab8f048f61a28998c1f0340d9660c849b86d8f639955d5d2e15458875dd547c86fd96a74c48b3eb6fc3d31ffdfa24d78afdbb0dbfa8200e87562668293bbab1ca9fc67af4f7369f0e12b6e6e07388189381b38059737ddc3322cbbfd6d1bce912d2bacdfa66f3f22835fc0f3f213e6abef8eacfaffd4c204296900e1651d1c9cbf981364629250b4bafaa5e4d1cbba21a03f6270ddbcaed684caceea5b2870a856a11835923277e5648db0d92ad60f280c7dbb1c2820000fa3117f3e1e0a08cec9f3dfeaf2b6d8d714e072b674d5dbb53420dc9cf67c8f0010665119a8200dd774f0107b17a7398706cf10eecd219f252b2d6813f4d8a672a1dca61f68c75cefb9f2ba7653bdae0b2faa6e76afe9ff62d2ecdfc72d497210517a66f2bf39f402991e5608e754c551e75bd26e7f33474b68d690d5285bd949182731fc49b43d4673aaca20d665c0d0b6ad7cced361c91b06e114e46495400738ef9d528744267fd47d3239e4548a6a3ff6e43b6ca821f32fc261f2c649674a4dc2bac93d177e0c44f4c78694f50ce374978599de6aefbd37e892de81d8d6012675f31daa75fb35bbb339754355e67ea4cf0b8c67d573af3f4f3382b3f408682a50d58767a796ca1ed3dca4227f9d107dc08c0e53134d4fa6e06792182873aa895f3373c388b0c9c7d4a2c065b6f1cd807ef3f4d7b2737eeb90ebb557c859ff17b898d350d8cffc7ca1e08dcb9baba5a336f17e6eba7a2425da8c43caefbd7fa58390e78ad083c36720336fe824983d1fa17402e89d3c224e994248c88ec2f547aeb48227705fd8a4ac3f9f30b139b4e30bb0b82af9bae87800c875c19d6fbd45a26763d056bef6899e031442185ae50a5ed24b006a852f8ce3d55b2d2d9f4179797f93bbffd8905cc9ce69cebc8a17e1e8b8eb5e1e675620c70b22de348969b993246520c00bfa467125a2528a829120b3f64d2c1f58f45cb31a1d15ba368d7df55aa65e65ad4a8f5bc63e06396d40964b3aff6084a83f567b186b1c70072dfebc873638409".into(), + // donor tx for input #0: + // tx#3 from block#409840 + // https://zcash.blockexplorer.com/api/rawblock/0000000002d83a0d7d5011a19d2bd89125dc22d63b6484f2792fd1d636c4d940 + "030000807082c4030b10d6644275fdb7553601349f524ce0a4fb6acc1d17551249b7cb87cb97e07f1f100000006b483045022100977b46b263f691777cb13b9b9c623ce15ccef2d5d5f1efcb7fd1f16aeac98fe20220090ecb6f82cccb37f295ec3c898c1c9b5bb3f46f7b524bc641137a9ce6277bbb012102be56007d075b0ae8e9de4027b61af2b0c8f458c5d1cc6c0a3e0a7f699cfb96c7feffffff8308f2430ed380564b53e7e4fdb16fbb30d1c482dc5fae68613e69d368608c44190000006a47304402201b5673ce6c541a42eac79742e7d1a1c9f51456d5012226985067eec93922f96f0220064c88fa17711860e5a06ebf8849cd4dcbb8f944c39ff227a99a91d8c82a4621012102be56007d075b0ae8e9de4027b61af2b0c8f458c5d1cc6c0a3e0a7f699cfb96c7feffffff5c89bda835f37289182b3123c49c7906629631e4c3c883de97fb637f92802c16580000006a47304402203533eca9827a92959ee7b8c0ea8154b62e9935bc8ca4c61020ff268bb336c59402200e14f6ea6f2e9e0bce19db50b2b10ed3d1e40db957aae3b491797540932dd8ea012102be56007d075b0ae8e9de4027b61af2b0c8f458c5d1cc6c0a3e0a7f699cfb96c7feffffff9b057ba8d3e81fa9c10a98c4ee258df57a9eb1a80f1fb22b08b1685d1ada17fc1f0000006b483045022100e845ab5355bd877641e8238d9f16ac1345af346e81fbeeedda128f23dec5f71002207367e2e38d32e6843aa8c52eedd6b1fbda08d8436e26e0e80e035cb2314710d7012102be56007d075b0ae8e9de4027b61af2b0c8f458c5d1cc6c0a3e0a7f699cfb96c7feffffff9e01f46736183d109a9739638cfff185f62aebf2e9a010f795d80a0b82daad50200000006b483045022100fdf3156db7f2cad51acfd4fcda6ec9f3608ba68f49d5d947e7504a00522f6a4102202f4c74cc34843efbecd53612fd8ea065d8f79b3419190e1792fe3fa03b8d5447012102be56007d075b0ae8e9de4027b61af2b0c8f458c5d1cc6c0a3e0a7f699cfb96c7fefffffffafa19844c386065fd650694ec15094a24e4a6499eb585a0c0544fbf5be9e002010000006a47304402205e2a73750d0ee3672184da65e356fbb95023586ef77c8f01d87eecab09aba5e7022015f8b13eece11b9c7a57bd869b5f44af3a4dfc07ebaf324f899b6541f7ffba050121025792386461f81e038989e4ac62a4142e1e987ae740906ab3032a04e4ac74967bfeffffff1ae4ec68f85c5d67797eccba7653c1a2bc5df34423366af7793bf1232c2bacb9000000006a47304402207501656ce6d97dbd5573953c117cb8fbdc61e09be58798cdab162304291311860220088945ac2142cf3689d7686b146720e4ac1c7f5292c13406fc2628a8e63ff221012103fbdca468248d731579fb6e756566d48903a84a16cd0f415cddfb5c41458bb262feffffff2d805a191e7699a2dcadf2444f682afe2be378b20e3b4b4431f57f32c7d554dd000000006b483045022100b39190d59a549f04f1e22d637f997b1b1ab7abda98d34a67fb616d380d247ea5022039feb0bf51e496dcfad037b0c675038d0c36793a26a66e58a9039524bd6e3e6c012103bb4932a7677891b8945557cb23530b6e9a688f0fa6deac31c9e323f0edf40439feffffff71934ffa32133dc62c7e4b2a8b10f51a8aa0a96099b4fc1e648ea9c676884c6f000000006b48304502210094af623575cec584e3d4406ed30a2f6364a0c6ba493cb77ea0f0d6e8d372cc5f02206ee94b089bfc4e347cf5de9de27785102e3bb146276868a4d9220d405d4e28e2012102cb666a57bb47dc4d447795e439f9d03d7b935f94fd92f80ecabc6a41061a50e8feffffff1bddd5649e33d1595c956b574d3dbb3883d25d1b9be93ef3ce7fad492534d8e4000000006b483045022100800c193e55b1234ed405248ebd69250fb0373bcfe6dda2da593c8f4213d10e5e02202c7b7387449b74125ee8d1f8c381d30f9474f0d5718e33ddd0a9179f5c6ca5ff0121020b2c90de955d7b4bf93415147b8bb43af3186b46e743316ee662ec9136899bf9fefffffffec3b71b6d478340583518ac04357d7a01e39ef311a9b7a9eed4bd3afe8a2a39010000006b483045022100b0e445d7bb23bf2400428d17d8b076d1ea6f415981ef9b806c714697538eaad402203b29f0108126b3345f48dfe22a8b743f3fd91b3c809272d8f81d566530540aa20121029506cd31b962743382a7c5b372d4a6ce66584f7aafefd358ad1b720902c3c907feffffff023d4a0f00000000001976a914e212f89515c07fc61c01fd9ccee566544956822088acef30a006000000001976a91414c42abe82c257103f4589e738f4f05b0f0c600e88ace54006000441060000".into(), + // we're testing input#0 + 0, + ), + ( + // tx#3 from block#420083 + // https://zcash.blockexplorer.com/api/rawtx/56d3fd4241520573c4f90e8b89a634cb3b3c4f2cfca34e7b3208de4678bf67b2 + "0400008085202f890666a4b9fe9308b92f1785f9e18a18f473d5a8c44ac3abf5cf52d43773a5bab580000000006b4830450221008336e1a227f894d41a4f9a13bb67ae3a0bc0be6558dd4c17ffa79732f3908f8802200978b7abfa0cd38cf73a161aedfcdf78600a3112ad8a5821d12f709bf9e640b4032102c13a62ce58bf064cd5e3bdb8ad61e07ff5fe854284728edd1117125c7bffbfeeffffffffb9fcb288f427e0556f629d248656d6bab6d2b6343aee8ffd8eddfb754b6f8fe0000000006b483045022100bf6744adf8ad5a3aea4e6170ff5e638702179832f8ab7c20721a5d7db169944a0220147ab55f2316097fe54a67c9fa08950ab3645cf475ba7a61596f39ea03893da40321032bf181cbe48f2ab8ae2b2c3840eff37615a0299bb90b49bfecbc36ebc83c7b6bffffffffb9ee9a5db14130b14cb28a8488ee25a451b31b7793925564c2cf158b5750d148010000006a473044022038f9774cd6654a6efec33150d27a2e2b8177e11c98cd3196b157c18ed2af20fa02203f3ea40ca22fb9fc1c28d06105c5a2289311ab03aa5fb03509230e1a87644cfa0321025371db306496fa8ec28ffc8ed28f1c8fd93c751d7b2448d7959656a6fbfd36faffffffff6f3447fe2d7f1a35ea6024083868067786fc2d2a8e65787dc5a5268efae2290a010000006a47304402207a975173da91e93280f5664f58e07959d18408554a28c428836b90b9e0e73578022070c495faa40b8c1fa784454e29f8a621f1824a182f68c6600cb0daffc7b4e45f0321025778f397884e483eb74ae861d52abc625b114052dfd40936b3d505767f38ed33ffffffff794ff48194c959056ff80ef547262259c9f1583d860301425c04a6824807b820010000006b483045022100bd707bc1a01edb5bfc5e6120490ec07cb330a78aa366d718f291980bdfd15758022035f8e860431c915591b3cde8fced541eca8a26c8966b92368f09a0356f6d92800321030ede2c25e6d034f4a855f9976cd67615e4279656f775fe13be300a4f6b688b8effffffff1fe56983eee881144093b6e4e2953779ea2cee051005bd1e78a06edceda90333010000006b483045022100a5a36678dd45ec9e4de1a275829da911dded2c59bb0e7467110bb9c6fc8cb099022033a2c22315ae87aedff8c1c9631c4c890cca9690544433dec52dc358332334400321023e4a72e165868a1257f53e093eda3c1e95c7e36b5a4a32bf78c4d0d8417addc7ffffffff01eaf82800000000001976a91417379a9efb16569951887cb5365791ad9895389788ac00000000000000000000000000000000000000".into(), + // donor tx for input #0: + // https://zcash.blockexplorer.com/api/rawtx/80b5baa57337d452cff5abc34ac4a8d573f4188ae1f985172fb90893feb9a466 + "030000807082c4030363fa1654f87357fa2f9daf554424d97fe9b0275f820e10fa80c713329816b085000000006a47304402200fc27c0e1c63142f6903608967f66432ca88c95b54cc80794b099b82d0a2df780220440a9c21de092f4cc33f83405a27023362961e2423b5957b387442b30b9fea8f01210227855d8d44fa991a92a8608df49a3a76bb2a37a6ec4146df59bedabc6ba97572feffffff956dc5a8869f4ea22e806f2422248b840d2fae0abaaaaec798b97bc8358cdb5d000000006a47304402203c3dcf0206a5b48ef517d2d23f04a3d6a2383536c208daa7d6f5ce9a2cf61e5402206ac954167c7f6ec1e8be22c997c32104dc0368b3f2d97fea670a9d6490a169bc012102fbdf4afcad38290d7268db506165af2e4fbd99632c2f609b2512f951af17b116feffffffb126f38d5cb212b2ec56184dde40acb66583d26ae979dc54893f5321fe329df8000000006a473044022054842385d5596ae51626c507dacb575274df071ed76f064825206e90687503fa02201e7e3f9c34adf182776048b0b87a9ff7cb34819ca97866a92fb1c960257e56d2012103c6d5ce7fcb7483662e702ff6a40141ce6e03cdf10206513a6ee33543194bcea3feffffff020f9f1000000000001976a9146b5f30eb97d6908d7319b57b5c453612045bc98688ac085a0f00000000001976a9140a47e7e4b7767f6cd0eb630a2bc3a69c9f9175f788ac430f0600620f060000".into(), + // we're testing input#0 + 0, + ) + ]; - // donor tx for input #1: - // tx#3 from block#409840 - // https://zcash.blockexplorer.com/api/rawblock/0000000002d83a0d7d5011a19d2bd89125dc22d63b6484f2792fd1d636c4d940 - let donor_tx: Transaction = "030000807082c4030b10d6644275fdb7553601349f524ce0a4fb6acc1d17551249b7cb87cb97e07f1f100000006b483045022100977b46b263f691777cb13b9b9c623ce15ccef2d5d5f1efcb7fd1f16aeac98fe20220090ecb6f82cccb37f295ec3c898c1c9b5bb3f46f7b524bc641137a9ce6277bbb012102be56007d075b0ae8e9de4027b61af2b0c8f458c5d1cc6c0a3e0a7f699cfb96c7feffffff8308f2430ed380564b53e7e4fdb16fbb30d1c482dc5fae68613e69d368608c44190000006a47304402201b5673ce6c541a42eac79742e7d1a1c9f51456d5012226985067eec93922f96f0220064c88fa17711860e5a06ebf8849cd4dcbb8f944c39ff227a99a91d8c82a4621012102be56007d075b0ae8e9de4027b61af2b0c8f458c5d1cc6c0a3e0a7f699cfb96c7feffffff5c89bda835f37289182b3123c49c7906629631e4c3c883de97fb637f92802c16580000006a47304402203533eca9827a92959ee7b8c0ea8154b62e9935bc8ca4c61020ff268bb336c59402200e14f6ea6f2e9e0bce19db50b2b10ed3d1e40db957aae3b491797540932dd8ea012102be56007d075b0ae8e9de4027b61af2b0c8f458c5d1cc6c0a3e0a7f699cfb96c7feffffff9b057ba8d3e81fa9c10a98c4ee258df57a9eb1a80f1fb22b08b1685d1ada17fc1f0000006b483045022100e845ab5355bd877641e8238d9f16ac1345af346e81fbeeedda128f23dec5f71002207367e2e38d32e6843aa8c52eedd6b1fbda08d8436e26e0e80e035cb2314710d7012102be56007d075b0ae8e9de4027b61af2b0c8f458c5d1cc6c0a3e0a7f699cfb96c7feffffff9e01f46736183d109a9739638cfff185f62aebf2e9a010f795d80a0b82daad50200000006b483045022100fdf3156db7f2cad51acfd4fcda6ec9f3608ba68f49d5d947e7504a00522f6a4102202f4c74cc34843efbecd53612fd8ea065d8f79b3419190e1792fe3fa03b8d5447012102be56007d075b0ae8e9de4027b61af2b0c8f458c5d1cc6c0a3e0a7f699cfb96c7fefffffffafa19844c386065fd650694ec15094a24e4a6499eb585a0c0544fbf5be9e002010000006a47304402205e2a73750d0ee3672184da65e356fbb95023586ef77c8f01d87eecab09aba5e7022015f8b13eece11b9c7a57bd869b5f44af3a4dfc07ebaf324f899b6541f7ffba050121025792386461f81e038989e4ac62a4142e1e987ae740906ab3032a04e4ac74967bfeffffff1ae4ec68f85c5d67797eccba7653c1a2bc5df34423366af7793bf1232c2bacb9000000006a47304402207501656ce6d97dbd5573953c117cb8fbdc61e09be58798cdab162304291311860220088945ac2142cf3689d7686b146720e4ac1c7f5292c13406fc2628a8e63ff221012103fbdca468248d731579fb6e756566d48903a84a16cd0f415cddfb5c41458bb262feffffff2d805a191e7699a2dcadf2444f682afe2be378b20e3b4b4431f57f32c7d554dd000000006b483045022100b39190d59a549f04f1e22d637f997b1b1ab7abda98d34a67fb616d380d247ea5022039feb0bf51e496dcfad037b0c675038d0c36793a26a66e58a9039524bd6e3e6c012103bb4932a7677891b8945557cb23530b6e9a688f0fa6deac31c9e323f0edf40439feffffff71934ffa32133dc62c7e4b2a8b10f51a8aa0a96099b4fc1e648ea9c676884c6f000000006b48304502210094af623575cec584e3d4406ed30a2f6364a0c6ba493cb77ea0f0d6e8d372cc5f02206ee94b089bfc4e347cf5de9de27785102e3bb146276868a4d9220d405d4e28e2012102cb666a57bb47dc4d447795e439f9d03d7b935f94fd92f80ecabc6a41061a50e8feffffff1bddd5649e33d1595c956b574d3dbb3883d25d1b9be93ef3ce7fad492534d8e4000000006b483045022100800c193e55b1234ed405248ebd69250fb0373bcfe6dda2da593c8f4213d10e5e02202c7b7387449b74125ee8d1f8c381d30f9474f0d5718e33ddd0a9179f5c6ca5ff0121020b2c90de955d7b4bf93415147b8bb43af3186b46e743316ee662ec9136899bf9fefffffffec3b71b6d478340583518ac04357d7a01e39ef311a9b7a9eed4bd3afe8a2a39010000006b483045022100b0e445d7bb23bf2400428d17d8b076d1ea6f415981ef9b806c714697538eaad402203b29f0108126b3345f48dfe22a8b743f3fd91b3c809272d8f81d566530540aa20121029506cd31b962743382a7c5b372d4a6ce66584f7aafefd358ad1b720902c3c907feffffff023d4a0f00000000001976a914e212f89515c07fc61c01fd9ccee566544956822088acef30a006000000001976a91414c42abe82c257103f4589e738f4f05b0f0c600e88ace54006000441060000".into(); - assert_eq!(donor_tx.hash().reversed(), "99936b439b7d01046b8b6008cebe74e937b7ad791a1fe143943d71a8d4ddd8dd".into()); + for (spend_tx, donor_tx, input_index) in test_cases { + let output_index = spend_tx.inputs[input_index].previous_output.index as usize; - // prepare tx signature checker - let consensus_branch_id = 0x76b809bb; // sapling starts from block#419200 - let signer: TransactionInputSigner = spend_tx.clone().into(); - let mut checker = TransactionSignatureChecker { - signer, - input_index: 0, - input_amount: donor_tx.outputs[1].value, - consensus_branch_id, - cache: None, - }; + // prepare tx signature checker + let consensus_branch_id = 0x76b809bb; // all test cases are for sapling era + let signer: TransactionInputSigner = spend_tx.clone().into(); + let mut checker = TransactionSignatureChecker { + signer, + input_index, + input_amount: donor_tx.outputs[output_index].value, + consensus_branch_id, + cache: Default::default(), + }; - // calculate signature => fill cache - checker.signer.signature_hash( - &mut checker.cache, - None, - 0, - &From::from(vec![]), - ::sign::SighashBase::All.into(), - consensus_branch_id, - ); + // calculate signature => fill cache + checker.signer.signature_hash( + &mut checker.cache, + None, + 0, + &From::from(vec![]), + ::sign::SighashBase::All.into(), + consensus_branch_id, + ); - // and finally check input#0 (the cached signature portions are used here) - let input: Script = spend_tx.inputs[0].script_sig.clone().into(); - let output: Script = donor_tx.outputs[1].script_pubkey.clone().into(); - let flags = VerificationFlags::default() - .verify_p2sh(true) - .verify_locktime(true) - .verify_dersig(true); - assert_eq!(verify_script(&input, &output, &flags, &mut checker), Ok(())); + // and finally check input (the cached signature portions are used here) + let input: Script = spend_tx.inputs[input_index].script_sig.clone().into(); + let output: Script = donor_tx.outputs[output_index].script_pubkey.clone().into(); + let flags = VerificationFlags::default() + .verify_p2sh(true) + .verify_locktime(true) + .verify_dersig(true); + assert_eq!(verify_script(&input, &output, &flags, &mut checker), Ok(())); + } } } diff --git a/script/src/verify.rs b/script/src/verify.rs index b0271882..425c11fc 100644 --- a/script/src/verify.rs +++ b/script/src/verify.rs @@ -53,7 +53,7 @@ pub struct TransactionSignatureChecker { pub input_index: usize, pub input_amount: u64, pub consensus_branch_id: u32, - pub cache: Option, + pub cache: SighashCache, } impl SignatureChecker for TransactionSignatureChecker { diff --git a/verification/src/accept_transaction.rs b/verification/src/accept_transaction.rs index 71daf77a..aac1518a 100644 --- a/verification/src/accept_transaction.rs +++ b/verification/src/accept_transaction.rs @@ -387,7 +387,7 @@ impl<'a> TransactionEval<'a> { input_index: 0, input_amount: 0, consensus_branch_id: self.consensus_branch_id, - cache: None, + cache: Default::default(), }; // generate sighash that is not associated with a transparent input @@ -781,7 +781,7 @@ mod tests { input_index: 0, input_amount: 0, consensus_branch_id: 0, - cache: None, + cache: Default::default(), }; let flags = VerificationFlags::default() diff --git a/verification/src/sapling.rs b/verification/src/sapling.rs index dae0b9c4..e4cda8b4 100644 --- a/verification/src/sapling.rs +++ b/verification/src/sapling.rs @@ -306,7 +306,7 @@ mod tests { fn compute_sighash(tx: Transaction) -> [u8; 32] { let signer: TransactionInputSigner = tx.into(); - signer.signature_hash(&mut None, None, 0, &From::from(vec![]), SighashBase::All.into(), 0x76b809bb).into() + signer.signature_hash(&mut Default::default(), None, 0, &From::from(vec![]), SighashBase::All.into(), 0x76b809bb).into() } fn run_accept_sapling(tx: Transaction) -> Result<(), Error> { From 77d76c0f64101d60a2f9ad3a41e6183ee3807c23 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 16 Apr 2019 16:58:02 +0300 Subject: [PATCH 06/12] g1 to bn --- Cargo.lock | 8 ++-- crypto/Cargo.toml | 2 +- crypto/src/pghr13.rs | 96 +++++++++++--------------------------------- 3 files changed, 29 insertions(+), 77 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 554e926e..59186fdf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,7 +125,7 @@ dependencies = [ "bellman 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc.git?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)", "blake2b_simd 0.4.1 (git+https://github.com/oconnor663/blake2b_simd.git)", - "bn 0.4.4 (git+https://github.com/paritytech/bn)", + "bn 0.4.4 (git+https://github.com/paritytech/bn?branch=upgrade2)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", "primitives 0.1.0", @@ -172,11 +172,13 @@ dependencies = [ [[package]] name = "bn" version = "0.4.4" -source = "git+https://github.com/paritytech/bn#7f6a93623fe1867a5de6e2b9f4196581a3594f84" +source = "git+https://github.com/paritytech/bn?branch=upgrade2#309ab3c5795c5706f39227ded462eeffe6cf6f91" dependencies = [ "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "crunchy 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2004,7 +2006,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bitvec 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81be2dda00e233388a4605ceb6ce977503cf16e2859d8ef8899e6780b988f4b1" "checksum blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc.git?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)" = "" "checksum blake2b_simd 0.4.1 (git+https://github.com/oconnor663/blake2b_simd.git)" = "" -"checksum bn 0.4.4 (git+https://github.com/paritytech/bn)" = "" +"checksum bn 0.4.4 (git+https://github.com/paritytech/bn?branch=upgrade2)" = "" "checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" "checksum bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa" "checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index ef322600..cf6fb61d 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -12,7 +12,7 @@ sapling-crypto = { git = "https://github.com/zcash-hackworks/sapling-crypto.git" serde_json = "1.0" siphasher = "0.1.1" primitives = { path = "../primitives" } -bn = { git = "https://github.com/paritytech/bn" } +bn = { git = "https://github.com/paritytech/bn", branch = "upgrade2" } serde = "1.0" serde_derive = "1.0" rustc-hex = "2" diff --git a/crypto/src/pghr13.rs b/crypto/src/pghr13.rs index 5d8e8dba..135c8fd0 100644 --- a/crypto/src/pghr13.rs +++ b/crypto/src/pghr13.rs @@ -1,4 +1,4 @@ -pub use bn::{Fr, Fq, Fq2, G1, G2, Group, arith::{U256, U512}, AffineG1, AffineG2}; +pub use bn::{Fr, Fq, Fq2, G1, G2, Group, arith::{U256, U512}, AffineG1, AffineG2, CurveError}; use bn::pairing; use std::ops::Neg; use json::pghr13 as json; @@ -46,6 +46,13 @@ pub enum Error { NotFqMember, NotFq2Member, InvalidSignPrefix, + Curve(CurveError), +} + +impl From for Error { + fn from(e: CurveError) -> Self { + Error::Curve(e) + } } #[derive(Clone)] @@ -63,57 +70,40 @@ pub struct Proof { impl Proof { pub fn from_raw(data: &[u8; 296]) -> Result { Ok(Proof { - a: g1_from_compressed(&data[0..33])?, - a_prime: g1_from_compressed(&data[33..66])?, + a: G1::from_compressed(&data[0..33])?, + a_prime: G1::from_compressed(&data[33..66])?, b: g2_from_compressed(&data[66..131])?, - b_prime: g1_from_compressed(&data[131..164])?, - c: g1_from_compressed(&data[164..197])?, - c_prime: g1_from_compressed(&data[197..230])?, - k: g1_from_compressed(&data[230..263])?, - h: g1_from_compressed(&data[263..296])?, + b_prime: G1::from_compressed(&data[131..164])?, + c: G1::from_compressed(&data[164..197])?, + c_prime: G1::from_compressed(&data[197..230])?, + k: G1::from_compressed(&data[230..263])?, + h: G1::from_compressed(&data[263..296])?, }) } } lazy_static! { // integer modulus for Fq field - pub static ref FQ: U256 = U256::from([ + static ref FQ: U256 = U256::from([ 0x3c208c16d87cfd47, 0x97816a916871ca8d, 0xb85045b68181585d, 0x30644e72e131a029 ]); - pub static ref G1_B: Fq = Fq::from_u256(3.into()).expect("3 is a valid field element and static; qed"); + static ref G1_B: Fq = Fq::from_u256(3.into()).expect("3 is a valid field element and static; qed"); - pub static ref FQ_MINUS3_DIV4: Fq = - Fq::from_u256(3.into()).expect("3 is a valid field element and static; qed").neg() * - Fq::from_u256(4.into()).expect("4 is a valid field element and static; qed").inverse() - .expect("4 has inverse in Fq and is static; qed"); - - pub static ref FQ_MINUS1_DIV2: Fq = + static ref FQ_MINUS1_DIV2: Fq = Fq::from_u256(1.into()).expect("1 is a valid field element and static; qed").neg() * Fq::from_u256(2.into()).expect("2 is a valid field element and static; qed").inverse() .expect("2 has inverse in Fq and is static; qed"); + static ref FQ_MINUS3_DIV4: Fq = + Fq::from_u256(3.into()).expect("3 is a valid field element and static; qed").neg() * + Fq::from_u256(4.into()).expect("4 is a valid field element and static; qed").inverse() + .expect("4 has inverse in Fq and is static; qed"); } -// Shanks’s algorithm for q ≡ 3 (mod 4) -// (FQ mod 4 = 3) -fn fq_sqrt(a: Fq) -> Option { - let a1 = a.pow(*FQ_MINUS3_DIV4); - let a1a = a1 * a; - let a0 = a1 * (a1a); - - let mut am1 = *FQ; - am1.sub(&1.into(), &*FQ); - - if a0 == Fq::from_u256(am1).unwrap() { - None - } else { - Some(a1a) - } -} fn fq2_to_u512(e: Fq2) -> U512 { let c0 = e.real().into_u256(); @@ -142,24 +132,6 @@ fn fq2_sqrt(a: Fq2) -> Option { } } -fn g1_from_compressed(data: &[u8]) -> Result { - if data.len() != 33 { return Err(Error::InvalidRawInput); } - - let sign = data[0]; - let fq = deserialize_fq(&data[1..])?; - let x = fq; - let y_squared = (fq * fq * fq) + *G1_B; - - let mut y = fq_sqrt(y_squared).ok_or(Error::InvalidFieldElement)?; - - if sign == 2 && y.into_u256().get_bit(0).expect("bit 0 always exist; qed") { y = y.neg(); } - else if sign == 3 && !y.into_u256().get_bit(0).expect("bit 0 always exist; qed") { y = y.neg(); } - else if sign != 3 && sign != 2 { - return Err(Error::InvalidSignPrefix); - } - AffineG1::new(x, y).map_err(|_| Error::InvalidCurvePoint).map(Into::into) -} - fn g2_from_compressed(data: &[u8]) -> Result { if data.len() != 65 { return Err(Error::InvalidRawInput); } @@ -181,11 +153,6 @@ fn g2_from_compressed(data: &[u8]) -> Result { AffineG2::new(x, e_y).map_err(|_| Error::InvalidCurvePoint).map(Into::into) } -fn deserialize_fq(data: &[u8]) -> Result { - let u256 = U256::from_slice(data).map_err(|_| Error::InvalidU256Encoding)?; - Ok(Fq::from_u256(u256).map_err(|_| Error::NotFqMember)?) -} - fn deserialize_fq2(data: &[u8]) -> Result { let u512 = U512::from_slice(data).map_err(|_| Error::InvalidU512Encoding)?; let (res, c0) = u512.divrem(&Fq::modulus()); @@ -228,15 +195,6 @@ mod tests { s.from_hex().unwrap() } - #[test] - fn sqrt_fq() { - // from zcash test_proof.cpp - let fq1 = Fq::from_str("5204065062716160319596273903996315000119019512886596366359652578430118331601").unwrap(); - let fq2 = Fq::from_str("348579348568").unwrap(); - - assert_eq!(fq1, fq_sqrt(fq2).expect("348579348568 is quadratic residue")); - } - #[test] fn sqrt_fq2() { // from zcash test_proof.cpp @@ -264,14 +222,6 @@ mod tests { ); } - #[test] - fn g1_deserialize() { - let g1 = g1_from_compressed(&hex("0230644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46")).expect("Invalid g1 decompress result"); - assert_eq!(g1.x(), Fq::from_str("21888242871839275222246405745257275088696311157297823662689037894645226208582").unwrap()); - assert_eq!(g1.y(), Fq::from_str("3969792565221544645472939191694882283483352126195956956354061729942568608776").unwrap()); - assert_eq!(g1.z(), Fq::one()); - } - fn vkey() -> VerifyingKey { json::pghr13::decode(include_bytes!("../../res/sprout-verifying-key.json")).expect("known to be good").into() } @@ -528,4 +478,4 @@ mod tests { ).is_err() ); } -} \ No newline at end of file +} From c286191aec99fbb4a3b3343842dccb7b89c7e2f6 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 16 Apr 2019 17:37:24 +0300 Subject: [PATCH 07/12] g2 to bn --- Cargo.lock | 2 +- crypto/src/pghr13.rs | 162 +------------------------------------------ 2 files changed, 2 insertions(+), 162 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 59186fdf..5b32024d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -172,7 +172,7 @@ dependencies = [ [[package]] name = "bn" version = "0.4.4" -source = "git+https://github.com/paritytech/bn?branch=upgrade2#309ab3c5795c5706f39227ded462eeffe6cf6f91" +source = "git+https://github.com/paritytech/bn?branch=upgrade2#6d28d953ec01dc87118bf66ad465bb379b78c833" dependencies = [ "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "crunchy 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/crypto/src/pghr13.rs b/crypto/src/pghr13.rs index 135c8fd0..0af85e3e 100644 --- a/crypto/src/pghr13.rs +++ b/crypto/src/pghr13.rs @@ -1,6 +1,5 @@ pub use bn::{Fr, Fq, Fq2, G1, G2, Group, arith::{U256, U512}, AffineG1, AffineG2, CurveError}; use bn::pairing; -use std::ops::Neg; use json::pghr13 as json; #[derive(Clone)] @@ -72,7 +71,7 @@ impl Proof { Ok(Proof { a: G1::from_compressed(&data[0..33])?, a_prime: G1::from_compressed(&data[33..66])?, - b: g2_from_compressed(&data[66..131])?, + b: G2::from_compressed(&data[66..131])?, b_prime: G1::from_compressed(&data[131..164])?, c: G1::from_compressed(&data[164..197])?, c_prime: G1::from_compressed(&data[197..230])?, @@ -82,86 +81,6 @@ impl Proof { } } -lazy_static! { - // integer modulus for Fq field - static ref FQ: U256 = U256::from([ - 0x3c208c16d87cfd47, - 0x97816a916871ca8d, - 0xb85045b68181585d, - 0x30644e72e131a029 - ]); - - static ref G1_B: Fq = Fq::from_u256(3.into()).expect("3 is a valid field element and static; qed"); - - static ref FQ_MINUS1_DIV2: Fq = - Fq::from_u256(1.into()).expect("1 is a valid field element and static; qed").neg() * - Fq::from_u256(2.into()).expect("2 is a valid field element and static; qed").inverse() - .expect("2 has inverse in Fq and is static; qed"); - - static ref FQ_MINUS3_DIV4: Fq = - Fq::from_u256(3.into()).expect("3 is a valid field element and static; qed").neg() * - Fq::from_u256(4.into()).expect("4 is a valid field element and static; qed").inverse() - .expect("4 has inverse in Fq and is static; qed"); -} - - -fn fq2_to_u512(e: Fq2) -> U512 { - let c0 = e.real().into_u256(); - let c1 = e.imaginary().into_u256(); - - U512::new(&c1, &c0, &FQ) -} - -// Algorithm 9 Square root computation over Fq2, with q ≡ 3 (mod 4) -// from https://eprint.iacr.org/2012/685.pdf (Square root computation over even extension fields) -fn fq2_sqrt(a: Fq2) -> Option { - let a1 = a.pow(FQ_MINUS3_DIV4.into_u256()); - let a1a = a1 * a; - let alpha = a1 * a1a; - let a0 = alpha.pow(*FQ) * alpha; - - if a0 == Fq2::one().neg() { - return None; - } - - if alpha == Fq2::one().neg() { - Some(Fq2::i() * a1a) - } else { - let b = (alpha + Fq2::one()).pow(FQ_MINUS1_DIV2.into_u256()); - Some(b * a1a) - } -} - -fn g2_from_compressed(data: &[u8]) -> Result { - if data.len() != 65 { return Err(Error::InvalidRawInput); } - - let sign = data[0]; - let x = deserialize_fq2(&data[1..])?; - - let y_squared = (x * x * x) + G2::b(); - let y = fq2_sqrt(y_squared).ok_or(Error::InvalidFieldElement)?; - let y_neg = -y; - - let y_gt = fq2_to_u512(y) > fq2_to_u512(y_neg); - - let e_y = if sign == 10 { if y_gt { y_neg } else { y } } - else if sign == 11 { if y_gt { y } else { y_neg } } - else { - return Err(Error::InvalidSignPrefix); - }; - - AffineG2::new(x, e_y).map_err(|_| Error::InvalidCurvePoint).map(Into::into) -} - -fn deserialize_fq2(data: &[u8]) -> Result { - let u512 = U512::from_slice(data).map_err(|_| Error::InvalidU512Encoding)?; - let (res, c0) = u512.divrem(&Fq::modulus()); - Ok(Fq2::new( - Fq::from_u256(c0).map_err(|_| Error::NotFqMember)?, - Fq::from_u256(res.ok_or(Error::NotFq2Member)?).map_err(|_| Error::NotFqMember)?, - )) -} - pub fn verify(vk: &VerifyingKey, primary_input: &[Fr], proof: &Proof) -> bool { let p2 = G2::one(); @@ -190,38 +109,6 @@ mod tests { use super::*; use json; - fn hex(s: &'static str) -> Vec { - use hex::FromHex; - s.from_hex().unwrap() - } - - #[test] - fn sqrt_fq2() { - // from zcash test_proof.cpp - let x1 = Fq2::new( - Fq::from_str("12844195307879678418043983815760255909500142247603239203345049921980497041944").unwrap(), - Fq::from_str("7476417578426924565731404322659619974551724117137577781074613937423560117731").unwrap(), - ); - - let x2 = Fq2::new( - Fq::from_str("3345897230485723946872934576923485762803457692345760237495682347502347589474").unwrap(), - Fq::from_str("1234912378405347958234756902345768290345762348957605678245967234857634857676").unwrap(), - ); - - assert_eq!(fq2_sqrt(x2).unwrap(), x1); - - // i is sqrt(-1) - assert_eq!( - fq2_sqrt(Fq2::one().neg()).unwrap(), - Fq2::i(), - ); - - // no sqrt for (1 + 2i) - assert!( - fq2_sqrt(Fq2::new(Fq::from_str("1").unwrap(), Fq::from_str("2").unwrap())).is_none() - ); - } - fn vkey() -> VerifyingKey { json::pghr13::decode(include_bytes!("../../res/sprout-verifying-key.json")).expect("known to be good").into() } @@ -431,51 +318,4 @@ mod tests { assert!(verify(&vk, &primary_input[..], &proof)); } - - #[test] - fn g2_deserialize() { - let g2 = g2_from_compressed( - &hex("0a023aed31b5a9e486366ea9988b05dba469c6206e58361d9c065bbea7d928204a761efc6e4fa08ed227650134b52c7f7dd0463963e8a4bf21f4899fe5da7f984a") - ).expect("Valid g2 point hex encoding"); - - assert_eq!(g2.x(), - Fq2::new( - Fq::from_str("5923585509243758863255447226263146374209884951848029582715967108651637186684").unwrap(), - Fq::from_str("5336385337059958111259504403491065820971993066694750945459110579338490853570").unwrap(), - ) - ); - - assert_eq!(g2.y(), - Fq2::new( - Fq::from_str("10374495865873200088116930399159835104695426846400310764827677226300185211748").unwrap(), - Fq::from_str("5256529835065685814318509161957442385362539991735248614869838648137856366932").unwrap(), - ) - ); - - // 0b prefix is point reflection on the curve - let g2 = -g2_from_compressed( - &hex("0b023aed31b5a9e486366ea9988b05dba469c6206e58361d9c065bbea7d928204a761efc6e4fa08ed227650134b52c7f7dd0463963e8a4bf21f4899fe5da7f984a") - ).expect("Valid g2 point hex encoding"); - - assert_eq!(g2.x(), - Fq2::new( - Fq::from_str("5923585509243758863255447226263146374209884951848029582715967108651637186684").unwrap(), - Fq::from_str("5336385337059958111259504403491065820971993066694750945459110579338490853570").unwrap(), - ) - ); - - assert_eq!(g2.y(), - Fq2::new( - Fq::from_str("10374495865873200088116930399159835104695426846400310764827677226300185211748").unwrap(), - Fq::from_str("5256529835065685814318509161957442385362539991735248614869838648137856366932").unwrap(), - ) - ); - - // valid point but invalid sign prefix - assert!( - g2_from_compressed( - &hex("0c023aed31b5a9e486366ea9988b05dba469c6206e58361d9c065bbea7d928204a761efc6e4fa08ed227650134b52c7f7dd0463963e8a4bf21f4899fe5da7f984a") - ).is_err() - ); - } } From 5ffe954598f043f4ab088593ee4cb6584132fba8 Mon Sep 17 00:00:00 2001 From: Dimitris Apostolou Date: Wed, 17 Apr 2019 08:34:04 +0300 Subject: [PATCH 08/12] Bitcoin Core -> zcashd --- pzec/cli.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pzec/cli.yml b/pzec/cli.yml index c0a41cea..11b9b92b 100644 --- a/pzec/cli.yml +++ b/pzec/cli.yml @@ -102,11 +102,11 @@ args: value_name: ADDRESS subcommands: - import: - about: Import blocks from a Bitcoin Core database. + about: Import blocks from a zcashd database. args: - PATH: required: true - help: Path of the Bitcoin Core database. + help: Path of the zcashd database. - rollback: about: Rollback the database to given canonical-chain block. args: From a35f8b152a25adfec1132cc85887456e1985d1bb Mon Sep 17 00:00:00 2001 From: NikVolf Date: Wed, 17 Apr 2019 12:02:05 +0300 Subject: [PATCH 09/12] use bn master --- Cargo.lock | 6 +++--- crypto/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b32024d..e54bc59b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,7 +125,7 @@ dependencies = [ "bellman 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc.git?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)", "blake2b_simd 0.4.1 (git+https://github.com/oconnor663/blake2b_simd.git)", - "bn 0.4.4 (git+https://github.com/paritytech/bn?branch=upgrade2)", + "bn 0.4.4 (git+https://github.com/paritytech/bn)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", "primitives 0.1.0", @@ -172,7 +172,7 @@ dependencies = [ [[package]] name = "bn" version = "0.4.4" -source = "git+https://github.com/paritytech/bn?branch=upgrade2#6d28d953ec01dc87118bf66ad465bb379b78c833" +source = "git+https://github.com/paritytech/bn#162149011cb30ad4ad417be2cf1c3a4d15575274" dependencies = [ "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "crunchy 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2006,7 +2006,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bitvec 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81be2dda00e233388a4605ceb6ce977503cf16e2859d8ef8899e6780b988f4b1" "checksum blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc.git?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)" = "" "checksum blake2b_simd 0.4.1 (git+https://github.com/oconnor663/blake2b_simd.git)" = "" -"checksum bn 0.4.4 (git+https://github.com/paritytech/bn?branch=upgrade2)" = "" +"checksum bn 0.4.4 (git+https://github.com/paritytech/bn)" = "" "checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" "checksum bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa" "checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index cf6fb61d..ef322600 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -12,7 +12,7 @@ sapling-crypto = { git = "https://github.com/zcash-hackworks/sapling-crypto.git" serde_json = "1.0" siphasher = "0.1.1" primitives = { path = "../primitives" } -bn = { git = "https://github.com/paritytech/bn", branch = "upgrade2" } +bn = { git = "https://github.com/paritytech/bn" } serde = "1.0" serde_derive = "1.0" rustc-hex = "2" From 7f896ec75230175cc444e783a7746cdd99a106ff Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 24 Apr 2019 11:46:51 +0300 Subject: [PATCH 10/12] known_blocks_are_ignored_in_headers_verification_success --- sync/src/synchronization_chain.rs | 14 ++++-- sync/src/synchronization_client_core.rs | 63 +++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/sync/src/synchronization_chain.rs b/sync/src/synchronization_chain.rs index 391f0f2f..b08944ea 100644 --- a/sync/src/synchronization_chain.rs +++ b/sync/src/synchronization_chain.rs @@ -310,11 +310,12 @@ impl Chain { self.verifying_headers.extend(headers.iter().map(|h| h.hash)) } - /// Remove headers from verifying queue - pub fn headers_verified(&mut self, headers: &[IndexedBlockHeader]) { - for header in headers { - self.verifying_headers.remove(&header.hash); - } + /// Remove headers from verifying queue. + /// + /// Returns all headers that still have VerifyingHeader state (i.e. they are not Verifying || Stored). + pub fn headers_verified(&mut self, mut headers: Vec) -> Vec { + headers.retain(|header| self.verifying_headers.remove(&header.hash)); + headers } /// Schedule blocks hashes for requesting @@ -336,6 +337,9 @@ impl Chain { /// chain, guarantees the header has already been pre-verified. The opposite isn't true - /// if the header isn't in the chain, it could have been (in rare cases) pre-verified. pub fn verify_block(&mut self, header: IndexedBlockHeader) -> bool { + // when we start verifying block, forget that we (possibly) verifying header of the block + self.verifying_headers.remove(&header.hash); + // insert header to the in-memory chain in case when it is not already there (non-headers-first sync) self.hash_chain.push_back_at(VERIFYING_QUEUE, header.hash.clone()); self.headers_chain.insert(header) diff --git a/sync/src/synchronization_client_core.rs b/sync/src/synchronization_client_core.rs index 4c5348fb..53d6bf08 100644 --- a/sync/src/synchronization_client_core.rs +++ b/sync/src/synchronization_client_core.rs @@ -1058,7 +1058,7 @@ impl SynchronizationClientCore where T: TaskExecutor { } fn on_headers_verification_success(&mut self, headers: Vec) { - self.chain.headers_verified(&headers); + let headers = self.chain.headers_verified(headers); self.chain.schedule_blocks_headers(headers); @@ -1076,7 +1076,7 @@ impl SynchronizationClientCore where T: TaskExecutor { } fn on_headers_verification_error(&mut self, peer: PeerIndex, error: String, hash: H256, headers: Vec) { - self.chain.headers_verified(&headers); + self.chain.headers_verified(headers); if self.config.close_connection_on_bad_block { self.peers.misbehaving( @@ -1305,7 +1305,7 @@ pub mod tests { use std::sync::Arc; use parking_lot::{Mutex, RwLock}; - use chain::{Block, Transaction}; + use chain::{Block, Transaction, IndexedBlock}; use db::BlockChainDatabase; use message::common::InventoryVector; use message::{Services, types}; @@ -1314,7 +1314,7 @@ pub mod tests { use primitives::hash::H256; use verification::BackwardsCompatibleChainVerifier as ChainVerifier; use inbound_connection::tests::DummyOutboundSyncConnection; - use synchronization_chain::Chain; + use synchronization_chain::{Chain, BlockState}; use synchronization_client::{SynchronizationClient, Client}; use synchronization_peers::PeersImpl; use synchronization_executor::Task; @@ -2459,4 +2459,59 @@ pub mod tests { assert_eq!(data.lock().is_synchronizing, false); assert_eq!(data.lock().best_blocks.len(), 3); } + + #[test] + fn known_blocks_are_ignored_in_headers_verification_success() { + let (_, sync, _) = create_sync(None, None); + let mut sync = sync.lock(); + + let block1: IndexedBlock = test_data::block_h1().into(); + let block2: IndexedBlock = test_data::block_h2().into(); + let header1 = block1.header.clone(); + let header2 = block2.header.clone(); + let hash1 = *block1.hash(); + let hash2 = *block2.hash(); + + // WHEN + // we have orphaned [block2] + sync.orphaned_blocks_pool.insert_unknown_block(block2.clone()); + + // THEN: + + // [header1] received => [header1] verification starts + sync.on_headers(0, vec![header1.clone()]); + assert_eq!(sync.chain().block_state(&hash1), BlockState::VerifyingHeader); + assert_eq!(sync.chain().block_state(&hash2), BlockState::Unknown); + + // [header1] verification ends => [block1] is requested + sync.on_headers_verification_success(vec![header1.clone()]); + assert_eq!(sync.chain().block_state(&hash1), BlockState::Requested); + assert_eq!(sync.chain().block_state(&hash2), BlockState::Unknown); + + // [header2] received => [header2] verification starts + sync.on_headers(0, vec![header2.clone()]); + assert_eq!(sync.chain().block_state(&hash1), BlockState::Requested); + assert_eq!(sync.chain().block_state(&hash2), BlockState::VerifyingHeader); + + // [block1] received => [block1, block2] verification starts + sync.on_block(0, block1.clone()); + assert_eq!(sync.chain().block_state(&hash1), BlockState::Verifying); + assert_eq!(sync.chain().block_state(&hash2), BlockState::Verifying); // pre-fix: VerifyingHeader + + // [block1, block2] verification ends => [block1, block2] are inserted into DB + sync.on_block_verification_success(block1.clone()); + sync.on_block_verification_success(block2.clone()); + assert_eq!(sync.chain().block_state(&hash1), BlockState::Stored); + assert_eq!(sync.chain().block_state(&hash2), BlockState::Stored); // pre-fix: VerifyingHeader + + // [header2] verification ends => [block2] is requested + sync.on_headers_verification_success(vec![header2.clone()]); + assert_eq!(sync.chain().block_state(&hash1), BlockState::Stored); + assert_eq!(sync.chain().block_state(&hash2), BlockState::Stored); // pre-fix: Requested + + // [block2] received => [block2] verification starts + sync.on_block(0, block2.clone()); + assert_eq!(sync.chain().block_state(&hash1), BlockState::Stored); + assert_eq!(sync.chain().block_state(&hash2), BlockState::Stored); // pre-fix: Verifying + } } From ff0388f77082fb2445d14e524a0ee7a599ce5832 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 24 Apr 2019 11:58:26 +0300 Subject: [PATCH 11/12] added useful logs --- sync/src/synchronization_chain.rs | 2 +- sync/src/synchronization_client_core.rs | 38 +++++++++++++++++++++++-- verification/src/chain_verifier.rs | 2 +- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/sync/src/synchronization_chain.rs b/sync/src/synchronization_chain.rs index b08944ea..0aed7278 100644 --- a/sync/src/synchronization_chain.rs +++ b/sync/src/synchronization_chain.rs @@ -366,7 +366,7 @@ impl Chain { match block_origin { storage::BlockOrigin::KnownBlock => { // there should be no known blocks at this point - unreachable!(); + unreachable!("Trying to re-insert known block: {}", block.hash().to_reversed_str()); }, // case 1: block has been added to the main branch storage::BlockOrigin::CanonChain { .. } => { diff --git a/sync/src/synchronization_client_core.rs b/sync/src/synchronization_client_core.rs index 53d6bf08..d8ab71a2 100644 --- a/sync/src/synchronization_client_core.rs +++ b/sync/src/synchronization_client_core.rs @@ -255,7 +255,7 @@ impl ClientCore for SynchronizationClientCore where T: TaskExecutor { /// Try to queue synchronization of unknown blocks when blocks headers are received. fn on_headers(&mut self, peer_index: PeerIndex, headers: Vec) -> Option> { - assert!(! headers.is_empty(), "This must be checked in incoming connection"); + assert!(!headers.is_empty(), "This is checked in incoming connection"); // update peers to select next tasks self.peers_tasks.on_headers_received(peer_index); @@ -421,6 +421,13 @@ impl ClientCore for SynchronizationClientCore where T: TaskExecutor { self.chain.forget_block_leave_header(&block.header.hash); // remember this block as unknown if !self.orphaned_blocks_pool.contains_unknown_block(&block.header.hash) { + trace!( + target: "sync", + "Inserting unknown orphan block: {}. Block state: {:?}, parent state: {:?}", + block.header.hash.to_reversed_str(), + block_state, + parent_block_state, + ); self.orphaned_blocks_pool.insert_unknown_block(block); } } @@ -455,9 +462,27 @@ impl ClientCore for SynchronizationClientCore where T: TaskExecutor { entry.insert((blocks_to_verify_hashes.into_iter().collect(), Vec::new())); } } + + trace!( + target: "sync", + "Scheduling verification of blocks: {}..{} First block state: {:?}, parent state: {:?}", + blocks_to_verify[0].hash().to_reversed_str(), + blocks_to_verify[blocks_to_verify.len() - 1].hash().to_reversed_str(), + block_state, + parent_block_state, + ); + result = Some(blocks_to_verify); }, BlockState::Requested | BlockState::Scheduled => { + trace!( + target: "sync", + "Inserting known orphan block: {}. Block state: {:?}, parent state: {:?}", + block.header.hash.to_reversed_str(), + block_state, + parent_block_state, + ); + // remember peer as useful self.peers_tasks.useful_peer(peer_index); // remember as orphan block @@ -1059,8 +1084,15 @@ impl SynchronizationClientCore where T: TaskExecutor { fn on_headers_verification_success(&mut self, headers: Vec) { let headers = self.chain.headers_verified(headers); - - self.chain.schedule_blocks_headers(headers); + if !headers.is_empty() { + trace!( + target: "sync", + "Scheduling retrieval of headers: {}..{}", + headers[0].hash.to_reversed_str(), + headers[headers.len() - 1].hash.to_reversed_str(), + ); + self.chain.schedule_blocks_headers(headers); + } // switch to synchronization state if !self.state.is_synchronizing() { diff --git a/verification/src/chain_verifier.rs b/verification/src/chain_verifier.rs index 04e08d06..bc4dff9b 100644 --- a/verification/src/chain_verifier.rs +++ b/verification/src/chain_verifier.rs @@ -53,7 +53,7 @@ impl BackwardsCompatibleChainVerifier { match block_origin { BlockOrigin::KnownBlock => { // there should be no known blocks at this point - unreachable!(); + unreachable!("Trying to re-verify known block: {}", block.hash().reversed()); }, BlockOrigin::CanonChain { block_number } => { let tx_out_provider = CachedTransactionOutputProvider::new(self.store.as_store().as_transaction_output_provider()); From a5dbbb9d7ee667a554ca138ad83978b4bd99efac Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 25 Apr 2019 11:40:49 +0300 Subject: [PATCH 12/12] use empty values as false in comparison ops --- script/src/interpreter.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/script/src/interpreter.rs b/script/src/interpreter.rs index b7c34cbe..6ca59179 100644 --- a/script/src/interpreter.rs +++ b/script/src/interpreter.rs @@ -621,7 +621,7 @@ pub fn eval_script( if v1 == v2 { stack.push(vec![1].into()); } else { - stack.push(vec![0].into()); + stack.push(Bytes::new()); } }, Opcode::OP_EQUALVERIFY => { @@ -738,7 +738,7 @@ pub fn eval_script( if v2 <= v3 && v3 < v1 { stack.push(vec![1].into()); } else { - stack.push(vec![0].into()); + stack.push(Bytes::new()); } }, Opcode::OP_RIPEMD160 => { @@ -774,7 +774,7 @@ pub fn eval_script( if success { stack.push(vec![1].into()); } else { - stack.push(vec![0].into()); + stack.push(Bytes::new()); } }, Opcode::OP_CHECKSIGVERIFY if !success => { @@ -829,7 +829,7 @@ pub fn eval_script( if success { stack.push(vec![1].into()); } else { - stack.push(vec![0].into()); + stack.push(Bytes::new()); } }, Opcode::OP_CHECKMULTISIGVERIFY if !success => { @@ -951,7 +951,7 @@ mod tests { .push_opcode(Opcode::OP_EQUAL) .into_script(); let result = Ok(false); - let stack = vec![vec![0].into()].into(); + let stack = vec![Bytes::new()].into(); basic_test(&script, result, stack); } @@ -1783,7 +1783,7 @@ mod tests { .push_opcode(Opcode::OP_WITHIN) .into_script(); let result = Ok(false); - let stack = vec![vec![0].into()].into(); + let stack = vec![Bytes::new()].into(); basic_test(&script, result, stack); }