packages feed

haskoin-core 0.12.0 → 1.2.2

raw patch · 144 files changed

Files

CHANGELOG.md view
@@ -1,130 +1,471 @@ # Changelog+ All notable changes to this project will be documented in this file.  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## 0.11.0+## [1.2.2] - 2025-05-08++### Changed++- Standardize author name and correct README link.++## [1.2.1] - 2025-05-08++### Changed++- New home in github.com/jprupp rather than github.com/haskoin.++## [1.2.0] - 2023-07-23++### Changed++- Move from `cryptonite` to `crypton` for now.++## [1.1.0] - 2023-03-14++### Changed++- Update upstream dependencies & LTS Haskell.++## [1.0.4] - 2023-10-09++### Changed++- Export the BIP39 word list+- Add new BIP39 test vectors++## [1.0.3] - 2023-09-02++### Changed++- New dependency on data-default+- Renamed some arbitrary (test) functions for clarity+- Reworked the identity testing tool to include Marshal and MarshalJSON classes++## [1.0.2] - 2023-08-04++### Fixed++- Text files needed for testing now in tarball.++## [1.0.1] - 2023-08-03++### Changed++- Do not use alternative monad for Either String values.++## [1.0.0] - 2023-07-28++### Changed++- Shorten all field names allow duplicates, using DuplicateRecordFields extension.+- Use OverloadedRecordDot language extension and syntax.+- Use explicit context object from secp256k1-haskell library.+- Unify serialization into custom Marhsal and MarshalJSON classes.+- Use ormolu for formatting.+- Simplify module organisation.+- Strongly break backwards compatibility.++## [0.22.0] - 2023-06-28++### Changed++- Upgrade to lastest secp256k1 and base16 packages.++## [0.21.2] - 2022-04-13++### Changed++- Serialisation test now works for both strict and lazy bytestrings.++## [0.21.1] - 2021-12-13++### Changed++- Make Base58 faster.++## [0.21.0] - 2022-11-23+ ### Added++- BCH Testnet4 support.++### Changed++- Fix inconsistent serialization/deserialization issues.++### Fixed++- Makes `finalScriptWitness` field encoding conform to bitcoin core.+- Fixes bug in `finalizeTransaction`++### Added++- Signing support for PSBTs+- Helper function for merging PSBTs+- More PSBT tests+- Partial support for taproot++## [0.20.5] - 2021-09-13++### Added++- Support Bech32m address format for Taproot.++## [0.20.4] - 2021-06-08++### Fixed++- Add missing case for witness version.++## [0.20.3] - 2021-05-17++### Fixed++- Allow unknown inv types.++## [0.20.2] - 2021-05-17++### Fixed++- Allow unknown messages of zero length.++## [0.20.1] - 2021-05-14++### Fixed++- Correct case where binary search returned the wrong element.++## [0.20.0] - 2021-02-22++### Chaged++- Use bytes instead of binary or cereal.++## [0.19.0] - 2021-01-25++### Added++- Hashable instances for extended keys.++### Changed++- Mnemonic passphrases now `Text` instead of `ByteString`.++### Fixed++- Tests now pass for witness addresses.++## [0.18.0] - 2020-12-10++### Added++- Support SegWit addresses with version other than 0.++## [0.17.6] - 2020-12-07++### Added++- Serialize instances for `XPubKey` and `XPrvKey`.++## [0.17.5] - 2020-12-03++### Fixed++- Handle special case in block header binary search function.++## [0.17.4] - 2020-12-03++### Fixed++- Bounds check too restrictive in block header binary search function.++## [0.17.3] - 2020-11-17++### Changed++- Reduce minimum version of text package dependency.++## [0.17.2] - 2020-11-17++### Changed++- Update lists of seeds for all networks.++## [0.17.1] - 2020-11-02++### Changed++- Use the C-preprocessor to handle versions of `base16-bytestring`++## [0.17.0] - 2020-10-21++### Added++- Support for Bitcoin Cash November 2020 hard fork.+- Functions to find block headers matching arbitrary sorted attributes.++### Removed++- GenesisNode constructor for BlockNode type.++## [0.15.0] - 2020-07-23++### Added++- Add more test vectors++### Changed++- stringToAddr renamed to textToAddr+- Move ScriptOutput to Standard.hs+- Move WIF encoding/decoding to Keys.hs+- (breaking) rename `OP_NOP2` and `OP_NOP3` to `OP_CHECKLOCKTIMEVERIFY` and `OP_CHECKSEQUENCEVERIFY` resp.+- Update to latest secp256k1 bindings.++## [0.14.1] - 2020-06-14++### Fixed++- Correct some Bitcoin Cash Testnet3 seeds.+- Add helpers for writing Data.Serialize and Data.Aeson identity tests++## [0.14.0] - 2020-06-14++### Changed++- Expose all modules for tests.+- Tests depend on library instead of having access to its source code.+- Use MIT license.+- Update seeds.+- Bump secp256k1-haskell dependency.++## [0.13.6] - 2020-06-05++### Changed++- Expose the Arbitrary test instances under Haskoin.Util.Arbitrary++## [0.13.5] - 2020-05-16++### Changed++- Provide meaningful JSON instances for most types.++## [0.13.4] - 2020-05-14++### Added++- Support for Bitcoin Cash May 2020 hard fork.++## [0.13.3] - 2020-05-08++### Changed++- Improve code and documentation organisation.++## [0.13.2] - 2020-05-08++### Changed++- Move all packages from Network.Haskoin namespace to Haskoin namespace.+- Expose all top-level modules directly.++## [0.13.1] - 2020-05-06++### Changed++- Faster JSON serialization.++## [0.13.0] - 2020-05-06++### Changed++- Consolidate all modules in Haskoin module.++### Removed++- Deprecate Network.Haskoin namespace.+- Hide QuickCheck generators in test suite.++## [0.12.0] - 2020-04-10++### Added++- Support for signing segwit transactions. - High-level representation of segwit v0 data and auxilliary functions.  ### Changed+ - Adds handling of segwit signing parameters to transaction signing code. -## 0.10.1+## [0.10.1] - 2020-02-08+ ### Added+ - Lower bound versions for some dependencies. -## 0.10.0+## [0.10.0] - 2020-01-15+ ### Added+ - DeepSeq instances for all data types.  ### Changed+ - There is no `SockAddr` inside `NetworkAddress` anymore. -## 0.9.8+## [0.9.8] - 2020-01-01+ ### Added+ - Ord instance for `DerivPathI` -## 0.9.7+## [0.9.7] - 2019-12-04+ ### Added+ - JSON encoding/decoding for blocks.  ### Fixed+ - Fix lowercase HRP test for Bech32. -## 0.9.6+## [0.9.6] - 2019-10-29+ ### Added+ - `bloomRelevantUpdate` implementation for Bloom filters (thanks to @IlyasRidhuan).  ### Fixed+ - Fix for Bech32 encoding (thanks to @pavel-main). -## 0.9.5+## [0.9.5] - 2019-10-23+ ### Added+ - Expose functions added in 0.9.4. -## 0.9.4+## [0.9.4] - 2019-10-23+ ### Added+ - Support for (P2SH-)P2WPKH addresses derived from extended keys.  ### Changed+ - Change names of backwards-compatible P2SH-P2WPKH functions from 0.9.3. -## 0.9.3+## [0.9.3] - 2019-10-22+ ### Added+ - Some support for P2WPKH-over-P2SH addresses. -## 0.9.2+## [0.9.2] - 2019-10-09+ ### Removed+ - Disable unnecessary `-O2` optimisation added in previous version.  ### Added+ - Allow decoding unknown P2P messages. -## 0.9.1+## [0.9.1] - 2019-10-02+ ### Added+ - Add a function to produce a structured signature over a transaction. - Enable `-O2` optimisations. -## 0.9.0+## [0.9.0] - 2019-04-12+ ### Changed+ - Address conversion to string now defined for all inputs. -## 0.8.4+## [0.8.4] - 2018-12-05+ ### Added+ - Add reward computation to block functions.-- Add PSBT [BIP-174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) types and functions+- Add PSBT BIP-174 types and functions. -## 0.8.3+## [0.8.3] - 2018-12-04+ ### Added+ - Add reward halving interval parameter to network constants. -## 0.8.2+## [0.8.2] - 2018-11-06+ ### Added+ - Recognize `OP_CHECKDATASIG` and `OP_CHECKDATASIGVERIFY` opcodes. -## 0.8.1+## [0.8.1] - 2018-10-13+ ### Added+ - Add instances of `Hashable` and `Generic` where possible. -## 0.8.0+## [0.8.0] - 2018-10-13+ ### Removed+ - Remove `deepseq` dependency. - Remove network constant reference from address and extended keys. -## 0.7.0+## [0.7.0] - 2018-10-13+ ### Added+ - Add `Serialize` instance for network constants. - Add `Serialize` instance for addresses that includes network constants.  ### Changed+ - Move functions related to addresses from `Script` to `Address` module. -## 0.6.1+## [0.6.1] - 2018-10-09+ ### Added+ - Compatibility with latest GHC and base.  ### Changed+ - Update minimum base to 4.9. -## 0.6.0+## [0.6.0] - 2018-10-08+ ### Changed+ - Force initialization of addresses through smart constructor. - Assume addresses are always valid when instantiated in code. - Allow to provide unwrapped private keys to transaction signing functions. -## 0.5.2+## [0.5.2] - 2018-09-10+ ### Changed+ - Make dependencies more specific. -## 0.5.1+## [0.5.1] - 2018-09-10+ ### Changed+ - Remove some unneeded dependencies from `stack.yaml`. - Change `secp256k1` dependency to `secp256k1-haskell`. -## 0.5.0+## [0.5.0] - 2018-09-09+ ### Added+ - Support for Bitcoin Cash network block sychronization. - Support for Bitcoin Cash signatures. - Initial work on SegWit support.@@ -140,6 +481,7 @@ - Support for CashAddr addresses.  ### Changed+ - Use of hpack `package.yaml` file to auto-generate Cabal file. - Removal of dependency version limits, relying on `stack.yaml` instead. - Tests moved to `hspec`.@@ -150,6 +492,7 @@ - Target LTS Haskell 12.9.  ### Removed+ - Removed `.stylish-haskell.yaml` files. - Removed old `haskoin-node` and `haskoin-wallet` packages from main repository. - Removed support for non-strict signatures and related tests.
+ LICENSE view
@@ -0,0 +1,19 @@+Copyright 2020 Haskoin Developers++Permission is hereby granted, free of charge, to any person obtaining a copy of+this software and associated documentation files (the "Software"), to deal in+the Software without restriction, including without limitation the rights to+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of+the Software, and to permit persons to whom the Software is furnished to do so,+subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.+
README.md view
@@ -14,3 +14,8 @@ - Parsing and manipulation of all Bitcoin and Bitcoin Cash protocol messages - Bloom filters and partial merkle trees (used in SPV wallets) - Comprehensive test suite++## Contributing++All code is formatted with [Ormolu](https://github.com/tweag/ormolu).+Convenience formatting script available at [scripts/format](scripts/format)
− UNLICENSE
@@ -1,24 +0,0 @@-This is free and unencumbered software released into the public domain.--Anyone is free to copy, modify, publish, use, compile, sell, or-distribute this software, either in source code form or as a compiled-binary, for any purpose, commercial or non-commercial, and by any-means.--In jurisdictions that recognize copyright laws, the author or authors-of this software dedicate any and all copyright interest in the-software to the public domain. We make this dedication for the benefit-of the public at large and to the detriment of our heirs and-successors. We intend this dedication to be an overt act of-relinquishment in perpetuity of all present and future rights to this-software under copyright law.--THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR-OTHER DEALINGS IN THE SOFTWARE.--For more information, please refer to <http://unlicense.org/>
+ data/bip341.json view
@@ -0,0 +1,452 @@+{+    "version": 1,+    "scriptPubKey": [+        {+            "given": {+                "internalPubkey": "d6889cb081036e0faefa3a35157ad71086b123b2b144b649798b494c300a961d",+                "scriptTree": null+            },+            "intermediary": {+                "merkleRoot": null,+                "tweak": "b86e7be8f39bab32a6f2c0443abbc210f0edac0e2c53d501b36b64437d9c6c70",+                "tweakedPubkey": "53a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343"+            },+            "expected": {+                "scriptPubKey": "512053a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343",+                "bip350Address": "bc1p2wsldez5mud2yam29q22wgfh9439spgduvct83k3pm50fcxa5dps59h4z5"+            }+        },+        {+            "given": {+                "internalPubkey": "187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27",+                "scriptTree": {+                    "id": 0,+                    "script": "20d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8ac",+                    "leafVersion": 192+                }+            },+            "intermediary": {+                "leafHashes": [+                    "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21"+                ],+                "merkleRoot": "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21",+                "tweak": "cbd8679ba636c1110ea247542cfbd964131a6be84f873f7f3b62a777528ed001",+                "tweakedPubkey": "147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3"+            },+            "expected": {+                "scriptPubKey": "5120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3",+                "bip350Address": "bc1pz37fc4cn9ah8anwm4xqqhvxygjf9rjf2resrw8h8w4tmvcs0863sa2e586",+                "scriptPathControlBlocks": [+                    "c1187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27"+                ]+            }+        },+        {+            "given": {+                "internalPubkey": "93478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820",+                "scriptTree": {+                    "id": 0,+                    "script": "20b617298552a72ade070667e86ca63b8f5789a9fe8731ef91202a91c9f3459007ac",+                    "leafVersion": 192+                }+            },+            "intermediary": {+                "leafHashes": [+                    "c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b"+                ],+                "merkleRoot": "c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b",+                "tweak": "6af9e28dbf9d6aaf027696e2598a5b3d056f5fd2355a7fd5a37a0e5008132d30",+                "tweakedPubkey": "e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e"+            },+            "expected": {+                "scriptPubKey": "5120e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e",+                "bip350Address": "bc1punvppl2stp38f7kwv2u2spltjuvuaayuqsthe34hd2dyy5w4g58qqfuag5",+                "scriptPathControlBlocks": [+                    "c093478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820"+                ]+            }+        },+        {+            "given": {+                "internalPubkey": "ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592",+                "scriptTree": [+                    {+                        "id": 0,+                        "script": "20387671353e273264c495656e27e39ba899ea8fee3bb69fb2a680e22093447d48ac",+                        "leafVersion": 192+                    },+                    {+                        "id": 1,+                        "script": "06424950333431",+                        "leafVersion": 250+                    }+                ]+            },+            "intermediary": {+                "leafHashes": [+                    "8ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7",+                    "f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a"+                ],+                "merkleRoot": "6c2dc106ab816b73f9d07e3cd1ef2c8c1256f519748e0813e4edd2405d277bef",+                "tweak": "9e0517edc8259bb3359255400b23ca9507f2a91cd1e4250ba068b4eafceba4a9",+                "tweakedPubkey": "712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5"+            },+            "expected": {+                "scriptPubKey": "5120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5",+                "bip350Address": "bc1pwyjywgrd0ffr3tx8laflh6228dj98xkjj8rum0zfpd6h0e930h6saqxrrm",+                "scriptPathControlBlocks": [+                    "c0ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a",+                    "faee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf37865928ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7"+                ]+            }+        },+        {+            "given": {+                "internalPubkey": "f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd8",+                "scriptTree": [+                    {+                        "id": 0,+                        "script": "2044b178d64c32c4a05cc4f4d1407268f764c940d20ce97abfd44db5c3592b72fdac",+                        "leafVersion": 192+                    },+                    {+                        "id": 1,+                        "script": "07546170726f6f74",+                        "leafVersion": 192+                    }+                ]+            },+            "intermediary": {+                "leafHashes": [+                    "64512fecdb5afa04f98839b50e6f0cb7b1e539bf6f205f67934083cdcc3c8d89",+                    "2cb2b90daa543b544161530c925f285b06196940d6085ca9474d41dc3822c5cb"+                ],+                "merkleRoot": "ab179431c28d3b68fb798957faf5497d69c883c6fb1e1cd9f81483d87bac90cc",+                "tweak": "639f0281b7ac49e742cd25b7f188657626da1ad169209078e2761cefd91fd65e",+                "tweakedPubkey": "77e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220"+            },+            "expected": {+                "scriptPubKey": "512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220",+                "bip350Address": "bc1pwl3s54fzmk0cjnpl3w9af39je7pv5ldg504x5guk2hpecpg2kgsqaqstjq",+                "scriptPathControlBlocks": [+                    "c1f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd82cb2b90daa543b544161530c925f285b06196940d6085ca9474d41dc3822c5cb",+                    "c1f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd864512fecdb5afa04f98839b50e6f0cb7b1e539bf6f205f67934083cdcc3c8d89"+                ]+            }+        },+        {+            "given": {+                "internalPubkey": "e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f",+                "scriptTree": [+                    {+                        "id": 0,+                        "script": "2072ea6adcf1d371dea8fba1035a09f3d24ed5a059799bae114084130ee5898e69ac",+                        "leafVersion": 192+                    },+                    [+                        {+                            "id": 1,+                            "script": "202352d137f2f3ab38d1eaa976758873377fa5ebb817372c71e2c542313d4abda8ac",+                            "leafVersion": 192+                        },+                        {+                            "id": 2,+                            "script": "207337c0dd4253cb86f2c43a2351aadd82cccb12a172cd120452b9bb8324f2186aac",+                            "leafVersion": 192+                        }+                    ]+                ]+            },+            "intermediary": {+                "leafHashes": [+                    "2645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817",+                    "ba982a91d4fc552163cb1c0da03676102d5b7a014304c01f0c77b2b8e888de1c",+                    "9e31407bffa15fefbf5090b149d53959ecdf3f62b1246780238c24501d5ceaf6"+                ],+                "merkleRoot": "ccbd66c6f7e8fdab47b3a486f59d28262be857f30d4773f2d5ea47f7761ce0e2",+                "tweak": "b57bfa183d28eeb6ad688ddaabb265b4a41fbf68e5fed2c72c74de70d5a786f4",+                "tweakedPubkey": "91b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605"+            },+            "expected": {+                "scriptPubKey": "512091b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605",+                "bip350Address": "bc1pjxmy65eywgafs5tsunw95ruycpqcqnev6ynxp7jaasylcgtcxczs6n332e",+                "scriptPathControlBlocks": [+                    "c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6fffe578e9ea769027e4f5a3de40732f75a88a6353a09d767ddeb66accef85e553",+                    "c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f9e31407bffa15fefbf5090b149d53959ecdf3f62b1246780238c24501d5ceaf62645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817",+                    "c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6fba982a91d4fc552163cb1c0da03676102d5b7a014304c01f0c77b2b8e888de1c2645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817"+                ]+            }+        },+        {+            "given": {+                "internalPubkey": "55adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d",+                "scriptTree": [+                    {+                        "id": 0,+                        "script": "2071981521ad9fc9036687364118fb6ccd2035b96a423c59c5430e98310a11abe2ac",+                        "leafVersion": 192+                    },+                    [+                        {+                            "id": 1,+                            "script": "20d5094d2dbe9b76e2c245a2b89b6006888952e2faa6a149ae318d69e520617748ac",+                            "leafVersion": 192+                        },+                        {+                            "id": 2,+                            "script": "20c440b462ad48c7a77f94cd4532d8f2119dcebbd7c9764557e62726419b08ad4cac",+                            "leafVersion": 192+                        }+                    ]+                ]+            },+            "intermediary": {+                "leafHashes": [+                    "f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d",+                    "737ed1fe30bc42b8022d717b44f0d93516617af64a64753b7a06bf16b26cd711",+                    "d7485025fceb78b9ed667db36ed8b8dc7b1f0b307ac167fa516fe4352b9f4ef7"+                ],+                "merkleRoot": "2f6b2c5397b6d68ca18e09a3f05161668ffe93a988582d55c6f07bd5b3329def",+                "tweak": "6579138e7976dc13b6a92f7bfd5a2fc7684f5ea42419d43368301470f3b74ed9",+                "tweakedPubkey": "75169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831"+            },+            "expected": {+                "scriptPubKey": "512075169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831",+                "bip350Address": "bc1pw5tf7sqp4f50zka7629jrr036znzew70zxyvvej3zrpf8jg8hqcssyuewe",+                "scriptPathControlBlocks": [+                    "c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d3cd369a528b326bc9d2133cbd2ac21451acb31681a410434672c8e34fe757e91",+                    "c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312dd7485025fceb78b9ed667db36ed8b8dc7b1f0b307ac167fa516fe4352b9f4ef7f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d",+                    "c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d737ed1fe30bc42b8022d717b44f0d93516617af64a64753b7a06bf16b26cd711f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d"+                ]+            }+        }+    ],+    "keyPathSpending": [+        {+            "given": {+                "rawUnsignedTx": "02000000097de20cbff686da83a54981d2b9bab3586f4ca7e48f57f5b55963115f3b334e9c010000000000000000d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd990000000000fffffffff8e1f583384333689228c5d28eac13366be082dc57441760d957275419a418420000000000fffffffff0689180aa63b30cb162a73c6d2a38b7eeda2a83ece74310fda0843ad604853b0100000000feffffffaa5202bdf6d8ccd2ee0f0202afbbb7461d9264a25e5bfd3c5a52ee1239e0ba6c0000000000feffffff956149bdc66faa968eb2be2d2faa29718acbfe3941215893a2a3446d32acd050000000000000000000e664b9773b88c09c32cb70a2a3e4da0ced63b7ba3b22f848531bbb1d5d5f4c94010000000000000000e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf0000000000ffffffffa778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af10100000000ffffffff0200ca9a3b000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac807840cb0000000020ac9a87f5594be208f8532db38cff670c450ed2fea8fcdefcc9a663f78bab962b0065cd1d",+                "utxosSpent": [+                    {+                        "scriptPubKey": "512053a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343",+                        "amountSats": 420000000+                    },+                    {+                        "scriptPubKey": "5120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3",+                        "amountSats": 462000000+                    },+                    {+                        "scriptPubKey": "76a914751e76e8199196d454941c45d1b3a323f1433bd688ac",+                        "amountSats": 294000000+                    },+                    {+                        "scriptPubKey": "5120e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e",+                        "amountSats": 504000000+                    },+                    {+                        "scriptPubKey": "512091b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605",+                        "amountSats": 630000000+                    },+                    {+                        "scriptPubKey": "00147dd65592d0ab2fe0d0257d571abf032cd9db93dc",+                        "amountSats": 378000000+                    },+                    {+                        "scriptPubKey": "512075169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831",+                        "amountSats": 672000000+                    },+                    {+                        "scriptPubKey": "5120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5",+                        "amountSats": 546000000+                    },+                    {+                        "scriptPubKey": "512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220",+                        "amountSats": 588000000+                    }+                ]+            },+            "intermediary": {+                "hashAmounts": "58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde6",+                "hashOutputs": "a2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc5",+                "hashPrevouts": "e3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f",+                "hashScriptPubkeys": "23ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e21",+                "hashSequences": "18959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e"+            },+            "inputSpending": [+                {+                    "given": {+                        "txinIndex": 0,+                        "internalPrivkey": "6b973d88838f27366ed61c9ad6367663045cb456e28335c109e30717ae0c6baa",+                        "merkleRoot": null,+                        "hashType": 3+                    },+                    "intermediary": {+                        "internalPubkey": "d6889cb081036e0faefa3a35157ad71086b123b2b144b649798b494c300a961d",+                        "tweak": "b86e7be8f39bab32a6f2c0443abbc210f0edac0e2c53d501b36b64437d9c6c70",+                        "tweakedPrivkey": "2405b971772ad26915c8dcdf10f238753a9b837e5f8e6a86fd7c0cce5b7296d9",+                        "sigMsg": "0003020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e0000000000d0418f0e9a36245b9a50ec87f8bf5be5bcae434337b87139c3a5b1f56e33cba0",+                        "precomputedUsed": [+                            "hashAmounts",+                            "hashPrevouts",+                            "hashScriptPubkeys",+                            "hashSequences"+                        ],+                        "sigHash": "2514a6272f85cfa0f45eb907fcb0d121b808ed37c6ea160a5a9046ed5526d555"+                    },+                    "expected": {+                        "witness": [+                            "ed7c1647cb97379e76892be0cacff57ec4a7102aa24296ca39af7541246d8ff14d38958d4cc1e2e478e4d4a764bbfd835b16d4e314b72937b29833060b87276c03"+                        ]+                    }+                },+                {+                    "given": {+                        "txinIndex": 1,+                        "internalPrivkey": "1e4da49f6aaf4e5cd175fe08a32bb5cb4863d963921255f33d3bc31e1343907f",+                        "merkleRoot": "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21",+                        "hashType": 131+                    },+                    "intermediary": {+                        "internalPubkey": "187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27",+                        "tweak": "cbd8679ba636c1110ea247542cfbd964131a6be84f873f7f3b62a777528ed001",+                        "tweakedPrivkey": "ea260c3b10e60f6de018455cd0278f2f5b7e454be1999572789e6a9565d26080",+                        "sigMsg": "0083020000000065cd1d00d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd9900000000808f891b00000000225120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3ffffffffffcef8fb4ca7efc5433f591ecfc57391811ce1e186a3793024def5c884cba51d",+                        "precomputedUsed": [],+                        "sigHash": "325a644af47e8a5a2591cda0ab0723978537318f10e6a63d4eed783b96a71a4d"+                    },+                    "expected": {+                        "witness": [+                            "052aedffc554b41f52b521071793a6b88d6dbca9dba94cf34c83696de0c1ec35ca9c5ed4ab28059bd606a4f3a657eec0bb96661d42921b5f50a95ad33675b54f83"+                        ]+                    }+                },+                {+                    "given": {+                        "txinIndex": 3,+                        "internalPrivkey": "d3c7af07da2d54f7a7735d3d0fc4f0a73164db638b2f2f7c43f711f6d4aa7e64",+                        "merkleRoot": "c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b",+                        "hashType": 1+                    },+                    "intermediary": {+                        "internalPubkey": "93478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820",+                        "tweak": "6af9e28dbf9d6aaf027696e2598a5b3d056f5fd2355a7fd5a37a0e5008132d30",+                        "tweakedPrivkey": "97323385e57015b75b0339a549c56a948eb961555973f0951f555ae6039ef00d",+                        "sigMsg": "0001020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957ea2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc50003000000",+                        "precomputedUsed": [+                            "hashAmounts",+                            "hashOutputs",+                            "hashPrevouts",+                            "hashScriptPubkeys",+                            "hashSequences"+                        ],+                        "sigHash": "bf013ea93474aa67815b1b6cc441d23b64fa310911d991e713cd34c7f5d46669"+                    },+                    "expected": {+                        "witness": [+                            "ff45f742a876139946a149ab4d9185574b98dc919d2eb6754f8abaa59d18b025637a3aa043b91817739554f4ed2026cf8022dbd83e351ce1fabc272841d2510a01"+                        ]+                    }+                },+                {+                    "given": {+                        "txinIndex": 4,+                        "internalPrivkey": "f36bb07a11e469ce941d16b63b11b9b9120a84d9d87cff2c84a8d4affb438f4e",+                        "merkleRoot": "ccbd66c6f7e8fdab47b3a486f59d28262be857f30d4773f2d5ea47f7761ce0e2",+                        "hashType": 0+                    },+                    "intermediary": {+                        "internalPubkey": "e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f",+                        "tweak": "b57bfa183d28eeb6ad688ddaabb265b4a41fbf68e5fed2c72c74de70d5a786f4",+                        "tweakedPrivkey": "a8e7aa924f0d58854185a490e6c41f6efb7b675c0f3331b7f14b549400b4d501",+                        "sigMsg": "0000020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957ea2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc50004000000",+                        "precomputedUsed": [+                            "hashAmounts",+                            "hashOutputs",+                            "hashPrevouts",+                            "hashScriptPubkeys",+                            "hashSequences"+                        ],+                        "sigHash": "4f900a0bae3f1446fd48490c2958b5a023228f01661cda3496a11da502a7f7ef"+                    },+                    "expected": {+                        "witness": [+                            "b4010dd48a617db09926f729e79c33ae0b4e94b79f04a1ae93ede6315eb3669de185a17d2b0ac9ee09fd4c64b678a0b61a0a86fa888a273c8511be83bfd6810f"+                        ]+                    }+                },+                {+                    "given": {+                        "txinIndex": 6,+                        "internalPrivkey": "415cfe9c15d9cea27d8104d5517c06e9de48e2f986b695e4f5ffebf230e725d8",+                        "merkleRoot": "2f6b2c5397b6d68ca18e09a3f05161668ffe93a988582d55c6f07bd5b3329def",+                        "hashType": 2+                    },+                    "intermediary": {+                        "internalPubkey": "55adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d",+                        "tweak": "6579138e7976dc13b6a92f7bfd5a2fc7684f5ea42419d43368301470f3b74ed9",+                        "tweakedPrivkey": "241c14f2639d0d7139282aa6abde28dd8a067baa9d633e4e7230287ec2d02901",+                        "sigMsg": "0002020000000065cd1de3b33bb4ef3a52ad1fffb555c0d82828eb22737036eaeb02a235d82b909c4c3f58a6964a4f5f8f0b642ded0a8a553be7622a719da71d1f5befcefcdee8e0fde623ad0f61ad2bca5ba6a7693f50fce988e17c3780bf2b1e720cfbb38fbdd52e2118959c7221ab5ce9e26c3cd67b22c24f8baa54bac281d8e6b05e400e6c3a957e0006000000",+                        "precomputedUsed": [+                            "hashAmounts",+                            "hashPrevouts",+                            "hashScriptPubkeys",+                            "hashSequences"+                        ],+                        "sigHash": "15f25c298eb5cdc7eb1d638dd2d45c97c4c59dcaec6679cfc16ad84f30876b85"+                    },+                    "expected": {+                        "witness": [+                            "a3785919a2ce3c4ce26f298c3d51619bc474ae24014bcdd31328cd8cfbab2eff3395fa0a16fe5f486d12f22a9cedded5ae74feb4bbe5351346508c5405bcfee002"+                        ]+                    }+                },+                {+                    "given": {+                        "txinIndex": 7,+                        "internalPrivkey": "c7b0e81f0a9a0b0499e112279d718cca98e79a12e2f137c72ae5b213aad0d103",+                        "merkleRoot": "6c2dc106ab816b73f9d07e3cd1ef2c8c1256f519748e0813e4edd2405d277bef",+                        "hashType": 130+                    },+                    "intermediary": {+                        "internalPubkey": "ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592",+                        "tweak": "9e0517edc8259bb3359255400b23ca9507f2a91cd1e4250ba068b4eafceba4a9",+                        "tweakedPrivkey": "65b6000cd2bfa6b7cf736767a8955760e62b6649058cbc970b7c0871d786346b",+                        "sigMsg": "0082020000000065cd1d00e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf00000000804c8b2000000000225120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5ffffffff",+                        "precomputedUsed": [],+                        "sigHash": "cd292de50313804dabe4685e83f923d2969577191a3e1d2882220dca88cbeb10"+                    },+                    "expected": {+                        "witness": [+                            "ea0c6ba90763c2d3a296ad82ba45881abb4f426b3f87af162dd24d5109edc1cdd11915095ba47c3a9963dc1e6c432939872bc49212fe34c632cd3ab9fed429c482"+                        ]+                    }+                },+                {+                    "given": {+                        "txinIndex": 8,+                        "internalPrivkey": "77863416be0d0665e517e1c375fd6f75839544eca553675ef7fdf4949518ebaa",+                        "merkleRoot": "ab179431c28d3b68fb798957faf5497d69c883c6fb1e1cd9f81483d87bac90cc",+                        "hashType": 129+                    },+                    "intermediary": {+                        "internalPubkey": "f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd8",+                        "tweak": "639f0281b7ac49e742cd25b7f188657626da1ad169209078e2761cefd91fd65e",+                        "tweakedPrivkey": "ec18ce6af99f43815db543f47b8af5ff5df3b2cb7315c955aa4a86e8143d2bf5",+                        "sigMsg": "0081020000000065cd1da2e6dab7c1f0dcd297c8d61647fd17d821541ea69c3cc37dcbad7f90d4eb4bc500a778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af101000000002b0c230000000022512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220ffffffff",+                        "precomputedUsed": [+                            "hashOutputs"+                        ],+                        "sigHash": "cccb739eca6c13a8a89e6e5cd317ffe55669bbda23f2fd37b0f18755e008edd2"+                    },+                    "expected": {+                        "witness": [+                            "bbc9584a11074e83bc8c6759ec55401f0ae7b03ef290c3139814f545b58a9f8127258000874f44bc46db7646322107d4d86aec8e73b8719a61fff761d75b5dd981"+                        ]+                    }+                }+            ],+            "auxiliary": {+                "fulledSignedTx": "020000000001097de20cbff686da83a54981d2b9bab3586f4ca7e48f57f5b55963115f3b334e9c010000000000000000d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd990000000000fffffffff8e1f583384333689228c5d28eac13366be082dc57441760d957275419a41842000000006b4830450221008f3b8f8f0537c420654d2283673a761b7ee2ea3c130753103e08ce79201cf32a022079e7ab904a1980ef1c5890b648c8783f4d10103dd62f740d13daa79e298d50c201210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798fffffffff0689180aa63b30cb162a73c6d2a38b7eeda2a83ece74310fda0843ad604853b0100000000feffffffaa5202bdf6d8ccd2ee0f0202afbbb7461d9264a25e5bfd3c5a52ee1239e0ba6c0000000000feffffff956149bdc66faa968eb2be2d2faa29718acbfe3941215893a2a3446d32acd050000000000000000000e664b9773b88c09c32cb70a2a3e4da0ced63b7ba3b22f848531bbb1d5d5f4c94010000000000000000e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf0000000000ffffffffa778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af10100000000ffffffff0200ca9a3b000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac807840cb0000000020ac9a87f5594be208f8532db38cff670c450ed2fea8fcdefcc9a663f78bab962b0141ed7c1647cb97379e76892be0cacff57ec4a7102aa24296ca39af7541246d8ff14d38958d4cc1e2e478e4d4a764bbfd835b16d4e314b72937b29833060b87276c030141052aedffc554b41f52b521071793a6b88d6dbca9dba94cf34c83696de0c1ec35ca9c5ed4ab28059bd606a4f3a657eec0bb96661d42921b5f50a95ad33675b54f83000141ff45f742a876139946a149ab4d9185574b98dc919d2eb6754f8abaa59d18b025637a3aa043b91817739554f4ed2026cf8022dbd83e351ce1fabc272841d2510a010140b4010dd48a617db09926f729e79c33ae0b4e94b79f04a1ae93ede6315eb3669de185a17d2b0ac9ee09fd4c64b678a0b61a0a86fa888a273c8511be83bfd6810f0247304402202b795e4de72646d76eab3f0ab27dfa30b810e856ff3a46c9a702df53bb0d8cc302203ccc4d822edab5f35caddb10af1be93583526ccfbade4b4ead350781e2f8adcd012102f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f90141a3785919a2ce3c4ce26f298c3d51619bc474ae24014bcdd31328cd8cfbab2eff3395fa0a16fe5f486d12f22a9cedded5ae74feb4bbe5351346508c5405bcfee0020141ea0c6ba90763c2d3a296ad82ba45881abb4f426b3f87af162dd24d5109edc1cdd11915095ba47c3a9963dc1e6c432939872bc49212fe34c632cd3ab9fed429c4820141bbc9584a11074e83bc8c6759ec55401f0ae7b03ef290c3139814f545b58a9f8127258000874f44bc46db7646322107d4d86aec8e73b8719a61fff761d75b5dd9810065cd1d"+            }+        }+    ]+}
+ data/complex_psbt.json view
@@ -0,0 +1,18 @@+{+  "initial_psbt": "cHNidP8BAP2aAQIAAAAJ0winN9IByUg2QqbcNv1NVfiD8Uh8MhEGnB9i/Ff6SFAAAAAAAP/////TCKc30gHJSDZCptw2/U1V+IPxSHwyEQacH2L8V/pIUAEAAAAA/////9MIpzfSAclINkKm3Db9TVX4g/FIfDIRBpwfYvxX+khQAgAAAAD/////0winN9IByUg2QqbcNv1NVfiD8Uh8MhEGnB9i/Ff6SFADAAAAAP/////TCKc30gHJSDZCptw2/U1V+IPxSHwyEQacH2L8V/pIUAQAAAAA/////9MIpzfSAclINkKm3Db9TVX4g/FIfDIRBpwfYvxX+khQBQAAAAD/////0winN9IByUg2QqbcNv1NVfiD8Uh8MhEGnB9i/Ff6SFAGAAAAAP/////TCKc30gHJSDZCptw2/U1V+IPxSHwyEQacH2L8V/pIUAcAAAAA/////9MIpzfSAclINkKm3Db9TVX4g/FIfDIRBpwfYvxX+khQCAAAAAD/////AcBByBcAAAAAFgAU86NkDG3go6XBS3IWTpj5bKedZfoAAAAAAAAAAAAAAAAAAAA=",+  "miner_psbt": "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",+  "p2pkh_psbt": "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",+  "p2sh_pk_psbt": "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",+  "p2sh_ms_1_psbt": "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",+  "p2sh_ms_2_psbt": "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",+  "p2sh_wsh_pk_psbt": "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",+  "p2sh_wsh_ms_1_psbt": "cHNidP8BAP2aAQIAAAAJ0winN9IByUg2QqbcNv1NVfiD8Uh8MhEGnB9i/Ff6SFAAAAAAAP/////TCKc30gHJSDZCptw2/U1V+IPxSHwyEQacH2L8V/pIUAEAAAAA/////9MIpzfSAclINkKm3Db9TVX4g/FIfDIRBpwfYvxX+khQAgAAAAD/////0winN9IByUg2QqbcNv1NVfiD8Uh8MhEGnB9i/Ff6SFADAAAAAP/////TCKc30gHJSDZCptw2/U1V+IPxSHwyEQacH2L8V/pIUAQAAAAA/////9MIpzfSAclINkKm3Db9TVX4g/FIfDIRBpwfYvxX+khQBQAAAAD/////0winN9IByUg2QqbcNv1NVfiD8Uh8MhEGnB9i/Ff6SFAGAAAAAP/////TCKc30gHJSDZCptw2/U1V+IPxSHwyEQacH2L8V/pIUAcAAAAA/////9MIpzfSAclINkKm3Db9TVX4g/FIfDIRBpwfYvxX+khQCAAAAAD/////AcBByBcAAAAAFgAU86NkDG3go6XBS3IWTpj5bKedZfoAAAAAAAEA/WkBAgAAAAEXUNNyuMJUiRxTf70xOP1jeqP319FEl7pG0OAxv2QmFAAAAAAA/v///wnOXi4SAQAAABYAFJ/NWVnRkyEUv9JimT6osqkmcauxgPD6AgAAAAAXqRSd5nSHjaZHXgBa1LuBJLvcf35j/IeA8PoCAAAAACIAIFTqZ4EMPhJ4ZabcILTBn62oVb4Gu1kegf/amL1qFDRJgPD6AgAAAAAWABR2WjPfBmcSViSAyRnb5DCwReSmmYDw+gIAAAAAIgAgvnOo3OeUTjP10JgLepzp4olyPr52+65XfA+h2tB4LN6A8PoCAAAAABepFAyQrTw0xTxZivkWe2bigAgT1chph4Dw+gIAAAAAF6kULdzdQIJRR4ojhfFdmfLdw6wUqpWHgPD6AgAAAAAXqRRVX4CcBGmL8gYxdD+Qj4OQ95+vAoeA8PoCAAAAABl2qRRHrrheehmBxwe2/LDeK5ALI8ti6oisZQAAAAEBH85eLhIBAAAAFgAUn81ZWdGTIRS/0mKZPqiyqSZxq7EAAQD9aQECAAAAARdQ03K4wlSJHFN/vTE4/WN6o/fX0USXukbQ4DG/ZCYUAAAAAAD+////Cc5eLhIBAAAAFgAUn81ZWdGTIRS/0mKZPqiyqSZxq7GA8PoCAAAAABepFJ3mdIeNpkdeAFrUu4Eku9x/fmP8h4Dw+gIAAAAAIgAgVOpngQw+EnhlptwgtMGfrahVvga7WR6B/9qYvWoUNEmA8PoCAAAAABYAFHZaM98GZxJWJIDJGdvkMLBF5KaZgPD6AgAAAAAiACC+c6jc55ROM/XQmAt6nOniiXI+vnb7rld8D6Ha0Hgs3oDw+gIAAAAAF6kUDJCtPDTFPFmK+RZ7ZuKACBPVyGmHgPD6AgAAAAAXqRQt3N1AglFHiiOF8V2Z8t3DrBSqlYeA8PoCAAAAABepFFVfgJwEaYvyBjF0P5CPg5D3n68Ch4Dw+gIAAAAAGXapFEeuuF56GYHHB7b8sN4rkAsjy2LqiKxlAAAAAAEA/WkBAgAAAAEXUNNyuMJUiRxTf70xOP1jeqP319FEl7pG0OAxv2QmFAAAAAAA/v///wnOXi4SAQAAABYAFJ/NWVnRkyEUv9JimT6osqkmcauxgPD6AgAAAAAXqRSd5nSHjaZHXgBa1LuBJLvcf35j/IeA8PoCAAAAACIAIFTqZ4EMPhJ4ZabcILTBn62oVb4Gu1kegf/amL1qFDRJgPD6AgAAAAAWABR2WjPfBmcSViSAyRnb5DCwReSmmYDw+gIAAAAAIgAgvnOo3OeUTjP10JgLepzp4olyPr52+65XfA+h2tB4LN6A8PoCAAAAABepFAyQrTw0xTxZivkWe2bigAgT1chph4Dw+gIAAAAAF6kULdzdQIJRR4ojhfFdmfLdw6wUqpWHgPD6AgAAAAAXqRRVX4CcBGmL8gYxdD+Qj4OQ95+vAoeA8PoCAAAAABl2qRRHrrheehmBxwe2/LDeK5ALI8ti6oisZQAAAAABAP1pAQIAAAABF1DTcrjCVIkcU3+9MTj9Y3qj99fRRJe6RtDgMb9kJhQAAAAAAP7///8Jzl4uEgEAAAAWABSfzVlZ0ZMhFL/SYpk+qLKpJnGrsYDw+gIAAAAAF6kUneZ0h42mR14AWtS7gSS73H9+Y/yHgPD6AgAAAAAiACBU6meBDD4SeGWm3CC0wZ+tqFW+BrtZHoH/2pi9ahQ0SYDw+gIAAAAAFgAUdloz3wZnElYkgMkZ2+QwsEXkppmA8PoCAAAAACIAIL5zqNznlE4z9dCYC3qc6eKJcj6+dvuuV3wPodrQeCzegPD6AgAAAAAXqRQMkK08NMU8WYr5Fntm4oAIE9XIaYeA8PoCAAAAABepFC3c3UCCUUeKI4XxXZny3cOsFKqVh4Dw+gIAAAAAF6kUVV+AnARpi/IGMXQ/kI+DkPefrwKHgPD6AgAAAAAZdqkUR664XnoZgccHtvyw3iuQCyPLYuqIrGUAAAABAR+A8PoCAAAAABYAFHZaM98GZxJWJIDJGdvkMLBF5KaZAAEA/WkBAgAAAAEXUNNyuMJUiRxTf70xOP1jeqP319FEl7pG0OAxv2QmFAAAAAAA/v///wnOXi4SAQAAABYAFJ/NWVnRkyEUv9JimT6osqkmcauxgPD6AgAAAAAXqRSd5nSHjaZHXgBa1LuBJLvcf35j/IeA8PoCAAAAACIAIFTqZ4EMPhJ4ZabcILTBn62oVb4Gu1kegf/amL1qFDRJgPD6AgAAAAAWABR2WjPfBmcSViSAyRnb5DCwReSmmYDw+gIAAAAAIgAgvnOo3OeUTjP10JgLepzp4olyPr52+65XfA+h2tB4LN6A8PoCAAAAABepFAyQrTw0xTxZivkWe2bigAgT1chph4Dw+gIAAAAAF6kULdzdQIJRR4ojhfFdmfLdw6wUqpWHgPD6AgAAAAAXqRRVX4CcBGmL8gYxdD+Qj4OQ95+vAoeA8PoCAAAAABl2qRRHrrheehmBxwe2/LDeK5ALI8ti6oisZQAAAAABAP1pAQIAAAABF1DTcrjCVIkcU3+9MTj9Y3qj99fRRJe6RtDgMb9kJhQAAAAAAP7///8Jzl4uEgEAAAAWABSfzVlZ0ZMhFL/SYpk+qLKpJnGrsYDw+gIAAAAAF6kUneZ0h42mR14AWtS7gSS73H9+Y/yHgPD6AgAAAAAiACBU6meBDD4SeGWm3CC0wZ+tqFW+BrtZHoH/2pi9ahQ0SYDw+gIAAAAAFgAUdloz3wZnElYkgMkZ2+QwsEXkppmA8PoCAAAAACIAIL5zqNznlE4z9dCYC3qc6eKJcj6+dvuuV3wPodrQeCzegPD6AgAAAAAXqRQMkK08NMU8WYr5Fntm4oAIE9XIaYeA8PoCAAAAABepFC3c3UCCUUeKI4XxXZny3cOsFKqVh4Dw+gIAAAAAF6kUVV+AnARpi/IGMXQ/kI+DkPefrwKHgPD6AgAAAAAZdqkUR664XnoZgccHtvyw3iuQCyPLYuqIrGUAAAABASCA8PoCAAAAABepFAyQrTw0xTxZivkWe2bigAgT1chphyICA3j8GOuIWnK8bD4+vsXq3hvODvaKSGOyt866LZblEZcvRzBEAiAmsEB3Lsapby4p9FfInpUgwi9H42iUXgrV8a8ENeqAeQIgKLTYlYUsPvtK17dhM8ze4PQlQxeB6LLwmAFK3kxzafwBAQQiACC50l4yj/cqAwKr3ZjnkAme0/Mmd1oanHsxRYufLZVfYwEFaVIhAhzyD15pnUspOXskhfMi8hrrkyZKuFMrxhEnl4QY1J7RIQLAb5THUmmR8mkB8ifjq3s5h0m8DDB7CkiR3cJayIr1HCEDePwY64hacrxsPj6+xereG84O9opIY7K3zrotluURly9TriIGAhzyD15pnUspOXskhfMi8hrrkyZKuFMrxhEnl4QY1J7RBF0EzPMiBgLAb5THUmmR8mkB8ifjq3s5h0m8DDB7CkiR3cJayIr1HASVBcNoIgYDePwY64hacrxsPj6+xereG84O9opIY7K3zrotluURly8EkECwlQABAP1pAQIAAAABF1DTcrjCVIkcU3+9MTj9Y3qj99fRRJe6RtDgMb9kJhQAAAAAAP7///8Jzl4uEgEAAAAWABSfzVlZ0ZMhFL/SYpk+qLKpJnGrsYDw+gIAAAAAF6kUneZ0h42mR14AWtS7gSS73H9+Y/yHgPD6AgAAAAAiACBU6meBDD4SeGWm3CC0wZ+tqFW+BrtZHoH/2pi9ahQ0SYDw+gIAAAAAFgAUdloz3wZnElYkgMkZ2+QwsEXkppmA8PoCAAAAACIAIL5zqNznlE4z9dCYC3qc6eKJcj6+dvuuV3wPodrQeCzegPD6AgAAAAAXqRQMkK08NMU8WYr5Fntm4oAIE9XIaYeA8PoCAAAAABepFC3c3UCCUUeKI4XxXZny3cOsFKqVh4Dw+gIAAAAAF6kUVV+AnARpi/IGMXQ/kI+DkPefrwKHgPD6AgAAAAAZdqkUR664XnoZgccHtvyw3iuQCyPLYuqIrGUAAAAAAQD9aQECAAAAARdQ03K4wlSJHFN/vTE4/WN6o/fX0USXukbQ4DG/ZCYUAAAAAAD+////Cc5eLhIBAAAAFgAUn81ZWdGTIRS/0mKZPqiyqSZxq7GA8PoCAAAAABepFJ3mdIeNpkdeAFrUu4Eku9x/fmP8h4Dw+gIAAAAAIgAgVOpngQw+EnhlptwgtMGfrahVvga7WR6B/9qYvWoUNEmA8PoCAAAAABYAFHZaM98GZxJWJIDJGdvkMLBF5KaZgPD6AgAAAAAiACC+c6jc55ROM/XQmAt6nOniiXI+vnb7rld8D6Ha0Hgs3oDw+gIAAAAAF6kUDJCtPDTFPFmK+RZ7ZuKACBPVyGmHgPD6AgAAAAAXqRQt3N1AglFHiiOF8V2Z8t3DrBSqlYeA8PoCAAAAABepFFVfgJwEaYvyBjF0P5CPg5D3n68Ch4Dw+gIAAAAAGXapFEeuuF56GYHHB7b8sN4rkAsjy2LqiKxlAAAAAAEA/WkBAgAAAAEXUNNyuMJUiRxTf70xOP1jeqP319FEl7pG0OAxv2QmFAAAAAAA/v///wnOXi4SAQAAABYAFJ/NWVnRkyEUv9JimT6osqkmcauxgPD6AgAAAAAXqRSd5nSHjaZHXgBa1LuBJLvcf35j/IeA8PoCAAAAACIAIFTqZ4EMPhJ4ZabcILTBn62oVb4Gu1kegf/amL1qFDRJgPD6AgAAAAAWABR2WjPfBmcSViSAyRnb5DCwReSmmYDw+gIAAAAAIgAgvnOo3OeUTjP10JgLepzp4olyPr52+65XfA+h2tB4LN6A8PoCAAAAABepFAyQrTw0xTxZivkWe2bigAgT1chph4Dw+gIAAAAAF6kULdzdQIJRR4ojhfFdmfLdw6wUqpWHgPD6AgAAAAAXqRRVX4CcBGmL8gYxdD+Qj4OQ95+vAoeA8PoCAAAAABl2qRRHrrheehmBxwe2/LDeK5ALI8ti6oisZQAAAAAA",+  "p2sh_wsh_ms_2_psbt": "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",+  "p2wpkh_psbt": "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",+  "p2wsh_pk_psbt": "cHNidP8BAP2aAQIAAAAJ0winN9IByUg2QqbcNv1NVfiD8Uh8MhEGnB9i/Ff6SFAAAAAAAP/////TCKc30gHJSDZCptw2/U1V+IPxSHwyEQacH2L8V/pIUAEAAAAA/////9MIpzfSAclINkKm3Db9TVX4g/FIfDIRBpwfYvxX+khQAgAAAAD/////0winN9IByUg2QqbcNv1NVfiD8Uh8MhEGnB9i/Ff6SFADAAAAAP/////TCKc30gHJSDZCptw2/U1V+IPxSHwyEQacH2L8V/pIUAQAAAAA/////9MIpzfSAclINkKm3Db9TVX4g/FIfDIRBpwfYvxX+khQBQAAAAD/////0winN9IByUg2QqbcNv1NVfiD8Uh8MhEGnB9i/Ff6SFAGAAAAAP/////TCKc30gHJSDZCptw2/U1V+IPxSHwyEQacH2L8V/pIUAcAAAAA/////9MIpzfSAclINkKm3Db9TVX4g/FIfDIRBpwfYvxX+khQCAAAAAD/////AcBByBcAAAAAFgAU86NkDG3go6XBS3IWTpj5bKedZfoAAAAAAAEA/WkBAgAAAAEXUNNyuMJUiRxTf70xOP1jeqP319FEl7pG0OAxv2QmFAAAAAAA/v///wnOXi4SAQAAABYAFJ/NWVnRkyEUv9JimT6osqkmcauxgPD6AgAAAAAXqRSd5nSHjaZHXgBa1LuBJLvcf35j/IeA8PoCAAAAACIAIFTqZ4EMPhJ4ZabcILTBn62oVb4Gu1kegf/amL1qFDRJgPD6AgAAAAAWABR2WjPfBmcSViSAyRnb5DCwReSmmYDw+gIAAAAAIgAgvnOo3OeUTjP10JgLepzp4olyPr52+65XfA+h2tB4LN6A8PoCAAAAABepFAyQrTw0xTxZivkWe2bigAgT1chph4Dw+gIAAAAAF6kULdzdQIJRR4ojhfFdmfLdw6wUqpWHgPD6AgAAAAAXqRRVX4CcBGmL8gYxdD+Qj4OQ95+vAoeA8PoCAAAAABl2qRRHrrheehmBxwe2/LDeK5ALI8ti6oisZQAAAAEBH85eLhIBAAAAFgAUn81ZWdGTIRS/0mKZPqiyqSZxq7EAAQD9aQECAAAAARdQ03K4wlSJHFN/vTE4/WN6o/fX0USXukbQ4DG/ZCYUAAAAAAD+////Cc5eLhIBAAAAFgAUn81ZWdGTIRS/0mKZPqiyqSZxq7GA8PoCAAAAABepFJ3mdIeNpkdeAFrUu4Eku9x/fmP8h4Dw+gIAAAAAIgAgVOpngQw+EnhlptwgtMGfrahVvga7WR6B/9qYvWoUNEmA8PoCAAAAABYAFHZaM98GZxJWJIDJGdvkMLBF5KaZgPD6AgAAAAAiACC+c6jc55ROM/XQmAt6nOniiXI+vnb7rld8D6Ha0Hgs3oDw+gIAAAAAF6kUDJCtPDTFPFmK+RZ7ZuKACBPVyGmHgPD6AgAAAAAXqRQt3N1AglFHiiOF8V2Z8t3DrBSqlYeA8PoCAAAAABepFFVfgJwEaYvyBjF0P5CPg5D3n68Ch4Dw+gIAAAAAGXapFEeuuF56GYHHB7b8sN4rkAsjy2LqiKxlAAAAAAEA/WkBAgAAAAEXUNNyuMJUiRxTf70xOP1jeqP319FEl7pG0OAxv2QmFAAAAAAA/v///wnOXi4SAQAAABYAFJ/NWVnRkyEUv9JimT6osqkmcauxgPD6AgAAAAAXqRSd5nSHjaZHXgBa1LuBJLvcf35j/IeA8PoCAAAAACIAIFTqZ4EMPhJ4ZabcILTBn62oVb4Gu1kegf/amL1qFDRJgPD6AgAAAAAWABR2WjPfBmcSViSAyRnb5DCwReSmmYDw+gIAAAAAIgAgvnOo3OeUTjP10JgLepzp4olyPr52+65XfA+h2tB4LN6A8PoCAAAAABepFAyQrTw0xTxZivkWe2bigAgT1chph4Dw+gIAAAAAF6kULdzdQIJRR4ojhfFdmfLdw6wUqpWHgPD6AgAAAAAXqRRVX4CcBGmL8gYxdD+Qj4OQ95+vAoeA8PoCAAAAABl2qRRHrrheehmBxwe2/LDeK5ALI8ti6oisZQAAAAEBK4Dw+gIAAAAAIgAgVOpngQw+EnhlptwgtMGfrahVvga7WR6B/9qYvWoUNEkBCG0CRzBEAiAgppSlBXPyJhuvNZOvzBCsnm50+0s2qNuCc+2XYZXAowIgXjxcZO2KsZqhoZNBlcifRCSzTZn5+mLHgWVLVafFhCcBIyECCuSWz8qG5RyIxFQ4twaXreeWlCnXnO7956gk/FhdD3+sAAEA/WkBAgAAAAEXUNNyuMJUiRxTf70xOP1jeqP319FEl7pG0OAxv2QmFAAAAAAA/v///wnOXi4SAQAAABYAFJ/NWVnRkyEUv9JimT6osqkmcauxgPD6AgAAAAAXqRSd5nSHjaZHXgBa1LuBJLvcf35j/IeA8PoCAAAAACIAIFTqZ4EMPhJ4ZabcILTBn62oVb4Gu1kegf/amL1qFDRJgPD6AgAAAAAWABR2WjPfBmcSViSAyRnb5DCwReSmmYDw+gIAAAAAIgAgvnOo3OeUTjP10JgLepzp4olyPr52+65XfA+h2tB4LN6A8PoCAAAAABepFAyQrTw0xTxZivkWe2bigAgT1chph4Dw+gIAAAAAF6kULdzdQIJRR4ojhfFdmfLdw6wUqpWHgPD6AgAAAAAXqRRVX4CcBGmL8gYxdD+Qj4OQ95+vAoeA8PoCAAAAABl2qRRHrrheehmBxwe2/LDeK5ALI8ti6oisZQAAAAEBH4Dw+gIAAAAAFgAUdloz3wZnElYkgMkZ2+QwsEXkppkAAQD9aQECAAAAARdQ03K4wlSJHFN/vTE4/WN6o/fX0USXukbQ4DG/ZCYUAAAAAAD+////Cc5eLhIBAAAAFgAUn81ZWdGTIRS/0mKZPqiyqSZxq7GA8PoCAAAAABepFJ3mdIeNpkdeAFrUu4Eku9x/fmP8h4Dw+gIAAAAAIgAgVOpngQw+EnhlptwgtMGfrahVvga7WR6B/9qYvWoUNEmA8PoCAAAAABYAFHZaM98GZxJWJIDJGdvkMLBF5KaZgPD6AgAAAAAiACC+c6jc55ROM/XQmAt6nOniiXI+vnb7rld8D6Ha0Hgs3oDw+gIAAAAAF6kUDJCtPDTFPFmK+RZ7ZuKACBPVyGmHgPD6AgAAAAAXqRQt3N1AglFHiiOF8V2Z8t3DrBSqlYeA8PoCAAAAABepFFVfgJwEaYvyBjF0P5CPg5D3n68Ch4Dw+gIAAAAAGXapFEeuuF56GYHHB7b8sN4rkAsjy2LqiKxlAAAAAAEA/WkBAgAAAAEXUNNyuMJUiRxTf70xOP1jeqP319FEl7pG0OAxv2QmFAAAAAAA/v///wnOXi4SAQAAABYAFJ/NWVnRkyEUv9JimT6osqkmcauxgPD6AgAAAAAXqRSd5nSHjaZHXgBa1LuBJLvcf35j/IeA8PoCAAAAACIAIFTqZ4EMPhJ4ZabcILTBn62oVb4Gu1kegf/amL1qFDRJgPD6AgAAAAAWABR2WjPfBmcSViSAyRnb5DCwReSmmYDw+gIAAAAAIgAgvnOo3OeUTjP10JgLepzp4olyPr52+65XfA+h2tB4LN6A8PoCAAAAABepFAyQrTw0xTxZivkWe2bigAgT1chph4Dw+gIAAAAAF6kULdzdQIJRR4ojhfFdmfLdw6wUqpWHgPD6AgAAAAAXqRRVX4CcBGmL8gYxdD+Qj4OQ95+vAoeA8PoCAAAAABl2qRRHrrheehmBxwe2/LDeK5ALI8ti6oisZQAAAAABAP1pAQIAAAABF1DTcrjCVIkcU3+9MTj9Y3qj99fRRJe6RtDgMb9kJhQAAAAAAP7///8Jzl4uEgEAAAAWABSfzVlZ0ZMhFL/SYpk+qLKpJnGrsYDw+gIAAAAAF6kUneZ0h42mR14AWtS7gSS73H9+Y/yHgPD6AgAAAAAiACBU6meBDD4SeGWm3CC0wZ+tqFW+BrtZHoH/2pi9ahQ0SYDw+gIAAAAAFgAUdloz3wZnElYkgMkZ2+QwsEXkppmA8PoCAAAAACIAIL5zqNznlE4z9dCYC3qc6eKJcj6+dvuuV3wPodrQeCzegPD6AgAAAAAXqRQMkK08NMU8WYr5Fntm4oAIE9XIaYeA8PoCAAAAABepFC3c3UCCUUeKI4XxXZny3cOsFKqVh4Dw+gIAAAAAF6kUVV+AnARpi/IGMXQ/kI+DkPefrwKHgPD6AgAAAAAZdqkUR664XnoZgccHtvyw3iuQCyPLYuqIrGUAAAAAAQD9aQECAAAAARdQ03K4wlSJHFN/vTE4/WN6o/fX0USXukbQ4DG/ZCYUAAAAAAD+////Cc5eLhIBAAAAFgAUn81ZWdGTIRS/0mKZPqiyqSZxq7GA8PoCAAAAABepFJ3mdIeNpkdeAFrUu4Eku9x/fmP8h4Dw+gIAAAAAIgAgVOpngQw+EnhlptwgtMGfrahVvga7WR6B/9qYvWoUNEmA8PoCAAAAABYAFHZaM98GZxJWJIDJGdvkMLBF5KaZgPD6AgAAAAAiACC+c6jc55ROM/XQmAt6nOniiXI+vnb7rld8D6Ha0Hgs3oDw+gIAAAAAF6kUDJCtPDTFPFmK+RZ7ZuKACBPVyGmHgPD6AgAAAAAXqRQt3N1AglFHiiOF8V2Z8t3DrBSqlYeA8PoCAAAAABepFFVfgJwEaYvyBjF0P5CPg5D3n68Ch4Dw+gIAAAAAGXapFEeuuF56GYHHB7b8sN4rkAsjy2LqiKxlAAAAAAEA/WkBAgAAAAEXUNNyuMJUiRxTf70xOP1jeqP319FEl7pG0OAxv2QmFAAAAAAA/v///wnOXi4SAQAAABYAFJ/NWVnRkyEUv9JimT6osqkmcauxgPD6AgAAAAAXqRSd5nSHjaZHXgBa1LuBJLvcf35j/IeA8PoCAAAAACIAIFTqZ4EMPhJ4ZabcILTBn62oVb4Gu1kegf/amL1qFDRJgPD6AgAAAAAWABR2WjPfBmcSViSAyRnb5DCwReSmmYDw+gIAAAAAIgAgvnOo3OeUTjP10JgLepzp4olyPr52+65XfA+h2tB4LN6A8PoCAAAAABepFAyQrTw0xTxZivkWe2bigAgT1chph4Dw+gIAAAAAF6kULdzdQIJRR4ojhfFdmfLdw6wUqpWHgPD6AgAAAAAXqRRVX4CcBGmL8gYxdD+Qj4OQ95+vAoeA8PoCAAAAABl2qRRHrrheehmBxwe2/LDeK5ALI8ti6oisZQAAAAAA",+  "p2wsh_ms_1_psbt": "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",+  "p2wsh_ms_2_psbt": "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",+  "combined_psbt": "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",+  "complete_psbt": "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",+  "final_tx": "02000000000109d308a737d201c9483642a6dc36fd4d55f883f1487c3211069c1f62fc57fa48500000000000ffffffffd308a737d201c9483642a6dc36fd4d55f883f1487c3211069c1f62fc57fa485001000000232200208637ab26e75a3150d2aafa9d5d4fd9ba4db83f952ae98c7301f3a25752f479e5ffffffffd308a737d201c9483642a6dc36fd4d55f883f1487c3211069c1f62fc57fa48500200000000ffffffffd308a737d201c9483642a6dc36fd4d55f883f1487c3211069c1f62fc57fa48500300000000ffffffffd308a737d201c9483642a6dc36fd4d55f883f1487c3211069c1f62fc57fa48500400000000ffffffffd308a737d201c9483642a6dc36fd4d55f883f1487c3211069c1f62fc57fa48500500000023220020b9d25e328ff72a0302abdd98e790099ed3f326775a1a9c7b31458b9f2d955f63ffffffffd308a737d201c9483642a6dc36fd4d55f883f1487c3211069c1f62fc57fa485006000000fc0047304402207b0a8e5b2163005382621e4810d59e0e5c729b574a7abe4f2dec0670be202177022068203b4a9ffe67ac05bca14569887050d6a3727e0bbc254a972b7a1c2f3fa039014730440220736d307da0c73188ab312e55d12c3026e0804a5e2a17dd99a3d86a3abca7375e0220499b86bb24e19f106ef883e5a3e38b2e62dc7554da2a447204ccf383bed4bd67014c695221031546a19b617dc6a83810d8e07f334ce351b418613eaf2635aa427b1526a5c7f821033956816eb50d404b3d5f3279915a17a20728a46363df05a7f29d46d554062650210389993845fe8d4bd284242c6db6c76370b091ebcf170433f7304f6ec1cca9480c53aeffffffffd308a737d201c9483642a6dc36fd4d55f883f1487c3211069c1f62fc57fa4850070000006c47304402205ba47300dd590f445cb56443ce6c13f0bcd07129de44764a26b83705f66262a602205bba7cd49fe34aa1c06e83ead381d64382dcb0109adcd9157250656d781929ff01232102628c8b13d6ad3290b100d40ae1cdecae515efc3f1abd5ef854c0755cb187d1f6acffffffffd308a737d201c9483642a6dc36fd4d55f883f1487c3211069c1f62fc57fa4850080000006a4730440220782f395dd1ee76f83395273eb214bda9ee21004e76be88951b863982287c850e0220200434957f72e0bae2ca10a50c8cf97ff279d51e13880b820d8e923cd99296ae0121035010f2bda2a5996aaf6edac6cd89e987730f8ad1b7620eac29a72795d513a623ffffffff01c041c81700000000160014f3a3640c6de0a3a5c14b72164e98f96ca79d65fa024730440220407fc97f300771f0499b7eb8f2cdd8705ce7a615fc893a1f2c25e90a322ff84202201f7c69db3033aebf7febb0809df36a750d065db56b9fb0f1e4492500ee7c1f440121028ec9808ed77394c3ff6e94c1c0f83fa0baaec915eff20566c7e8b09758f08beb0247304402207434a180083d825e5f034279832541fa3a140894a7950119d04ee4b64310c1e302206218a7134dc2e19d88b1fb931cd1577d988f1aa004690c17193a63bcd4a84dd4012321034f208b00923a0c6c5a91d735206b0a63e746d63a8b232b6aa9bce9832f3f4da5ac02473044022020a694a50573f2261baf3593afcc10ac9e6e74fb4b36a8db8273ed976195c0a302205e3c5c64ed8ab19aa1a1934195c89f4424b34d99f9fa62c781654b55a7c58427012321020ae496cfca86e51c88c45438b70697ade7969429d79ceefde7a824fc585d0f7fac02473044022074645c573a73d6e9eceac7f2b61b0d411df41f6691ef612f5f2d25209b6cc4c10220114fab0a3dfd58a7e7bed5aa828844e8cfd521450b2428330cd2507227b22b91012103693192b81b9cbefdd05321eb4c460264a1081d64396a76332f3db3b121bdd433040047304402203c168f021ee2bc3aeea53b2b289b2add195162fa090d3859e1f4ada6551dbc5302204badf65a27f22c22023eb0bf46e39f190fd3c794c98881ee1f9d44e5a1a5ca140147304402200c112ae3a3758c252af58c48b3ff756b93e43ac3e7a388651c7c1f8054209d7c022063e679bdef2b69b14c570fd6315096e372ea888b4149682cdbb37a8d12ada90f0169522102f8310173f04a117fa3c2ff96593908d1ec72c3e9da848099fd91daf118c48af3210346c02205f439c57cdf441a8cb55f4c339521c8d037ad804a15ed68e3391eb87b21034cf24693086f890b5d20b0c3f3152839ae00461ad1b29771e459a1676c47d57253ae040047304402201fc9a9292a2a92db4e99081fca2bf4c7062715c48b05c487b844a49e677e14b102207153528156c535e1500e1980593ba37ac649f8b2c1f8693a0a47beaf2dd4c67a01473044022026b040772ec6a96f2e29f457c89e9520c22f47e368945e0ad5f1af0435ea8079022028b4d895852c3efb4ad7b76133ccdee0f425431781e8b2f098014ade4c7369fc01695221021cf20f5e699d4b29397b2485f322f21aeb93264ab8532bc61127978418d49ed12102c06f94c7526991f26901f227e3ab7b398749bc0c307b0a4891ddc25ac88af51c210378fc18eb885a72bc6c3e3ebec5eade1bce0ef68a4863b2b7ceba2d96e511972f53ae00000000000000"+}
+ data/key_io_invalid.json view
@@ -0,0 +1,182 @@+[+    [+        ""+    ],+    [+        "x"+    ],+    [+        "37qgekLpCCHrQuSjvX3fs496FWTGsHFHizjJAs6NPcR47aefnnCWECAhHV6E3g4YN7u7Yuwod5Y"+    ],+    [+        "dzb7VV1Ui55BARxv7ATxAtCUeJsANKovDGWFVgpTbhq9gvPqP3yv"+    ],+    [+        "MuNu7ZAEDFiHthiunm7dPjwKqrVNCM3mAz6rP9zFveQu14YA8CxExSJTHcVP9DErn6u84E6Ej7S"+    ],+    [+        "rPpQpYknyNQ5AEHuY6H8ijJJrYc2nDKKk9jjmKEXsWzyAQcFGpDLU2Zvsmoi8JLR7hAwoy3RQWf"+    ],+    [+        "4Uc3FmN6NQ6zLBK5QQBXRBUREaaHwCZYsGCueHauuDmJpZKn6jkEskMB2Zi2CNgtb5r6epWEFfUJq"+    ],+    [+        "7aQgR5DFQ25vyXmqZAWmnVCjL3PkBcdVkBUpjrjMTcghHx3E8wb"+    ],+    [+        "17QpPprjeg69fW1DV8DcYYCKvWjYhXvWkov6MJ1iTTvMFj6weAqW7wybZeH57WTNxXVCRH4veVs"+    ],+    [+        "KxuACDviz8Xvpn1xAh9MfopySZNuyajYMZWz16Dv2mHHryznWUp3"+    ],+    [+        "7nK3GSmqdXJQtdohvGfJ7KsSmn3TmGqExug49583bDAL91pVSGq5xS9SHoAYL3Wv3ijKTit65th"+    ],+    [+        "cTivdBmq7bay3RFGEBBuNfMh2P1pDCgRYN2Wbxmgwr4ki3jNUL2va"+    ],+    [+        "gjMV4vjNjyMrna4fsAr8bWxAbwtmMUBXJS3zL4NJt5qjozpbQLmAfK1uA3CquSqsZQMpoD1g2nk"+    ],+    [+        "emXm1naBMoVzPjbk7xpeTVMFy4oDEe25UmoyGgKEB1gGWsK8kRGs"+    ],+    [+        "7VThQnNRj1o3Zyvc7XHPRrjDf8j2oivPTeDXnRPYWeYGE4pXeRJDZgf28ppti5hsHWXS2GSobdqyo"+    ],+    [+        "1G9u6oCVCPh2o8m3t55ACiYvG1y5BHewUkDSdiQarDcYXXhFHYdzMdYfUAhfxn5vNZBwpgUNpso"+    ],+    [+        "31QQ7ZMLkScDiB4VyZjuptr7AEc9j1SjstF7pRoLhHTGkW4Q2y9XELobQmhhWxeRvqcukGd1XCq"+    ],+    [+        "DHqKSnpxa8ZdQyH8keAhvLTrfkyBMQxqngcQA5N8LQ9KVt25kmGN"+    ],+    [+        "2LUHcJPbwLCy9GLH1qXmfmAwvadWw4bp4PCpDfduLqV17s6iDcy1imUwhQJhAoNoN1XNmweiJP4i"+    ],+    [+        "7USRzBXAnmck8fX9HmW7RAb4qt92VFX6soCnts9s74wxm4gguVhtG5of8fZGbNPJA83irHVY6bCos"+    ],+    [+        "1DGezo7BfVebZxAbNT3XGujdeHyNNBF3vnficYoTSp4PfK2QaML9bHzAMxke3wdKdHYWmsMTJVu"+    ],+    [+        "2D12DqDZKwCxxkzs1ZATJWvgJGhQ4cFi3WrizQ5zLAyhN5HxuAJ1yMYaJp8GuYsTLLxTAz6otCfb"+    ],+    [+        "8AFJzuTujXjw1Z6M3fWhQ1ujDW7zsV4ePeVjVo7D1egERqSW9nZ"+    ],+    [+        "163Q17qLbTCue8YY3AvjpUhotuaodLm2uqMhpYirsKjVqnxJRWTEoywMVY3NbBAHuhAJ2cF9GAZ"+    ],+    [+        "2MnmgiRH4eGLyLc9eAqStzk7dFgBjFtUCtu"+    ],+    [+        "461QQ2sYWxU7H2PV4oBwJGNch8XVTYYbZxU"+    ],+    [+        "2UCtv53VttmQYkVU4VMtXB31REvQg4ABzs41AEKZ8UcB7DAfVzdkV9JDErwGwyj5AUHLkmgZeobs"+    ],+    [+        "cSNjAsnhgtiFMi6MtfvgscMB2Cbhn2v1FUYfviJ1CdjfidvmeW6mn"+    ],+    [+        "gmsow2Y6EWAFDFE1CE4Hd3Tpu2BvfmBfG1SXsuRARbnt1WjkZnFh1qGTiptWWbjsq2Q6qvpgJVj"+    ],+    [+        "nksUKSkzS76v8EsSgozXGMoQFiCoCHzCVajFKAXqzK5on9ZJYVHMD5CKwgmX3S3c7M1U3xabUny"+    ],+    [+        "L3favK1UzFGgdzYBF2oBT5tbayCo4vtVBLJhg2iYuMeePxWG8SQc"+    ],+    [+        "7VxLxGGtYT6N99GdEfi6xz56xdQ8nP2dG1CavuXx7Rf2PrvNMTBNevjkfgs9JmkcGm6EXpj8ipyPZ"+    ],+    [+        "2mbZwFXF6cxShaCo2czTRB62WTx9LxhTtpP"+    ],+    [+        "dB7cwYdcPSgiyAwKWL3JwCVwSk6epU2txw"+    ],+    [+        "HPhFUhUAh8ZQQisH8QQWafAxtQYju3SFTX"+    ],+    [+        "4ctAH6AkHzq5ioiM1m9T3E2hiYEev5mTsB"+    ],+    [+        "Hn1uFi4dNexWrqARpjMqgT6cX1UsNPuV3cHdGg9ExyXw8HTKadbktRDtdeVmY3M1BxJStiL4vjJ"+    ],+    [+        "Sq3fDbvutABmnAHHExJDgPLQn44KnNC7UsXuT7KZecpaYDMU9Txs"+    ],+    [+        "6TqWyrqdgUEYDQU1aChMuFMMEimHX44qHFzCUgGfqxGgZNMUVWJ"+    ],+    [+        "giqJo7oWqFxNKWyrgcBxAVHXnjJ1t6cGoEffce5Y1y7u649Noj5wJ4mmiUAKEVVrYAGg2KPB3Y4"+    ],+    [+        "cNzHY5e8vcmM3QVJUcjCyiKMYfeYvyueq5qCMV3kqcySoLyGLYUK"+    ],+    [+        "37uTe568EYc9WLoHEd9jXEvUiWbq5LFLscNyqvAzLU5vBArUJA6eydkLmnMwJDjkL5kXc2VK7ig"+    ],+    [+        "EsYbG4tWWWY45G31nox838qNdzksbPySWc"+    ],+    [+        "nbuzhfwMoNzA3PaFnyLcRxE9bTJPDkjZ6Rf6Y6o2ckXZfzZzXBT"+    ],+    [+        "cQN9PoxZeCWK1x56xnz6QYAsvR11XAce3Ehp3gMUdfSQ53Y2mPzx"+    ],+    [+        "1Gm3N3rkef6iMbx4voBzaxtXcmmiMTqZPhcuAepRzYUJQW4qRpEnHvMojzof42hjFRf8PE2jPde"+    ],+    [+        "2TAq2tuN6x6m233bpT7yqdYQPELdTDJn1eU"+    ],+    [+        "ntEtnnGhqPii4joABvBtSEJG6BxjT2tUZqE8PcVYgk3RHpgxgHDCQxNbLJf7ardf1dDk2oCQ7Cf"+    ],+    [+        "Ky1YjoZNgQ196HJV3HpdkecfhRBmRZdMJk89Hi5KGfpfPwS2bUbfd"+    ],+    [+        "2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED"+    ],+    [+        "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty"+    ],+    [+        "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"+    ],+    [+        "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2"+    ],+    [+        "bc1rw5uspcuh"+    ],+    [+        "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90"+    ],+    [+        "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P"+    ],+    [+        "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7"+    ],+    [+        "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du"+    ],+    [+        "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv"+    ],+    [+        "bc1gmk9yu"+    ]+]
+ data/key_io_valid.json view
@@ -0,0 +1,536 @@+[+    [+        "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",+        "76a91465a16059864a2fdbc7c99a4723a8395bc6f188eb88ac",+        {+            "isPrivkey": false,+            "chain": "main"+        }+    ],+    [+        "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou",+        "a91474f209f6ea907e2ea48f74fae05782ae8a66525787",+        {+            "isPrivkey": false,+            "chain": "main"+        }+    ],+    [+        "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs",+        "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac",+        {+            "isPrivkey": false,+            "chain": "test"+        }+    ],+    [+        "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs",+        "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac",+        {+            "isPrivkey": false,+            "chain": "regtest"+        }+    ],+    [+        "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br",+        "a9146349a418fc4578d10a372b54b45c280cc8c4382f87",+        {+            "isPrivkey": false,+            "chain": "test"+        }+    ],+    [+        "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr",+        "eddbdc1168f1daeadbd3e44c1e3f8f5a284c2029f78ad26af98583a499de5b19",+        {+            "isCompressed": false,+            "isPrivkey": true,+            "chain": "main"+        }+    ],+    [+        "Kz6UJmQACJmLtaQj5A3JAge4kVTNQ8gbvXuwbmCj7bsaabudb3RD",+        "55c9bccb9ed68446d1b75273bbce89d7fe013a8acd1625514420fb2aca1a21c4",+        {+            "isCompressed": true,+            "isPrivkey": true,+            "chain": "main"+        }+    ],+    [+        "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko",+        "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2",+        {+            "isCompressed": false,+            "isPrivkey": true,+            "chain": "test"+        }+    ],+    [+        "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko",+        "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2",+        {+            "isCompressed": false,+            "isPrivkey": true,+            "chain": "regtest"+        }+    ],+    [+        "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH",+        "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3",+        {+            "isCompressed": true,+            "isPrivkey": true,+            "chain": "test"+        }+    ],+    [+        "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH",+        "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3",+        {+            "isCompressed": true,+            "isPrivkey": true,+            "chain": "regtest"+        }+    ],+    [+        "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ",+        "76a9146d23156cbbdcc82a5a47eee4c2c7c583c18b6bf488ac",+        {+            "isPrivkey": false,+            "chain": "main"+        }+    ],+    [+        "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy",+        "a914fcc5460dd6e2487c7d75b1963625da0e8f4c597587",+        {+            "isPrivkey": false,+            "chain": "main"+        }+    ],+    [+        "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ",+        "76a914f1d470f9b02370fdec2e6b708b08ac431bf7a5f788ac",+        {+            "isPrivkey": false,+            "chain": "test"+        }+    ],+    [+        "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n",+        "a914c579342c2c4c9220205e2cdc285617040c924a0a87",+        {+            "isPrivkey": false,+            "chain": "test"+        }+    ],+    [+        "5K494XZwps2bGyeL71pWid4noiSNA2cfCibrvRWqcHSptoFn7rc",+        "a326b95ebae30164217d7a7f57d72ab2b54e3be64928a19da0210b9568d4015e",+        {+            "isCompressed": false,+            "isPrivkey": true,+            "chain": "main"+        }+    ],+    [+        "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi",+        "7d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb4",+        {+            "isCompressed": true,+            "isPrivkey": true,+            "chain": "main"+        }+    ],+    [+        "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj",+        "d6bca256b5abc5602ec2e1c121a08b0da2556587430bcf7e1898af2224885203",+        {+            "isCompressed": false,+            "isPrivkey": true,+            "chain": "test"+        }+    ],+    [+        "cTDVKtMGVYWTHCb1AFjmVbEbWjvKpKqKgMaR3QJxToMSQAhmCeTN",+        "a81ca4e8f90181ec4b61b6a7eb998af17b2cb04de8a03b504b9e34c4c61db7d9",+        {+            "isCompressed": true,+            "isPrivkey": true,+            "chain": "test"+        }+    ],+    [+        "1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv",+        "76a9147987ccaa53d02c8873487ef919677cd3db7a691288ac",+        {+            "isPrivkey": false,+            "chain": "main"+        }+    ],+    [+        "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks",+        "a91463bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb87",+        {+            "isPrivkey": false,+            "chain": "main"+        }+    ],+    [+        "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk",+        "76a914ef66444b5b17f14e8fae6e7e19b045a78c54fd7988ac",+        {+            "isPrivkey": false,+            "chain": "test"+        }+    ],+    [+        "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o",+        "a914c3e55fceceaa4391ed2a9677f4a4d34eacd021a087",+        {+            "isPrivkey": false,+            "chain": "test"+        }+    ],+    [+        "5KaBW9vNtWNhc3ZEDyNCiXLPdVPHCikRxSBWwV9NrpLLa4LsXi9",+        "e75d936d56377f432f404aabb406601f892fd49da90eb6ac558a733c93b47252",+        {+            "isCompressed": false,+            "isPrivkey": true,+            "chain": "main"+        }+    ],+    [+        "L1axzbSyynNYA8mCAhzxkipKkfHtAXYF4YQnhSKcLV8YXA874fgT",+        "8248bd0375f2f75d7e274ae544fb920f51784480866b102384190b1addfbaa5c",+        {+            "isCompressed": true,+            "isPrivkey": true,+            "chain": "main"+        }+    ],+    [+        "927CnUkUbasYtDwYwVn2j8GdTuACNnKkjZ1rpZd2yBB1CLcnXpo",+        "44c4f6a096eac5238291a94cc24c01e3b19b8d8cef72874a079e00a242237a52",+        {+            "isCompressed": false,+            "isPrivkey": true,+            "chain": "test"+        }+    ],+    [+        "cUcfCMRjiQf85YMzzQEk9d1s5A4K7xL5SmBCLrezqXFuTVefyhY7",+        "d1de707020a9059d6d3abaf85e17967c6555151143db13dbb06db78df0f15c69",+        {+            "isCompressed": true,+            "isPrivkey": true,+            "chain": "test"+        }+    ],+    [+        "1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu",+        "76a914adc1cc2081a27206fae25792f28bbc55b831549d88ac",+        {+            "isPrivkey": false,+            "chain": "main"+        }+    ],+    [+        "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk",+        "a914188f91a931947eddd7432d6e614387e32b24470987",+        {+            "isPrivkey": false,+            "chain": "main"+        }+    ],+    [+        "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H",+        "76a9141694f5bc1a7295b600f40018a618a6ea48eeb49888ac",+        {+            "isPrivkey": false,+            "chain": "test"+        }+    ],+    [+        "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN",+        "a9143b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f387",+        {+            "isPrivkey": false,+            "chain": "test"+        }+    ],+    [+        "5HtH6GdcwCJA4ggWEL1B3jzBBUB8HPiBi9SBc5h9i4Wk4PSeApR",+        "091035445ef105fa1bb125eccfb1882f3fe69592265956ade751fd095033d8d0",+        {+            "isCompressed": false,+            "isPrivkey": true,+            "chain": "main"+        }+    ],+    [+        "L2xSYmMeVo3Zek3ZTsv9xUrXVAmrWxJ8Ua4cw8pkfbQhcEFhkXT8",+        "ab2b4bcdfc91d34dee0ae2a8c6b6668dadaeb3a88b9859743156f462325187af",+        {+            "isCompressed": true,+            "isPrivkey": true,+            "chain": "main"+        }+    ],+    [+        "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq",+        "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856",+        {+            "isCompressed": false,+            "isPrivkey": true,+            "chain": "test"+        }+    ],+    [+        "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq",+        "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856",+        {+            "isCompressed": false,+            "isPrivkey": true,+            "chain": "regtest"+        }+    ],+    [+        "cVM65tdYu1YK37tNoAyGoJTR13VBYFva1vg9FLuPAsJijGvG6NEA",+        "e7b230133f1b5489843260236b06edca25f66adb1be455fbd38d4010d48faeef",+        {+            "isCompressed": true,+            "isPrivkey": true,+            "chain": "test"+        }+    ],+    [+        "1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4",+        "76a914c4c1b72491ede1eedaca00618407ee0b772cad0d88ac",+        {+            "isPrivkey": false,+            "chain": "main"+        }+    ],+    [+        "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y",+        "a914f6fe69bcb548a829cce4c57bf6fff8af3a5981f987",+        {+            "isPrivkey": false,+            "chain": "main"+        }+    ],+    [+        "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6",+        "76a914261f83568a098a8638844bd7aeca039d5f2352c088ac",+        {+            "isPrivkey": false,+            "chain": "test"+        }+    ],+    [+        "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda",+        "a914e930e1834a4d234702773951d627cce82fbb5d2e87",+        {+            "isPrivkey": false,+            "chain": "test"+        }+    ],+    [+        "5KQmDryMNDcisTzRp3zEq9e4awRmJrEVU1j5vFRTKpRNYPqYrMg",+        "d1fab7ab7385ad26872237f1eb9789aa25cc986bacc695e07ac571d6cdac8bc0",+        {+            "isCompressed": false,+            "isPrivkey": true,+            "chain": "main"+        }+    ],+    [+        "L39Fy7AC2Hhj95gh3Yb2AU5YHh1mQSAHgpNixvm27poizcJyLtUi",+        "b0bbede33ef254e8376aceb1510253fc3550efd0fcf84dcd0c9998b288f166b3",+        {+            "isCompressed": true,+            "isPrivkey": true,+            "chain": "main"+        }+    ],+    [+        "91cTVUcgydqyZLgaANpf1fvL55FH53QMm4BsnCADVNYuWuqdVys",+        "037f4192c630f399d9271e26c575269b1d15be553ea1a7217f0cb8513cef41cb",+        {+            "isCompressed": false,+            "isPrivkey": true,+            "chain": "test"+        }+    ],+    [+        "cQspfSzsgLeiJGB2u8vrAiWpCU4MxUT6JseWo2SjXy4Qbzn2fwDw",+        "6251e205e8ad508bab5596bee086ef16cd4b239e0cc0c5d7c4e6035441e7d5de",+        {+            "isCompressed": true,+            "isPrivkey": true,+            "chain": "test"+        }+    ],+    [+        "19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r",+        "76a9145eadaf9bb7121f0f192561a5a62f5e5f5421029288ac",+        {+            "isPrivkey": false,+            "chain": "main"+        }+    ],+    [+        "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3",+        "a9143f210e7277c899c3a155cc1c90f4106cbddeec6e87",+        {+            "isPrivkey": false,+            "chain": "main"+        }+    ],+    [+        "myoqcgYiehufrsnnkqdqbp69dddVDMopJu",+        "76a914c8a3c2a09a298592c3e180f02487cd91ba3400b588ac",+        {+            "isPrivkey": false,+            "chain": "test"+        }+    ],+    [+        "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C",+        "a91499b31df7c9068d1481b596578ddbb4d3bd90baeb87",+        {+            "isPrivkey": false,+            "chain": "test"+        }+    ],+    [+        "5KL6zEaMtPRXZKo1bbMq7JDjjo1bJuQcsgL33je3oY8uSJCR5b4",+        "c7666842503db6dc6ea061f092cfb9c388448629a6fe868d068c42a488b478ae",+        {+            "isCompressed": false,+            "isPrivkey": true,+            "chain": "main"+        }+    ],+    [+        "KwV9KAfwbwt51veZWNscRTeZs9CKpojyu1MsPnaKTF5kz69H1UN2",+        "07f0803fc5399e773555ab1e8939907e9badacc17ca129e67a2f5f2ff84351dd",+        {+            "isCompressed": true,+            "isPrivkey": true,+            "chain": "main"+        }+    ],+    [+        "93N87D6uxSBzwXvpokpzg8FFmfQPmvX4xHoWQe3pLdYpbiwT5YV",+        "ea577acfb5d1d14d3b7b195c321566f12f87d2b77ea3a53f68df7ebf8604a801",+        {+            "isCompressed": false,+            "isPrivkey": true,+            "chain": "test"+        }+    ],+    [+        "cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h",+        "0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c",+        {+            "isCompressed": true,+            "isPrivkey": true,+            "chain": "test"+        }+    ],+    [+        "13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE",+        "76a9141ed467017f043e91ed4c44b4e8dd674db211c4e688ac",+        {+            "isPrivkey": false,+            "chain": "main"+        }+    ],+    [+        "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G",+        "a9145ece0cadddc415b1980f001785947120acdb36fc87",+        {+            "isPrivkey": false,+            "chain": "main"+        }+    ],+    [+        "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",+        "0014751e76e8199196d454941c45d1b3a323f1433bd6",+        {+            "isPrivkey": false,+            "chain": "main",+            "tryCaseFlip": true+        }+    ],+    [+        "bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080",+        "0014751e76e8199196d454941c45d1b3a323f1433bd6",+        {+            "isPrivkey": false,+            "chain": "regtest",+            "tryCaseFlip": true+        }+    ],+    [+        "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",+        "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262",+        {+            "isPrivkey": false,+            "chain": "test",+            "tryCaseFlip": true+        }+    ],+    [+        "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx",+        "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6",+        {+            "isPrivkey": false,+            "chain": "main",+            "tryCaseFlip": true,+            "disabled": true+        }+    ],+    [+        "bc1sw50qa3jx3s",+        "6002751e",+        {+            "isPrivkey": false,+            "chain": "main",+            "tryCaseFlip": true,+            "disabled": true+        }+    ],+    [+        "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj",+        "5210751e76e8199196d454941c45d1b3a323",+        {+            "isPrivkey": false,+            "chain": "main",+            "tryCaseFlip": true,+            "disabled": true+        }+    ],+    [+        "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy",+        "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433",+        {+            "isPrivkey": false,+            "chain": "test",+            "tryCaseFlip": true+        }+    ],+    [+        "bcrt1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvseswlauz7",+        "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433",+        {+            "isPrivkey": false,+            "chain": "regtest",+            "tryCaseFlip": true+        }+    ]+]
+ data/rfc6979DERcash.json view
@@ -0,0 +1,102 @@+[+    [+        "0000000000000000000000000000000000000000000000000000000000000001",+        "Absence makes the heart grow fonder.",+        "304402206c0db71422a66840f238b55635dc87d013a77caebb2057713e14928abe0d0ce602207f687dedcd60e4a8feb3ef31450ad8e42730d909dd19b5237a5eaa4bf50c0741"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000002",+        "Actions speak louder than words.",+        "3045022100912705043b888d052c35a2d9736dde7f29c453cb6b2c355fa1b1a1328a1d23170220021ede2707be213c7d489720af88c43f6eb86c47835e7d6ff61cbe91ebec288a"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000003",+        "All for one and one for all.",+        "3044022055bffc0d939ae7f8209dea0ae77f5b4905818201a4c2ca7f06a302ea56258186022079cfc2f2b5ce08e0a8f3f1082366f509783bceb2c4a05819a715724d68bbe7c0"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000004",+        "All's fair in love and war.",+        "3045022100e1ea024069bd02f1332fafdbd12f50103e0438cbce1e32517c9932c1e043c902022078f4408e6851f25e8fdc91f82a920cf4ce400554f236d810c499886f97c9500b"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000005",+        "All work and no play makes Jack a dull boy.",+        "3045022100824b35db88c1600747a3d9fe3d40d1400c6b8c3527c91d3bdb22b317bdf4dfd60220793025386e2de4289b3f0e135121f20088e1d0e0cb5bbf366561dffc9d17c0d4"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000006",+        "All's well that ends well.",+        "3045022100f3a3f37b80880a5b1ac2a3fcde9a86aae7036e8576f1ce28ab2849155237de63022027ef8c5e61c42a86fe13953d57aea273f797828f22df686bbdbc5601a7cdf49a"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000007",+        "An apple a day keeps the doctor away.",+        "3045022100e8f392a1657ec6fdf747ea8ff5a6dbeec7e932975c0c2f9430b9f6b7e3120018022064dd010aba1522d1ae16219e425225e9508c34a00d3e5d7327336391fc3e0cac"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000008",+        "An apple never falls far from the tree.",+        "3045022100ba82ea8d785a951ce33dcf65379abfd1e3d1affa4557870c1d931bad119e3fde022070d86f0cdb023fd40c365d9caab1351900371166a72f55315e661486ed769753"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000009",+        "An ounce of prevention is worth a pound of cure.",+        "30450221009d898ae395ed916e3b58f3f9e08019c65be8bcdc1634581891c30f7873de2570022048c7c244ceef3c3a6a6cd34119dc07ad54b8f8d50273629ac20fadd580241a92"+    ],+    [+        "000000000000000000000000000000000000000000000000000000000000000a",+        "Appearances can be deceiving.",+        "304402206a75d44757c6ae076d8dce8ea79dfde47765f02659e0ccb6e9e3f675c6d0013602200345ff627949d08d6d02ba1d43d07e89fd74c54d250f212b6d9e1e227887cce2"+    ],+    [+        "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a",+        "Absence makes the heart grow fonder.",+        "3044022001e95d38c5d65344800d9d87de5c2d4319674adf6d248bb59113d42d18bdb97502200d5afe847651e64a358e65ba16cca9d1e02eac371cfa8e1206cd3dceb438f9d1"+    ],+    [+        "dbc1b4c900ffe48d575b5da5c638040125f65db0fe3e24494b76ea986457d986",+        "Actions speak louder than words.",+        "30440220771a29e95d6e9e4c1b05b3cb7853ebc1057a9f68133fcc34190c4ba237e40b99022046067aff0918c18a3b3822c937921905ef22bf10c9f59d806ccfb23f7fcaa1ac"+    ],+    [+        "084fed08b978af4d7d196a7446a86b58009e636b611db16211b65a9aadff29c5",+        "All for one and one for all.",+        "3045022100822eb70daf001ed5b8a9ccd9e2fa849918148bdd5d2cdf3fb5797be0fa82fec0022060d43b32e6c86804d351dfe863135d8391f10442552dd59b75f2cbc3894f7277"+    ],+    [+        "e52d9c508c502347344d8c07ad91cbd6068afc75ff6292f062a09ca381c89e71",+        "All's fair in love and war.",+        "30440220264b1220e15eb1256bdac36512687aa37e059bb3b177bbf42f19a75cb95a9710022040928fcc170203596bb449f4d745b49f75b76d013a5c0d70af3f6eefa9af94d6"+    ],+    [+        "e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db",+        "All work and no play makes Jack a dull boy.",+        "304402203b96a2ea837df2aba4e94be5167ce9bcb1f00c2a6c6698f4ff340b7a9379dd5f0220051222212a9a4d1886acfac0594ae8f98db354448c8aa626d34ee7d4abb83d4c"+    ],+    [+        "67586e98fad27da0b9968bc039a1ef34c939b9b8e523a8bef89d478608c5ecf6",+        "All's well that ends well.",+        "30450221009d6297db8a9446e376d45cc816ea9172d6a6db715680928dc0ead4f2f0fbbdfc02207df1a6c9e7a23c9baee9b2833d8dd18ea6ed33f7f7d653a1a29e75ead07ce143"+    ],+    [+        "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879",+        "An apple a day keeps the doctor away.",+        "304402202e58d297a0d452ae6afc3cc8f3bfc4ff1a10c76c3f71ff1062564f196d4038f3022073f77ea431bd2b95a0e226eccb2fd4680b71a2ee6ba46a6b25d54db5a928f4ce"+    ],+    [+        "beead77994cf573341ec17b58bbf7eb34d2711c993c1d976b128b3188dc1829a",+        "An apple never falls far from the tree.",+        "304402204997ddbdb33574552d938e84378f29c3bb24a32e7a32e467315dd841fa278a040220198c27facdf3ac10b59cdc97ab269df337268d5a61527f41966f0ff876ac00df"+    ],+    [+        "2b4c342f5433ebe591a1da77e013d1b72475562d48578dca8b84bac6651c3cb9",+        "An ounce of prevention is worth a pound of cure.",+        "304402202803b367eacdff2a0ee8727108f43efc864519d0fc972e42a5e4f3f2221b6f880220632003636d7293672003650babc3a589e35be93b98dabf20dead8e70085fa8a8"+    ],+    [+        "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b",+        "Appearances can be deceiving.",+        "30440220033aef6b3a2cea67a19d91130ee2736652588a21fdca9520ed778ca2d30e343d02206f0be9db4ebfc474470c4d164a67479e232f20e57f8659bc5b404520b665765d"+    ]+]
+ data/rfc6979DERcore.json view
@@ -0,0 +1,102 @@+[+    [+        "0000000000000000000000000000000000000000000000000000000000000001",+        "Absence makes the heart grow fonder.",+        "3045022100afff580595971b8c1700e77069d73602aef4c2a760dbd697881423dfff845de80220579adb6a1ac03acde461b5821a049ebd39a8a8ebf2506b841b15c27342d2e342"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000002",+        "Actions speak louder than words.",+        "304502210085f28bbc90975b1907a51cbfe7bf0dc1ac74ade49318ee97498dbbde3894a31c0220241d24da8d263e7af7ff49bca6a7a850f0e087faf6fef44f85851b0283c3f026"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000003",+        "All for one and one for all.",+        "30440220502c6ac38e1c68ce68f044f5ab680f2880a6c1cd34e70f2b4f945c6fd30abd03022018ef5c6c3392b9d67ad5109c85476a0e159425d7f6ace2cebeaa65f02f210bbb"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000004",+        "All's fair in love and war.",+        "30440220452d4ab234891cf6e5432cd5472bdca1cfc6fb28563333885f068da02ee216d8022056c368d16a64d29cff92f17203d926e113064527af0480d3bcc1d3fadfde9364"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000005",+        "All work and no play makes Jack a dull boy.",+        "3045022100995025b4880eeb1ecedba945fe8c9b2ddf2b07dbc293c2586c079d7b663ef38a022022fb54ab95014616d014277e05c97a7ed9e22596a0420bbd2d749ca9a2f876fe"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000006",+        "All's well that ends well.",+        "3045022100a9c1593fa6459777b2eba6d7e2a206e3bb119e85b2163973cf28ffaf24ec381c02202f166f13230b3853b928efb649d30375ec6a4b1a64a8d56fbcc0a9d86a0943e9"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000007",+        "An apple a day keeps the doctor away.",+        "304402202fc9c8b749621241c33fd51b57fc5140c1d7fc1594f91b073953e79da2f5e8f60220345e4ea7693b5069c0251771ea476cbe236586ed24b90aeeea7b7c2814edf477"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000008",+        "An apple never falls far from the tree.",+        "3044022052b6e2c49a6f6adbe52fb6bbe744caa3f49364085db118eab8670bc766be160302207d96a42866637ca3d4caf36e597a460eb305adac0220b027410c821a7191a1c4"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000009",+        "An ounce of prevention is worth a pound of cure.",+        "3045022100be53e7c00788e4417083d7511800f18c7c6f5f259de39bc6f8b1bebcd5056bd002201f389e13cfe7d1dbd8d2d1bff18138219f57de166673762009686a28fbc44df6"+    ],+    [+        "000000000000000000000000000000000000000000000000000000000000000a",+        "Appearances can be deceiving.",+        "304402202f2413a1673f642c30ea2e23fcae45776bc77a94f96920aea3c14303b1469428022053ac3e8ea0a488e9159d56e429a51f207bf04e462f8d4ba2c69b1b1635f30217"+    ],+    [+        "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a",+        "Absence makes the heart grow fonder.",+        "3045022100996d79fba54b24e9394fc5fab6bf94d173f3752645075de6e32574fe08625f770220345e638b373dcb0ce0c09e5799695ef64ffc5e01dd8367b9a205ce25f28870f6"+    ],+    [+        "dbc1b4c900ffe48d575b5da5c638040125f65db0fe3e24494b76ea986457d986",+        "Actions speak louder than words.",+        "304502210088164430985a4437471417c2386faa536e1fe8ec91bd0f1f642bc22a776891530220090dc83d6e3b54a1a54dc2e79c693144179a512d9c9e686a6c25e7641a2101a8"+    ],+    [+        "084fed08b978af4d7d196a7446a86b58009e636b611db16211b65a9aadff29c5",+        "All for one and one for all.",+        "30450221009f1073c9c09b664498d4b216983330b01c29a0fb55dd61aa145b4ebd0579905502204592fb6626f672d4f3ad4bb2d0a1ed6c2a161cc35c6bb77e6f0fd3b63feab36f"+    ],+    [+        "e52d9c508c502347344d8c07ad91cbd6068afc75ff6292f062a09ca381c89e71",+        "All's fair in love and war.",+        "304502210080eabf24117b492635043886e7229b9705b970cbb6828c4e03a39dae7ac34bda022070e8a32ca1df82add53facbd58b4f2d3984d0a17b6b13c44460238d9ff74e41f"+    ],+    [+        "e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db",+        "All work and no play makes Jack a dull boy.",+        "3045022100a43ff5edea7ea0b9716d4359574e990a6859cdaeb9d7d6b4964afd40be11bd35022067f9d82e22fc447a122997335525f117f37b141c3efa9f8c6d77b586753f962f"+    ],+    [+        "67586e98fad27da0b9968bc039a1ef34c939b9b8e523a8bef89d478608c5ecf6",+        "All's well that ends well.",+        "3044022053ce16251f4fae7eb87e2ab040a6f334e08687fb445566256cd217ece389e0440220576506a168cbc9ee0dd485d6c418961e7a0861b0f05d22a93401812978d0b215"+    ],+    [+        "ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879",+        "An apple a day keeps the doctor away.",+        "3045022100df8744cc06a304b041e88149acfd84a68d8f4a2a4047056644e1ec8357e11ebe02204ba2d5499a26d072c797a86c7851533f287ceb8b818cae2c5d4483c37c62750c"+    ],+    [+        "beead77994cf573341ec17b58bbf7eb34d2711c993c1d976b128b3188dc1829a",+        "An apple never falls far from the tree.",+        "3045022100878372d211ed0dbde1273ae3dd85aec577c08a06a55960f2e274f97cc9f2f38f02203f992caa66f472a64f6ccdd8076c0a12202c674155a6a61b8cd23c1ded08aab7"+    ],+    [+        "2b4c342f5433ebe591a1da77e013d1b72475562d48578dca8b84bac6651c3cb9",+        "An ounce of prevention is worth a pound of cure.",+        "3045022100d5cb4e148c0a29ce37f1542be416e8ef575da522666b19b541960d726c99662b022045c951c1ca938c90dad6c3eede7c5df67fcf0d14f90faf201e8d215f215c5c18"+    ],+    [+        "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b",+        "Appearances can be deceiving.",+        "304402203e2f0118062306e2239c873828a7275dd35545a143797e224148c5bbbd59dd08022073a8c9e17be75c66362913b5e05d81fd619b434edda766fae6c352e86987809d"+    ]+]
+ data/rfc6979cash.json view
@@ -0,0 +1,62 @@+[+    [+        "0000000000000000000000000000000000000000000000000000000000000001",+        "Satoshi Nakamoto",+        "cd7a225c6661e35a85f309a66ba9fbcd134487b80ae1d8a3c58d85015ac1f7ed046b573d040c8324ecf796d6b86732fdfd8938888fd8a28309e476732475a743"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000001",+        "All those moments will be lost in time, like tears in rain. Time to die...",+        "fb073d886dda0444913a3f1fa32d5b2bc35d09420672b6e7e8e36c530422c98f114230b9362ba6985ccb0d87b86c752e597fa3ca46f1c0abe226a9496187588c"+    ],+    [+        "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",+        "Satoshi Nakamoto",+        "20d0306cd55957786c6dedae03c2dc85cea8030de04ec6ba027cffe8b1be37d15f46f9282a7117c4179f54d00c944b4ce04b51e118cfddcdc73d72d5b5923933"+    ],+    [+        "f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181",+        "Alan Turing",+        "b27049b4834f874e41a4b6c586ed6aa1d86dba5ef411bd2a149161e39287ab4247ad691e06358323d6bbb16589ac186c63393c694bf75914c4a29ee56675c740"+    ],+    [+        "e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2",+        "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!",+        "08438a0a59cb5c1f3de6a0c3c4852282472b9286ff6427f65dc8af31b6bdd41b300ad6ef64cf7d2f6ce7109d134d1c542c61430d4d3d1e404f4fd2634a0da0ad"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000001",+        "Everything should be made as simple as possible, but not simpler.",+        "53452b2fbb94b30051ab0e7075c1e613685079cc5244c36aa7b9148521a04b87268579ff46388ff7ceb7d5a691fcc995cd62d596603fffff6013eaaef3ec1f65"+    ],+    [+        "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",+        "Equations are more important to me, because politics is for the present, but an equation is something for eternity.",+        "fb904bd574f61510463e4e6593be29aa59de55f1dc31d3075920ac9ca7ef55997afa037dc9192877bf13ecf5458966bb92abd3328247b32425ab21d8b8a0d767"+    ],+    [+        "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",+        "Not only is the Universe stranger than we think, it is stranger than we can think.",+        "402f863e862bdfabfa9444e1370f4163a8f61a5f3e16b56ce639d3c373d0ad551aea2bdfb410f1f327737f7fa28646338876f4097b86c1204952e8f1733989d7"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000001",+        "How wonderful that we have met with a paradox. Now we have some hope of making progress.",+        "efdff52e87f77261f1aa07c7938e346547b712d3892a0ef6380cfff46198588b17efea735778bc04bae52cec6617b85537dc913f1423f21fd97860405ad78aee"+    ],+    [+        "69ec59eaa1f4f2e36b639716b7c30ca86d9a5375c7b38d8918bd9c0ebc80ba64",+        "Computer science is no more about computers than astronomy is about telescopes.",+        "d270a8604b11ec88a4e95cc1e4006f88452d5faf71dfd3f6d561d130a52903f93a6e9662a0fdf9f05ee5da5ba9fc437c97cf894a17844ccfb19d708942b72324"+    ],+    [+        "00000000000000000000000000007246174ab1e92e9149c6e446fe194d072637",+        "...if you aren't, at any given time, scandalized by code you wrote five or even three years ago, you're not learning anywhere near enough",+        "c0b51457714f4786e0f1d38219bfa124e06cbfff8b0c74c1a0ab11dff80b3a1b207aefdd3ac67158746b0ffad2be589a8f816296012b394302ab8a514aaeb120"+    ],+    [+        "000000000000000000000000000000000000000000056916d0f9b31dc9b637f3",+        "The question of whether computers can think is like the question of whether submarines can swim.",+        "fdcc51599f0b25a48f4b1f3701c153d840d49414b77d582b73bc0a93a3d2fe0c5770d55329857e5e1095545f911881447feccdde067faac1338c7f48a0355407"+    ]+]
+ data/rfc6979core.json view
@@ -0,0 +1,62 @@+[+    [+        "0000000000000000000000000000000000000000000000000000000000000001",+        "Satoshi Nakamoto",+        "934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d82442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000001",+        "All those moments will be lost in time, like tears in rain. Time to die...",+        "8600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21"+    ],+    [+        "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",+        "Satoshi Nakamoto",+        "fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d06b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5"+    ],+    [+        "f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181",+        "Alan Turing",+        "7063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c58dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea"+    ],+    [+        "e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2",+        "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!",+        "b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000001",+        "Everything should be made as simple as possible, but not simpler.",+        "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c96f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"+    ],+    [+        "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",+        "Equations are more important to me, because politics is for the present, but an equation is something for eternity.",+        "54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5"+    ],+    [+        "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",+        "Not only is the Universe stranger than we think, it is stranger than we can think.",+        "ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd06fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283"+    ],+    [+        "0000000000000000000000000000000000000000000000000000000000000001",+        "How wonderful that we have met with a paradox. Now we have some hope of making progress.",+        "c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d375afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3"+    ],+    [+        "69ec59eaa1f4f2e36b639716b7c30ca86d9a5375c7b38d8918bd9c0ebc80ba64",+        "Computer science is no more about computers than astronomy is about telescopes.",+        "7186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d0de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6"+    ],+    [+        "00000000000000000000000000007246174ab1e92e9149c6e446fe194d072637",+        "...if you aren't, at any given time, scandalized by code you wrote five or even three years ago, you're not learning anywhere near enough",+        "fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda4870e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37"+    ],+    [+        "000000000000000000000000000000000000000000056916d0f9b31dc9b637f3",+        "The question of whether computers can think is like the question of whether submarines can swim.",+        "cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf906ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef"+    ]+]
+ data/test_vectors_aserti3-2d_run01.txt view
@@ -0,0 +1,19 @@+## description: run1 - steady 600s blocks at POW limit target+##   anchor height: 1+##   anchor parent time: 0+##   anchor nBits: 0x1d00ffff+##   start height: 2+##   start time: 1200+##   iterations: 10+# iteration,height,time,target+1 2 1200 0x1d00ffff+2 3 1800 0x1d00ffff+3 4 2400 0x1d00ffff+4 5 3000 0x1d00ffff+5 6 3600 0x1d00ffff+6 7 4200 0x1d00ffff+7 8 4800 0x1d00ffff+8 9 5400 0x1d00ffff+9 10 6000 0x1d00ffff+10 11 6600 0x1d00ffff+
+ data/test_vectors_aserti3-2d_run02.txt view
@@ -0,0 +1,19 @@+## description: run2 - steady 600s blocks at arbitrary non-limit target 0x1a2b3c4d+##   anchor height: 1+##   anchor parent time: 0+##   anchor nBits: 0x1a2b3c4d+##   start height: 2+##   start time: 1200+##   iterations: 10+# iteration,height,time,target+1 2 1200 0x1a2b3c4d+2 3 1800 0x1a2b3c4d+3 4 2400 0x1a2b3c4d+4 5 3000 0x1a2b3c4d+5 6 3600 0x1a2b3c4d+6 7 4200 0x1a2b3c4d+7 8 4800 0x1a2b3c4d+8 9 5400 0x1a2b3c4d+9 10 6000 0x1a2b3c4d+10 11 6600 0x1a2b3c4d+
+ data/test_vectors_aserti3-2d_run03.txt view
@@ -0,0 +1,19 @@+## description: run3 - steady 600s blocks at minimum limit target 0x01010000+##   anchor height: 1+##   anchor parent time: 0+##   anchor nBits: 0x01010000+##   start height: 2+##   start time: 1200+##   iterations: 10+# iteration,height,time,target+1 2 1200 0x01010000+2 3 1800 0x01010000+3 4 2400 0x01010000+4 5 3000 0x01010000+5 6 3600 0x01010000+6 7 4200 0x01010000+7 8 4800 0x01010000+8 9 5400 0x01010000+9 10 6000 0x01010000+10 11 6600 0x01010000+
+ data/test_vectors_aserti3-2d_run04.txt view
@@ -0,0 +1,234 @@+## description: run4 - from minimum target, a series of halflife schedule jumps, doubling target at each block+##   anchor height: 1+##   anchor parent time: 0+##   anchor nBits: 0x01010000+##   start height: 2+##   start time: 174000+##   iterations: 225+# iteration,height,time,target+1 2 174000 0x01020000+2 3 347400 0x01040000+3 4 520800 0x01080000+4 5 694200 0x01100000+5 6 867600 0x01200000+6 7 1041000 0x01400000+7 8 1214400 0x02008000+8 9 1387800 0x02010000+9 10 1561200 0x02020000+10 11 1734600 0x02040000+11 12 1908000 0x02080000+12 13 2081400 0x02100000+13 14 2254800 0x02200000+14 15 2428200 0x02400000+15 16 2601600 0x03008000+16 17 2775000 0x03010000+17 18 2948400 0x03020000+18 19 3121800 0x03040000+19 20 3295200 0x03080000+20 21 3468600 0x03100000+21 22 3642000 0x03200000+22 23 3815400 0x03400000+23 24 3988800 0x04008000+24 25 4162200 0x04010000+25 26 4335600 0x04020000+26 27 4509000 0x04040000+27 28 4682400 0x04080000+28 29 4855800 0x04100000+29 30 5029200 0x04200000+30 31 5202600 0x04400000+31 32 5376000 0x05008000+32 33 5549400 0x05010000+33 34 5722800 0x05020000+34 35 5896200 0x05040000+35 36 6069600 0x05080000+36 37 6243000 0x05100000+37 38 6416400 0x05200000+38 39 6589800 0x05400000+39 40 6763200 0x06008000+40 41 6936600 0x06010000+41 42 7110000 0x06020000+42 43 7283400 0x06040000+43 44 7456800 0x06080000+44 45 7630200 0x06100000+45 46 7803600 0x06200000+46 47 7977000 0x06400000+47 48 8150400 0x07008000+48 49 8323800 0x07010000+49 50 8497200 0x07020000+50 51 8670600 0x07040000+51 52 8844000 0x07080000+52 53 9017400 0x07100000+53 54 9190800 0x07200000+54 55 9364200 0x07400000+55 56 9537600 0x08008000+56 57 9711000 0x08010000+57 58 9884400 0x08020000+58 59 10057800 0x08040000+59 60 10231200 0x08080000+60 61 10404600 0x08100000+61 62 10578000 0x08200000+62 63 10751400 0x08400000+63 64 10924800 0x09008000+64 65 11098200 0x09010000+65 66 11271600 0x09020000+66 67 11445000 0x09040000+67 68 11618400 0x09080000+68 69 11791800 0x09100000+69 70 11965200 0x09200000+70 71 12138600 0x09400000+71 72 12312000 0x0a008000+72 73 12485400 0x0a010000+73 74 12658800 0x0a020000+74 75 12832200 0x0a040000+75 76 13005600 0x0a080000+76 77 13179000 0x0a100000+77 78 13352400 0x0a200000+78 79 13525800 0x0a400000+79 80 13699200 0x0b008000+80 81 13872600 0x0b010000+81 82 14046000 0x0b020000+82 83 14219400 0x0b040000+83 84 14392800 0x0b080000+84 85 14566200 0x0b100000+85 86 14739600 0x0b200000+86 87 14913000 0x0b400000+87 88 15086400 0x0c008000+88 89 15259800 0x0c010000+89 90 15433200 0x0c020000+90 91 15606600 0x0c040000+91 92 15780000 0x0c080000+92 93 15953400 0x0c100000+93 94 16126800 0x0c200000+94 95 16300200 0x0c400000+95 96 16473600 0x0d008000+96 97 16647000 0x0d010000+97 98 16820400 0x0d020000+98 99 16993800 0x0d040000+99 100 17167200 0x0d080000+100 101 17340600 0x0d100000+101 102 17514000 0x0d200000+102 103 17687400 0x0d400000+103 104 17860800 0x0e008000+104 105 18034200 0x0e010000+105 106 18207600 0x0e020000+106 107 18381000 0x0e040000+107 108 18554400 0x0e080000+108 109 18727800 0x0e100000+109 110 18901200 0x0e200000+110 111 19074600 0x0e400000+111 112 19248000 0x0f008000+112 113 19421400 0x0f010000+113 114 19594800 0x0f020000+114 115 19768200 0x0f040000+115 116 19941600 0x0f080000+116 117 20115000 0x0f100000+117 118 20288400 0x0f200000+118 119 20461800 0x0f400000+119 120 20635200 0x10008000+120 121 20808600 0x10010000+121 122 20982000 0x10020000+122 123 21155400 0x10040000+123 124 21328800 0x10080000+124 125 21502200 0x10100000+125 126 21675600 0x10200000+126 127 21849000 0x10400000+127 128 22022400 0x11008000+128 129 22195800 0x11010000+129 130 22369200 0x11020000+130 131 22542600 0x11040000+131 132 22716000 0x11080000+132 133 22889400 0x11100000+133 134 23062800 0x11200000+134 135 23236200 0x11400000+135 136 23409600 0x12008000+136 137 23583000 0x12010000+137 138 23756400 0x12020000+138 139 23929800 0x12040000+139 140 24103200 0x12080000+140 141 24276600 0x12100000+141 142 24450000 0x12200000+142 143 24623400 0x12400000+143 144 24796800 0x13008000+144 145 24970200 0x13010000+145 146 25143600 0x13020000+146 147 25317000 0x13040000+147 148 25490400 0x13080000+148 149 25663800 0x13100000+149 150 25837200 0x13200000+150 151 26010600 0x13400000+151 152 26184000 0x14008000+152 153 26357400 0x14010000+153 154 26530800 0x14020000+154 155 26704200 0x14040000+155 156 26877600 0x14080000+156 157 27051000 0x14100000+157 158 27224400 0x14200000+158 159 27397800 0x14400000+159 160 27571200 0x15008000+160 161 27744600 0x15010000+161 162 27918000 0x15020000+162 163 28091400 0x15040000+163 164 28264800 0x15080000+164 165 28438200 0x15100000+165 166 28611600 0x15200000+166 167 28785000 0x15400000+167 168 28958400 0x16008000+168 169 29131800 0x16010000+169 170 29305200 0x16020000+170 171 29478600 0x16040000+171 172 29652000 0x16080000+172 173 29825400 0x16100000+173 174 29998800 0x16200000+174 175 30172200 0x16400000+175 176 30345600 0x17008000+176 177 30519000 0x17010000+177 178 30692400 0x17020000+178 179 30865800 0x17040000+179 180 31039200 0x17080000+180 181 31212600 0x17100000+181 182 31386000 0x17200000+182 183 31559400 0x17400000+183 184 31732800 0x18008000+184 185 31906200 0x18010000+185 186 32079600 0x18020000+186 187 32253000 0x18040000+187 188 32426400 0x18080000+188 189 32599800 0x18100000+189 190 32773200 0x18200000+190 191 32946600 0x18400000+191 192 33120000 0x19008000+192 193 33293400 0x19010000+193 194 33466800 0x19020000+194 195 33640200 0x19040000+195 196 33813600 0x19080000+196 197 33987000 0x19100000+197 198 34160400 0x19200000+198 199 34333800 0x19400000+199 200 34507200 0x1a008000+200 201 34680600 0x1a010000+201 202 34854000 0x1a020000+202 203 35027400 0x1a040000+203 204 35200800 0x1a080000+204 205 35374200 0x1a100000+205 206 35547600 0x1a200000+206 207 35721000 0x1a400000+207 208 35894400 0x1b008000+208 209 36067800 0x1b010000+209 210 36241200 0x1b020000+210 211 36414600 0x1b040000+211 212 36588000 0x1b080000+212 213 36761400 0x1b100000+213 214 36934800 0x1b200000+214 215 37108200 0x1b400000+215 216 37281600 0x1c008000+216 217 37455000 0x1c010000+217 218 37628400 0x1c020000+218 219 37801800 0x1c040000+219 220 37975200 0x1c080000+220 221 38148600 0x1c100000+221 222 38322000 0x1c200000+222 223 38495400 0x1c400000+223 224 38668800 0x1d008000+224 225 38842200 0x1d00ffff+225 226 39015600 0x1d00ffff+
+ data/test_vectors_aserti3-2d_run05.txt view
@@ -0,0 +1,234 @@+## description: run5 - from POW limit, a series of halflife block height jumps w/o time increment, halving target at each block+##   anchor height: 1+##   anchor parent time: 0+##   anchor nBits: 0x1d00ffff+##   start height: 2+##   start time: 0+##   iterations: 225+# iteration,height,time,target+1 2 0 0x1d00fec5+2 290 0 0x1c7f62c0+3 578 0 0x1c3fb160+4 866 0 0x1c1fd8b0+5 1154 0 0x1c0fec58+6 1442 0 0x1c07f62c+7 1730 0 0x1c03fb16+8 2018 0 0x1c01fd8b+9 2306 0 0x1c00fec5+10 2594 0 0x1b7f62c0+11 2882 0 0x1b3fb160+12 3170 0 0x1b1fd8b0+13 3458 0 0x1b0fec58+14 3746 0 0x1b07f62c+15 4034 0 0x1b03fb16+16 4322 0 0x1b01fd8b+17 4610 0 0x1b00fec5+18 4898 0 0x1a7f62c0+19 5186 0 0x1a3fb160+20 5474 0 0x1a1fd8b0+21 5762 0 0x1a0fec58+22 6050 0 0x1a07f62c+23 6338 0 0x1a03fb16+24 6626 0 0x1a01fd8b+25 6914 0 0x1a00fec5+26 7202 0 0x197f62c0+27 7490 0 0x193fb160+28 7778 0 0x191fd8b0+29 8066 0 0x190fec58+30 8354 0 0x1907f62c+31 8642 0 0x1903fb16+32 8930 0 0x1901fd8b+33 9218 0 0x1900fec5+34 9506 0 0x187f62c0+35 9794 0 0x183fb160+36 10082 0 0x181fd8b0+37 10370 0 0x180fec58+38 10658 0 0x1807f62c+39 10946 0 0x1803fb16+40 11234 0 0x1801fd8b+41 11522 0 0x1800fec5+42 11810 0 0x177f62c0+43 12098 0 0x173fb160+44 12386 0 0x171fd8b0+45 12674 0 0x170fec58+46 12962 0 0x1707f62c+47 13250 0 0x1703fb16+48 13538 0 0x1701fd8b+49 13826 0 0x1700fec5+50 14114 0 0x167f62c0+51 14402 0 0x163fb160+52 14690 0 0x161fd8b0+53 14978 0 0x160fec58+54 15266 0 0x1607f62c+55 15554 0 0x1603fb16+56 15842 0 0x1601fd8b+57 16130 0 0x1600fec5+58 16418 0 0x157f62c0+59 16706 0 0x153fb160+60 16994 0 0x151fd8b0+61 17282 0 0x150fec58+62 17570 0 0x1507f62c+63 17858 0 0x1503fb16+64 18146 0 0x1501fd8b+65 18434 0 0x1500fec5+66 18722 0 0x147f62c0+67 19010 0 0x143fb160+68 19298 0 0x141fd8b0+69 19586 0 0x140fec58+70 19874 0 0x1407f62c+71 20162 0 0x1403fb16+72 20450 0 0x1401fd8b+73 20738 0 0x1400fec5+74 21026 0 0x137f62c0+75 21314 0 0x133fb160+76 21602 0 0x131fd8b0+77 21890 0 0x130fec58+78 22178 0 0x1307f62c+79 22466 0 0x1303fb16+80 22754 0 0x1301fd8b+81 23042 0 0x1300fec5+82 23330 0 0x127f62c0+83 23618 0 0x123fb160+84 23906 0 0x121fd8b0+85 24194 0 0x120fec58+86 24482 0 0x1207f62c+87 24770 0 0x1203fb16+88 25058 0 0x1201fd8b+89 25346 0 0x1200fec5+90 25634 0 0x117f62c0+91 25922 0 0x113fb160+92 26210 0 0x111fd8b0+93 26498 0 0x110fec58+94 26786 0 0x1107f62c+95 27074 0 0x1103fb16+96 27362 0 0x1101fd8b+97 27650 0 0x1100fec5+98 27938 0 0x107f62c0+99 28226 0 0x103fb160+100 28514 0 0x101fd8b0+101 28802 0 0x100fec58+102 29090 0 0x1007f62c+103 29378 0 0x1003fb16+104 29666 0 0x1001fd8b+105 29954 0 0x1000fec5+106 30242 0 0x0f7f62c0+107 30530 0 0x0f3fb160+108 30818 0 0x0f1fd8b0+109 31106 0 0x0f0fec58+110 31394 0 0x0f07f62c+111 31682 0 0x0f03fb16+112 31970 0 0x0f01fd8b+113 32258 0 0x0f00fec5+114 32546 0 0x0e7f62c0+115 32834 0 0x0e3fb160+116 33122 0 0x0e1fd8b0+117 33410 0 0x0e0fec58+118 33698 0 0x0e07f62c+119 33986 0 0x0e03fb16+120 34274 0 0x0e01fd8b+121 34562 0 0x0e00fec5+122 34850 0 0x0d7f62c0+123 35138 0 0x0d3fb160+124 35426 0 0x0d1fd8b0+125 35714 0 0x0d0fec58+126 36002 0 0x0d07f62c+127 36290 0 0x0d03fb16+128 36578 0 0x0d01fd8b+129 36866 0 0x0d00fec5+130 37154 0 0x0c7f62c0+131 37442 0 0x0c3fb160+132 37730 0 0x0c1fd8b0+133 38018 0 0x0c0fec58+134 38306 0 0x0c07f62c+135 38594 0 0x0c03fb16+136 38882 0 0x0c01fd8b+137 39170 0 0x0c00fec5+138 39458 0 0x0b7f62c0+139 39746 0 0x0b3fb160+140 40034 0 0x0b1fd8b0+141 40322 0 0x0b0fec58+142 40610 0 0x0b07f62c+143 40898 0 0x0b03fb16+144 41186 0 0x0b01fd8b+145 41474 0 0x0b00fec5+146 41762 0 0x0a7f62c0+147 42050 0 0x0a3fb160+148 42338 0 0x0a1fd8b0+149 42626 0 0x0a0fec58+150 42914 0 0x0a07f62c+151 43202 0 0x0a03fb16+152 43490 0 0x0a01fd8b+153 43778 0 0x0a00fec5+154 44066 0 0x097f62c0+155 44354 0 0x093fb160+156 44642 0 0x091fd8b0+157 44930 0 0x090fec58+158 45218 0 0x0907f62c+159 45506 0 0x0903fb16+160 45794 0 0x0901fd8b+161 46082 0 0x0900fec5+162 46370 0 0x087f62c0+163 46658 0 0x083fb160+164 46946 0 0x081fd8b0+165 47234 0 0x080fec58+166 47522 0 0x0807f62c+167 47810 0 0x0803fb16+168 48098 0 0x0801fd8b+169 48386 0 0x0800fec5+170 48674 0 0x077f62c0+171 48962 0 0x073fb160+172 49250 0 0x071fd8b0+173 49538 0 0x070fec58+174 49826 0 0x0707f62c+175 50114 0 0x0703fb16+176 50402 0 0x0701fd8b+177 50690 0 0x0700fec5+178 50978 0 0x067f62c0+179 51266 0 0x063fb160+180 51554 0 0x061fd8b0+181 51842 0 0x060fec58+182 52130 0 0x0607f62c+183 52418 0 0x0603fb16+184 52706 0 0x0601fd8b+185 52994 0 0x0600fec5+186 53282 0 0x057f62c0+187 53570 0 0x053fb160+188 53858 0 0x051fd8b0+189 54146 0 0x050fec58+190 54434 0 0x0507f62c+191 54722 0 0x0503fb16+192 55010 0 0x0501fd8b+193 55298 0 0x0500fec5+194 55586 0 0x047f62c0+195 55874 0 0x043fb160+196 56162 0 0x041fd8b0+197 56450 0 0x040fec58+198 56738 0 0x0407f62c+199 57026 0 0x0403fb16+200 57314 0 0x0401fd8b+201 57602 0 0x0400fec5+202 57890 0 0x037f62c0+203 58178 0 0x033fb160+204 58466 0 0x031fd8b0+205 58754 0 0x030fec58+206 59042 0 0x0307f62c+207 59330 0 0x0303fb16+208 59618 0 0x0301fd8b+209 59906 0 0x0300fec5+210 60194 0 0x027f6200+211 60482 0 0x023fb100+212 60770 0 0x021fd800+213 61058 0 0x020fec00+214 61346 0 0x0207f600+215 61634 0 0x0203fb00+216 61922 0 0x0201fd00+217 62210 0 0x0200fe00+218 62498 0 0x017f0000+219 62786 0 0x013f0000+220 63074 0 0x011f0000+221 63362 0 0x010f0000+222 63650 0 0x01070000+223 63938 0 0x01030000+224 64226 0 0x01010000+225 64514 0 0x01010000+
+ data/test_vectors_aserti3-2d_run06.txt view
@@ -0,0 +1,1009 @@+## description: run6 - deterministically random solvetimes for stable hashrate around a recent real life nBits+##   anchor height: 1+##   anchor parent time: 0+##   anchor nBits: 0x1802aee8+##   start height: 2+##   start time: 1200+##   iterations: 1000+# iteration,height,time,target+1 2 1200 0x1802aee8+2 3 1310 0x1802ad91+3 4 1327 0x1802abf8+4 5 1739 0x1802ab73+5 6 2219 0x1802ab20+6 7 2604 0x1802aa89+7 8 2746 0x1802a94b+8 9 7099 0x1802b39c+9 10 7099 0x1802b1f2+10 11 7657 0x1802b1d4+11 12 8626 0x1802b2db+12 13 8816 0x1802b1b7+13 14 9632 0x1802b252+14 15 10804 0x1802b3e7+15 16 11727 0x1802b4ce+16 17 11761 0x1802b33c+17 18 13000 0x1802b501+18 19 14144 0x1802b686+19 20 15302 0x1802b816+20 21 15443 0x1802b6cf+21 22 18902 0x1802bed6+22 23 19348 0x1802be68+23 24 20359 0x1802bf92+24 25 20760 0x1802bf01+25 26 21285 0x1802becb+26 27 21425 0x1802bd7c+27 28 21985 0x1802bd61+28 29 22100 0x1802bc02+29 30 22170 0x1802ba85+30 31 23287 0x1802bbf9+31 32 23465 0x1802baca+32 33 24549 0x1802bc27+33 34 24981 0x1802bbae+34 35 25619 0x1802bbc9+35 36 26539 0x1802bcb0+36 37 27381 0x1802bd5c+37 38 28265 0x1802be2a+38 39 30203 0x1802c1f6+39 40 30824 0x1802c203+40 41 31537 0x1802c254+41 42 32479 0x1802c34d+42 43 33013 0x1802c31d+43 44 33615 0x1802c31f+44 45 33944 0x1802c25c+45 46 34232 0x1802c177+46 47 34368 0x1802c028+47 48 34434 0x1802bea6+48 49 34805 0x1802bdff+49 50 34810 0x1802bc52+50 51 34841 0x1802bab7+51 52 34914 0x1802b93d+52 53 35335 0x1802b8bc+53 54 37423 0x1802bcee+54 55 38049 0x1802bcfe+55 56 38435 0x1802bc65+56 57 39072 0x1802bc7d+57 58 39434 0x1802bbd4+58 59 39506 0x1802ba57+59 60 39760 0x1802b960+60 61 40232 0x1802b902+61 62 40553 0x1802b83c+62 63 41296 0x1802b8a2+63 64 41590 0x1802b7c6+64 65 41709 0x1802b66e+65 66 42647 0x1802b760+66 67 43493 0x1802b80e+67 68 43493 0x1802b663+68 69 43501 0x1802b4bb+69 70 43969 0x1802b45d+70 71 44175 0x1802b344+71 72 44756 0x1802b339+72 73 44763 0x1802b191+73 74 45570 0x1802b225+74 75 46234 0x1802b252+75 76 46484 0x1802b159+76 77 46668 0x1802b032+77 78 46739 0x1802aebd+78 79 46749 0x1802ad21+79 80 46815 0x1802aba9+80 81 48149 0x1802adab+81 82 48206 0x1802ac2f+82 83 48491 0x1802ab52+83 84 48530 0x1802a9c9+84 85 49095 0x1802a9b2+85 86 49640 0x1802a98b+86 87 49766 0x1802a840+87 88 50290 0x1802a80d+88 89 52536 0x1802ac8a+89 90 52614 0x1802ab1c+90 91 52773 0x1802a9e9+91 92 53231 0x1802a986+92 93 55043 0x1802acd4+93 94 56035 0x1802ade9+94 95 56085 0x1802ac65+95 96 56918 0x1802ad0a+96 97 58635 0x1802b019+97 98 61599 0x1802b6ae+98 99 61973 0x1802b60b+99 100 63111 0x1802b78d+100 101 63201 0x1802b620+101 102 64481 0x1802b809+102 103 64935 0x1802b79d+103 104 65231 0x1802b6c4+104 105 65793 0x1802b6a9+105 106 66209 0x1802b626+106 107 67377 0x1802b7bb+107 108 67640 0x1802b6cc+108 109 67760 0x1802b575+109 110 68169 0x1802b4ec+110 111 68459 0x1802b410+111 112 69548 0x1802b56c+112 113 69669 0x1802b415+113 114 69893 0x1802b30b+114 115 69933 0x1802b17c+115 116 71307 0x1802b3a2+116 117 71811 0x1802b35f+117 118 71866 0x1802b1dc+118 119 72099 0x1802b0d8+119 120 72262 0x1802afa1+120 121 72407 0x1802ae60+121 122 72452 0x1802acdb+122 123 72631 0x1802abb4+123 124 73096 0x1802ab56+124 125 73181 0x1802a9ee+125 126 77005 0x1802b2cb+126 127 77349 0x1802b215+127 128 78766 0x1802b45b+128 129 78889 0x1802b306+129 130 79167 0x1802b222+130 131 79454 0x1802b143+131 132 79556 0x1802afe4+132 133 80610 0x1802b126+133 134 81949 0x1802b331+134 135 82413 0x1802b2d0+135 136 83361 0x1802b3c7+136 137 83730 0x1802b326+137 138 84038 0x1802b255+138 139 85156 0x1802b3c5+139 140 85409 0x1802b2ce+140 141 86177 0x1802b346+141 142 86755 0x1802b336+142 143 87052 0x1802b25d+143 144 87370 0x1802b196+144 145 87389 0x1802aff9+145 146 87442 0x1802ae7b+146 147 87678 0x1802ad7b+147 148 88050 0x1802acda+148 149 88291 0x1802abdf+149 150 88673 0x1802ab46+150 151 88863 0x1802aa27+151 152 88867 0x1802a887+152 153 89154 0x1802a7ac+153 154 89506 0x1802a700+154 155 91610 0x1802ab18+155 156 93523 0x1802aeb2+156 157 93538 0x1802ad17+157 158 93737 0x1802abfe+158 159 94252 0x1802abc3+159 160 94544 0x1802aaeb+160 161 95195 0x1802ab10+161 162 95394 0x1802a9f8+162 163 95663 0x1802a910+163 164 95922 0x1802a822+164 165 96051 0x1802a6d9+165 166 96105 0x1802a55e+166 167 96501 0x1802a4d1+167 168 97176 0x1802a505+168 169 97543 0x1802a464+169 170 97695 0x1802a32d+170 171 99835 0x1802a75a+171 172 100007 0x1802a630+172 173 100819 0x1802a6c4+173 174 101427 0x1802a6c9+174 175 101665 0x1802a5ce+175 176 103212 0x1802a861+176 177 104441 0x1802aa17+177 178 105132 0x1802aa57+178 179 106722 0x1802ad0b+179 180 106762 0x1802ab83+180 181 107856 0x1802acde+181 182 108452 0x1802acdb+182 183 109334 0x1802ada1+183 184 109367 0x1802ac14+184 185 109654 0x1802ab38+185 186 112245 0x1802b0b0+186 187 112521 0x1802afc9+187 188 115789 0x1802b735+188 189 115930 0x1802b5ed+189 190 116109 0x1802b4c1+190 191 116489 0x1802b425+191 192 117759 0x1802b603+192 193 119446 0x1802b90d+193 194 120049 0x1802b910+194 195 120141 0x1802b7a3+195 196 120436 0x1802b6c9+196 197 122187 0x1802ba04+197 198 122997 0x1802ba97+198 199 123490 0x1802ba4c+199 200 125018 0x1802bce8+200 201 125604 0x1802bcde+201 202 126289 0x1802bd1b+202 203 126617 0x1802bc57+203 204 127145 0x1802bc22+204 205 127271 0x1802bacd+205 206 127839 0x1802bab7+206 207 127846 0x1802b90d+207 208 128461 0x1802b918+208 209 129263 0x1802b9a8+209 210 129952 0x1802b9e9+210 211 130129 0x1802b8ba+211 212 130818 0x1802b8fa+212 213 131516 0x1802b940+213 214 133313 0x1802bc9d+214 215 134069 0x1802bd0e+215 216 134091 0x1802bb6e+216 217 135378 0x1802bd5c+217 218 135572 0x1802bc37+218 219 135617 0x1802baa7+219 220 136908 0x1802bc98+220 221 138330 0x1802beeb+221 222 138420 0x1802bd79+222 223 138693 0x1802bc8d+223 224 139047 0x1802bbdc+224 225 141426 0x1802c0e4+225 226 142736 0x1802c2e7+226 227 143823 0x1802c449+227 228 144028 0x1802c32a+228 229 144076 0x1802c198+229 230 144305 0x1802c08b+230 231 144409 0x1802bf24+231 232 144753 0x1802be6b+232 233 145140 0x1802bdd2+233 234 145256 0x1802bc72+234 235 146289 0x1802bdac+235 236 146670 0x1802bd0e+236 237 146851 0x1802bbdf+237 238 147778 0x1802bccb+238 239 148460 0x1802bd08+239 240 148765 0x1802bc32+240 241 150937 0x1802c0a1+241 242 152635 0x1802c3be+242 243 152668 0x1802c223+243 244 154605 0x1802c5f1+244 245 154626 0x1802c44c+245 246 156211 0x1802c718+246 247 157840 0x1802ca0d+247 248 158420 0x1802c9fd+248 249 158546 0x1802c8a0+249 250 158683 0x1802c74e+250 251 159190 0x1802c70b+251 252 159346 0x1802c5c6+252 253 161669 0x1802cab3+253 254 162729 0x1802cc06+254 255 162805 0x1802ca86+255 256 162908 0x1802c919+256 257 162913 0x1802c764+257 258 163796 0x1802c832+258 259 163885 0x1802c6bd+259 260 164782 0x1802c797+260 261 165461 0x1802c7d2+261 262 166072 0x1802c7d7+262 263 166184 0x1802c672+263 264 167970 0x1802c9d7+264 265 168010 0x1802c840+265 266 168100 0x1802c6c8+266 267 168290 0x1802c59e+267 268 168805 0x1802c55e+268 269 168867 0x1802c3d6+269 270 168956 0x1802c264+270 271 169552 0x1802c261+271 272 169886 0x1802c1a0+272 273 171499 0x1802c47f+273 274 171533 0x1802c2e4+274 275 171626 0x1802c172+275 276 171784 0x1802c033+276 277 171789 0x1802be83+277 278 172130 0x1802bdca+278 279 172878 0x1802be35+279 280 173620 0x1802be9b+280 281 174625 0x1802bfbf+281 282 175913 0x1802c1b3+282 283 176123 0x1802c099+283 284 176659 0x1802c068+284 285 179230 0x1802c604+285 286 179652 0x1802c581+286 287 180535 0x1802c652+287 288 181130 0x1802c64c+288 289 181754 0x1802c65f+289 290 182492 0x1802c6c3+290 291 182943 0x1802c657+291 292 184205 0x1802c83a+292 293 185303 0x1802c9a7+293 294 185558 0x1802c8ab+294 295 185565 0x1802c6f8+295 296 186150 0x1802c6ed+296 297 187086 0x1802c7e4+297 298 188965 0x1802cb8f+298 299 189427 0x1802cb27+299 300 189476 0x1802c992+300 301 191306 0x1802cd1d+301 302 191342 0x1802cb7d+302 303 193074 0x1802cebf+303 304 194118 0x1802d009+304 305 195282 0x1802d1a9+305 306 195586 0x1802d0cd+306 307 196066 0x1802d075+307 308 196253 0x1802cf43+308 309 196875 0x1802cf55+309 310 197005 0x1802cdf9+310 311 198744 0x1802d143+311 312 199668 0x1802d232+312 313 200183 0x1802d1f4+313 314 201905 0x1802d537+314 315 202203 0x1802d455+315 316 202217 0x1802d2a3+316 317 203398 0x1802d453+317 318 203442 0x1802d2b5+318 319 203719 0x1802d1c7+319 320 204031 0x1802d0ed+320 321 204406 0x1802d04a+321 322 204630 0x1802cf33+322 323 205282 0x1802cf58+323 324 205986 0x1802cfa6+324 325 206367 0x1802cf05+325 326 207491 0x1802d087+326 327 208220 0x1802d0e8+327 328 208462 0x1802cfde+328 329 209152 0x1802d021+329 330 209504 0x1802cf68+330 331 209810 0x1802ce8f+331 332 210374 0x1802ce74+332 333 210480 0x1802cd0a+333 334 210775 0x1802cc28+334 335 210886 0x1802cac1+335 336 211520 0x1802cad9+336 337 212428 0x1802cbbd+337 338 212804 0x1802cb17+338 339 212881 0x1802c997+339 340 213220 0x1802c8d6+340 341 213649 0x1802c85a+341 342 214589 0x1802c954+342 343 214776 0x1802c825+343 344 215486 0x1802c875+344 345 215631 0x1802c728+345 346 215936 0x1802c652+346 347 216036 0x1802c4e2+347 348 216088 0x1802c355+348 349 216873 0x1802c3db+349 350 218112 0x1802c5ae+350 351 218515 0x1802c51d+351 352 219568 0x1802c667+352 353 219869 0x1802c58e+353 354 220479 0x1802c596+354 355 220868 0x1802c4fd+355 356 221242 0x1802c457+356 357 221357 0x1802c2f5+357 358 221500 0x1802c1aa+358 359 223428 0x1802c570+359 360 224084 0x1802c599+360 361 224126 0x1802c404+361 362 224965 0x1802c4af+362 363 225185 0x1802c39b+363 364 225755 0x1802c385+364 365 225964 0x1802c269+365 366 227000 0x1802c3a6+366 367 227111 0x1802c243+367 368 227897 0x1802c2ca+368 369 228316 0x1802c246+369 370 228380 0x1802c0c1+370 371 228576 0x1802bf9d+371 372 228582 0x1802bdef+372 373 228962 0x1802bd4e+373 374 229363 0x1802bcc0+374 375 229905 0x1802bc98+375 376 230037 0x1802bb46+376 377 230138 0x1802b9de+377 378 231396 0x1802bbb9+378 379 231623 0x1802baaa+379 380 232554 0x1802bb9c+380 381 232611 0x1802ba14+381 382 233200 0x1802ba0c+382 383 233719 0x1802b9d1+383 384 234535 0x1802ba6c+384 385 236433 0x1802be15+385 386 236471 0x1802bc7d+386 387 236693 0x1802bb6e+387 388 239252 0x1802c0f4+388 389 239765 0x1802c0b6+389 390 240052 0x1802bfd5+390 391 240278 0x1802bec6+391 392 240756 0x1802be6d+392 393 240885 0x1802bd19+393 394 241254 0x1802bc72+394 395 241913 0x1802bc9d+395 396 242464 0x1802bc78+396 397 243033 0x1802bc62+397 398 243081 0x1802bad5+398 399 243768 0x1802bb13+399 400 243817 0x1802b988+400 401 244075 0x1802b891+401 402 244468 0x1802b7fe+402 403 244512 0x1802b671+403 404 245063 0x1802b64b+404 405 245326 0x1802b55c+405 406 245549 0x1802b450+406 407 245780 0x1802b349+407 408 246848 0x1802b496+408 409 247721 0x1802b55a+409 410 247779 0x1802b3d5+410 411 248989 0x1802b58a+411 412 249067 0x1802b415+412 413 249862 0x1802b4a1+413 414 249934 0x1802b326+414 415 250197 0x1802b237+415 416 251266 0x1802b384+416 417 251496 0x1802b27d+417 418 251502 0x1802b0d8+418 419 252819 0x1802b2d6+419 420 253248 0x1802b25d+420 421 253637 0x1802b1c7+421 422 254084 0x1802b159+422 423 254230 0x1802b017+423 424 254564 0x1802af5b+424 425 255322 0x1802afcc+425 426 257123 0x1802b31e+426 427 257814 0x1802b361+427 428 259029 0x1802b517+428 429 259280 0x1802b420+429 430 261956 0x1802b9ec+430 431 262037 0x1802b877+431 432 262598 0x1802b85c+432 433 262640 0x1802b6cc+433 434 262985 0x1802b616+434 435 263302 0x1802b54c+435 436 263576 0x1802b463+436 437 265608 0x1802b861+437 438 266294 0x1802b8a2+438 439 266491 0x1802b780+439 440 266737 0x1802b684+440 441 267140 0x1802b5f5+441 442 268115 0x1802b702+442 443 268169 0x1802b57a+443 444 268543 0x1802b4d9+444 445 268693 0x1802b39a+445 446 269434 0x1802b3fd+446 447 270331 0x1802b4d1+447 448 270936 0x1802b4d6+448 449 271165 0x1802b3cd+449 450 271296 0x1802b27d+450 451 271654 0x1802b1d1+451 452 272212 0x1802b1b4+452 453 272468 0x1802b0c0+453 454 272519 0x1802af3b+454 455 273022 0x1802aef8+455 456 273131 0x1802ada0+456 457 273635 0x1802ad5c+457 458 274006 0x1802acbb+458 459 275468 0x1802af15+459 460 275673 0x1802ae02+460 461 275779 0x1802aca8+461 462 275995 0x1802ab9a+462 463 277324 0x1802ad9b+463 464 277819 0x1802ad50+464 465 278231 0x1802accc+465 466 278774 0x1802aca4+466 467 278979 0x1802ab90+467 468 279447 0x1802ab33+468 469 280730 0x1802ad11+469 470 282233 0x1802af8b+470 471 282874 0x1802afa9+471 472 283519 0x1802afc9+472 473 283875 0x1802af1a+473 474 284002 0x1802add2+474 475 284113 0x1802ac79+475 476 284407 0x1802aba4+476 477 285150 0x1802ac07+477 478 285223 0x1802aa96+478 479 285898 0x1802aacc+479 480 286658 0x1802ab3c+480 481 288074 0x1802ad77+481 482 289756 0x1802b06f+482 483 289845 0x1802af08+483 484 289872 0x1802ad75+484 485 291161 0x1802af58+485 486 291360 0x1802ae3e+486 487 291396 0x1802acb5+487 488 293190 0x1802aff9+488 489 293469 0x1802af18+489 490 293870 0x1802ae8e+490 491 294577 0x1802aed9+491 492 295696 0x1802b047+492 493 295883 0x1802af23+493 494 297061 0x1802b0ba+494 495 297178 0x1802af66+495 496 297235 0x1802ade9+496 497 298229 0x1802aefa+497 498 299108 0x1802afc1+498 499 299129 0x1802ae2a+499 500 299790 0x1802ae55+500 501 300620 0x1802aef5+501 502 302957 0x1802b3c7+502 503 302969 0x1802b225+503 504 303470 0x1802b1dc+504 505 304333 0x1802b298+505 506 304549 0x1802b186+506 507 304873 0x1802b0c5+507 508 306711 0x1802b433+508 509 307111 0x1802b3a4+509 510 307259 0x1802b265+510 511 308120 0x1802b31e+511 512 308324 0x1802b204+512 513 308744 0x1802b184+513 514 309667 0x1802b26a+514 515 310251 0x1802b25d+515 516 310840 0x1802b258+516 517 311159 0x1802b18e+517 518 311209 0x1802b009+518 519 312869 0x1802b2f9+519 520 313088 0x1802b1ea+520 521 313594 0x1802b1a7+521 522 313847 0x1802b0b2+522 523 314156 0x1802afe4+523 524 317015 0x1802b62b+524 525 318195 0x1802b7c8+525 526 318450 0x1802b6d1+526 527 319118 0x1802b702+527 528 319131 0x1802b562+528 529 319169 0x1802b3cf+529 530 319969 0x1802b45d+530 531 320490 0x1802b425+531 532 320547 0x1802b2a3+532 533 320795 0x1802b1a9+533 534 320964 0x1802b075+534 535 323147 0x1802b4dc+535 536 323559 0x1802b455+536 537 324733 0x1802b5f0+537 538 324771 0x1802b45d+538 539 325284 0x1802b420+539 540 325507 0x1802b313+540 541 326393 0x1802b3e2+541 542 326935 0x1802b3b7+542 543 328262 0x1802b5bd+543 544 329269 0x1802b6e1+544 545 329549 0x1802b5fb+545 546 330098 0x1802b5d8+546 547 331221 0x1802b74d+547 548 331352 0x1802b5fd+548 549 332256 0x1802b6d7+549 550 332403 0x1802b592+550 551 333860 0x1802b7f8+551 552 338121 0x1802c249+552 553 338373 0x1802c14d+553 554 338589 0x1802c038+554 555 340901 0x1802c513+555 556 341396 0x1802c4c7+556 557 341506 0x1802c360+557 558 343767 0x1802c81d+558 559 345582 0x1802cb9a+559 560 346627 0x1802cce2+560 561 346726 0x1802cb72+561 562 347129 0x1802cade+562 563 347323 0x1802c9b5+563 564 347755 0x1802c939+564 565 349011 0x1802cb1c+565 566 349125 0x1802c9b7+566 567 349616 0x1802c967+567 568 352124 0x1802cee5+568 569 352790 0x1802cf15+569 570 353733 0x1802d011+570 571 353889 0x1802ceca+571 572 354836 0x1802cfcb+572 573 355122 0x1802cee2+573 574 355281 0x1802cd9d+574 575 358636 0x1802d597+575 576 358980 0x1802d4d9+576 577 359817 0x1802d58a+577 578 360240 0x1802d506+578 579 360536 0x1802d422+579 580 361017 0x1802d3ca+580 581 361085 0x1802d242+581 582 361301 0x1802d123+582 583 361342 0x1802cf86+583 584 362321 0x1802d09f+584 585 363170 0x1802d156+585 586 363245 0x1802cfd1+586 587 363645 0x1802cf3d+587 588 363941 0x1802ce5f+588 589 363946 0x1802cca6+589 590 365270 0x1802cebc+590 591 365501 0x1802cdab+591 592 366068 0x1802cd93+592 593 366544 0x1802cd37+593 594 366924 0x1802cc96+594 595 368705 0x1802cffe+595 596 368748 0x1802ce61+596 597 368918 0x1802cd25+597 598 369292 0x1802cc7e+598 599 369315 0x1802cad6+599 600 369787 0x1802ca76+600 601 370035 0x1802c977+601 602 370217 0x1802c842+602 603 371353 0x1802c9cd+603 604 371422 0x1802c845+604 605 372018 0x1802c845+605 606 372199 0x1802c710+606 607 372218 0x1802c568+607 608 372439 0x1802c454+608 609 372514 0x1802c2d7+609 610 373133 0x1802c2e4+610 611 373323 0x1802c1bb+611 612 374030 0x1802c208+612 613 374127 0x1802c09b+613 614 374378 0x1802bf9f+614 615 374482 0x1802be38+615 616 375874 0x1802c073+616 617 376239 0x1802bfca+617 618 376843 0x1802bfcd+618 619 376992 0x1802be86+619 620 378438 0x1802c0ec+620 621 379308 0x1802c1b0+621 622 379375 0x1802c02b+622 623 379950 0x1802c01b+623 624 380261 0x1802bf49+624 625 380465 0x1802be2a+625 626 380597 0x1802bcd8+626 627 383464 0x1802c342+627 628 383491 0x1802c1a2+628 629 383788 0x1802c0c6+629 630 384865 0x1802c221+630 631 384907 0x1802c08b+631 632 384970 0x1802bf06+632 633 384987 0x1802bd64+633 634 385254 0x1802bc72+634 635 385907 0x1802bc98+635 636 386117 0x1802bb81+636 637 386299 0x1802ba52+637 638 386731 0x1802b9d9+638 639 386754 0x1802b83c+639 640 387502 0x1802b8a7+640 641 387599 0x1802b73d+641 642 387922 0x1802b676+642 643 388862 0x1802b76a+643 644 389096 0x1802b663+644 645 389179 0x1802b4f4+645 646 390205 0x1802b623+646 647 392842 0x1802bbd9+647 648 393650 0x1802bc70+648 649 395227 0x1802bf2f+649 650 395437 0x1802be17+650 651 396328 0x1802bee9+651 652 396734 0x1802be5d+652 653 397021 0x1802bd7c+653 654 398290 0x1802bf5c+654 655 398351 0x1802bdda+655 656 398352 0x1802bc2a+656 657 398608 0x1802bb30+657 658 400692 0x1802bf5f+658 659 401050 0x1802beb0+659 660 401877 0x1802bf54+660 661 402081 0x1802be35+661 662 402895 0x1802bed1+662 663 403141 0x1802bdd2+663 664 403889 0x1802be3a+664 665 405485 0x1802c10c+665 666 406102 0x1802c11a+666 667 406424 0x1802c050+667 668 407129 0x1802c09b+668 669 408261 0x1802c21e+669 670 411009 0x1802c83d+670 671 411058 0x1802c6aa+671 672 411202 0x1802c55b+672 673 411222 0x1802c3b6+673 674 412528 0x1802c5b9+674 675 413119 0x1802c5b4+675 676 413150 0x1802c414+676 677 413673 0x1802c3db+677 678 414107 0x1802c363+678 679 414707 0x1802c363+679 680 416015 0x1802c566+680 681 416297 0x1802c47f+681 682 416872 0x1802c46c+682 683 417179 0x1802c396+683 684 417346 0x1802c25c+684 685 417907 0x1802c241+685 686 417967 0x1802c0b6+686 687 418431 0x1802c056+687 688 418720 0x1802bf74+688 689 419267 0x1802bf4f+689 690 419422 0x1802be0d+690 691 419535 0x1802bcab+691 692 419837 0x1802bbd4+692 693 420828 0x1802bcee+693 694 421425 0x1802bcee+694 695 421770 0x1802bc37+695 696 422491 0x1802bc8a+696 697 422687 0x1802bb69+697 698 423350 0x1802bb96+698 699 423617 0x1802baa7+699 700 425437 0x1802be17+700 701 425498 0x1802bc92+701 702 426799 0x1802be8b+702 703 430429 0x1802c726+703 704 430913 0x1802c6d3+704 705 432168 0x1802c8b3+705 706 433472 0x1802cab6+706 707 433525 0x1802c924+707 708 434589 0x1802ca78+708 709 434648 0x1802c8eb+709 710 434654 0x1802c739+710 711 436085 0x1802c99a+711 712 436419 0x1802c8d6+712 713 436687 0x1802c7e4+713 714 437526 0x1802c893+714 715 437933 0x1802c805+715 716 439897 0x1802cbf0+716 717 441227 0x1802ce09+717 718 441509 0x1802cd1f+718 719 441573 0x1802cb95+719 720 441768 0x1802ca68+720 721 443123 0x1802cc94+721 722 443312 0x1802cb67+722 723 443356 0x1802c9cf+723 724 445295 0x1802cda8+724 725 446484 0x1802cf5b+725 726 447388 0x1802d03c+726 727 447650 0x1802cf40+727 728 447685 0x1802cda0+728 729 447685 0x1802cbe5+729 730 447846 0x1802caa3+730 731 447887 0x1802c909+731 732 448873 0x1802ca23+732 733 449601 0x1802ca83+733 734 450072 0x1802ca23+734 735 451346 0x1802cc13+735 736 451515 0x1802cad6+736 737 451545 0x1802c934+737 738 451547 0x1802c77e+738 739 451600 0x1802c5ec+739 740 451710 0x1802c48a+740 741 452201 0x1802c439+741 742 452501 0x1802c35d+742 743 454119 0x1802c644+743 744 454121 0x1802c48f+744 745 455736 0x1802c776+745 746 455859 0x1802c619+746 747 456066 0x1802c4fa+747 748 456519 0x1802c48f+748 749 456655 0x1802c33d+749 750 456902 0x1802c23b+750 751 457978 0x1802c396+751 752 458088 0x1802c233+752 753 458522 0x1802c1bb+753 754 458649 0x1802c060+754 755 458776 0x1802bf0c+755 756 459470 0x1802bf4f+756 757 459726 0x1802be55+757 758 460982 0x1802c030+758 759 462187 0x1802c1e8+759 760 462493 0x1802c114+760 761 463504 0x1802c23e+761 762 463910 0x1802c1b0+762 763 464257 0x1802c0f9+763 764 464410 0x1802bfb5+764 765 464528 0x1802be58+765 766 464756 0x1802bd4c+766 767 465137 0x1802bcad+767 768 465417 0x1802bbc9+768 769 465800 0x1802bb2b+769 770 466521 0x1802bb81+770 771 468519 0x1802bf72+771 772 468840 0x1802beab+772 773 468909 0x1802bd29+773 774 469521 0x1802bd33+774 775 469605 0x1802bbbe+775 776 469736 0x1802ba6c+776 777 469906 0x1802b938+777 778 470457 0x1802b915+778 779 470592 0x1802b7c8+779 780 470786 0x1802b6a4+780 781 470867 0x1802b531+781 782 471904 0x1802b66b+782 783 472233 0x1802b5a7+783 784 472413 0x1802b47e+784 785 473428 0x1802b5a5+785 786 474802 0x1802b7ce+786 787 475964 0x1802b963+787 788 476987 0x1802ba92+788 789 477048 0x1802b910+789 790 477122 0x1802b795+790 791 477238 0x1802b63b+791 792 477281 0x1802b4ae+792 793 477855 0x1802b49b+793 794 478989 0x1802b618+794 795 480590 0x1802b8e5+795 796 480594 0x1802b73a+796 797 480743 0x1802b5f8+797 798 481364 0x1802b605+798 799 481456 0x1802b49b+799 800 481721 0x1802b3ac+800 801 482486 0x1802b422+801 802 482910 0x1802b3a4+802 803 482941 0x1802b20f+803 804 482966 0x1802b077+804 805 483761 0x1802b103+805 806 484289 0x1802b0d0+806 807 484520 0x1802afc9+807 808 484641 0x1802ae78+808 809 485590 0x1802af6e+809 810 485727 0x1802ae29+810 811 486746 0x1802af4d+811 812 487753 0x1802b06f+812 813 488318 0x1802b054+813 814 488451 0x1802af0a+814 815 488545 0x1802adaa+815 816 488830 0x1802accb+816 817 489408 0x1802acbc+817 818 489635 0x1802abb8+818 819 489659 0x1802aa24+819 820 490329 0x1802aa56+820 821 492017 0x1802ad4e+821 822 492262 0x1802ac56+822 823 492747 0x1802ac06+823 824 492753 0x1802aa66+824 825 493678 0x1802ab48+825 826 493893 0x1802aa3c+826 827 494049 0x1802a906+827 828 494357 0x1802a83a+828 829 494704 0x1802a789+829 830 494910 0x1802a679+830 831 495801 0x1802a742+831 832 496075 0x1802a65f+832 833 497310 0x1802a819+833 834 498058 0x1802a880+834 835 500404 0x1802ad45+835 836 500831 0x1802accc+836 837 501001 0x1802aba0+837 838 502507 0x1802ae1a+838 839 502709 0x1802ad02+839 840 503289 0x1802acf6+840 841 503410 0x1802aba5+841 842 503585 0x1802aa7d+842 843 503977 0x1802a9ea+843 844 505389 0x1802ac23+844 845 505926 0x1802abf7+845 846 506588 0x1802ac23+846 847 506933 0x1802ab6f+847 848 507014 0x1802aa04+848 849 507175 0x1802a8d3+849 850 507429 0x1802a7e0+850 851 507459 0x1802a653+851 852 507602 0x1802a517+852 853 507816 0x1802a40c+853 854 508462 0x1802a42b+854 855 508502 0x1802a2a8+855 856 509225 0x1802a2fd+856 857 509685 0x1802a29c+857 858 510040 0x1802a1f2+858 859 510381 0x1802a13f+859 860 510483 0x18029fe8+860 861 510760 0x18029f0a+861 862 510856 0x18029daf+862 863 513127 0x1802a22e+863 864 513536 0x1802a1ab+864 865 513564 0x1802a020+865 866 513968 0x18029f9a+866 867 514016 0x18029e1d+867 868 514416 0x18029d94+868 869 514964 0x18029d71+869 870 515313 0x18029cc4+870 871 516450 0x18029e35+871 872 516713 0x18029d4d+872 873 516741 0x18029bc5+873 874 516934 0x18029ab0+874 875 517227 0x180299de+875 876 517336 0x1802988e+876 877 517910 0x1802987c+877 878 517947 0x180296fe+878 879 518259 0x1802963a+879 880 518613 0x18029592+880 881 518711 0x1802943f+881 882 518715 0x180292ac+882 883 519175 0x1802924d+883 884 519234 0x180290e0+884 885 520514 0x180292ac+885 886 520866 0x18029205+886 887 522826 0x1802959c+887 888 523988 0x18029719+888 889 524155 0x180295f3+889 890 524618 0x18029596+890 891 525169 0x18029576+891 892 527140 0x1802991a+892 893 527335 0x18029806+893 894 527959 0x18029816+894 895 528204 0x18029725+895 896 528856 0x18029748+896 897 529145 0x18029674+897 898 529809 0x180296a1+898 899 529832 0x18029518+899 900 529900 0x180293b0+900 901 530252 0x18029309+901 902 530363 0x180291bd+902 903 530792 0x1802914b+903 904 531419 0x1802915d+904 905 531553 0x18029023+905 906 531867 0x18028f63+906 907 532051 0x18028e4b+907 908 533275 0x18028fef+908 909 533769 0x18028fa6+909 910 534468 0x18028feb+910 911 534494 0x18028e68+911 912 535858 0x1802906a+912 913 536485 0x1802907b+913 914 537936 0x180292ba+914 915 538082 0x18029188+915 916 538508 0x18029113+916 917 538754 0x18029023+917 918 539975 0x180291c5+918 919 540307 0x18029110+919 920 540541 0x1802901c+920 921 541484 0x18029103+921 922 541637 0x18028fd5+922 923 541982 0x18028f28+923 924 542150 0x18028e08+924 925 542551 0x18028d81+925 926 545123 0x180292b2+926 927 545167 0x1802913a+927 928 545556 0x180290ac+928 929 545588 0x18028f2d+929 930 545634 0x18028dba+930 931 545831 0x18028cac+931 932 546487 0x18028cd2+932 933 546691 0x18028bc8+933 934 546703 0x18028a40+934 935 547152 0x180289dc+935 936 547189 0x18028864+936 937 547424 0x18028771+937 938 547627 0x18028669+938 939 547649 0x180284ea+939 940 547720 0x1802838c+940 941 551263 0x18028b2f+941 942 551730 0x18028ad7+942 943 551767 0x1802895f+943 944 551984 0x18028860+944 945 552234 0x18028777+945 946 553696 0x180289b5+946 947 553905 0x180288b0+947 948 554812 0x1802897c+948 949 554989 0x18028864+949 950 555619 0x18028877+950 951 556119 0x18028834+951 952 557125 0x18028943+952 953 557238 0x180287ff+953 954 557459 0x18028703+954 955 557592 0x180285cd+955 956 558854 0x18028785+956 957 559950 0x180288ce+957 958 560504 0x180288b0+958 959 561621 0x18028a09+959 960 563620 0x18028daf+960 961 563879 0x18028ccc+961 962 563942 0x18028b63+962 963 564154 0x18028a60+963 964 565325 0x18028bdf+964 965 565451 0x18028aa2+965 966 565566 0x1802895f+966 967 565635 0x180287fe+967 968 566460 0x18028893+968 969 567692 0x18028a38+969 970 568536 0x18028adb+970 971 568724 0x180289c8+971 972 569029 0x18028904+972 973 569348 0x18028848+973 974 569672 0x18028791+974 975 571368 0x18028a6a+975 976 571742 0x180289d5+976 977 572100 0x18028933+977 978 572426 0x1802887c+978 979 572436 0x180286f3+979 980 573333 0x180287ba+980 981 573363 0x1802863f+981 982 573856 0x180285f8+982 983 576984 0x18028c8c+983 984 577936 0x18028d77+984 985 578576 0x18028d93+985 986 580110 0x18029007+986 987 581045 0x180290e8+987 988 582554 0x1802934d+988 989 583197 0x1802936b+989 990 583323 0x1802922a+990 991 583647 0x1802916e+991 992 583905 0x1802908a+992 993 586689 0x1802964e+993 994 586926 0x18029557+994 995 587291 0x180294b7+995 996 587688 0x1802942f+996 997 587802 0x180292e6+997 998 588403 0x180292e6+998 999 588544 0x180291b0+999 1000 589215 0x180291e0+1000 1001 589738 0x180291ad+
+ data/test_vectors_aserti3-2d_run07.txt view
@@ -0,0 +1,1009 @@+## description: run7 - deterministically random solvetimes for up-ramping hashrate around a recent real life nBits+##   anchor height: 1+##   anchor parent time: 0+##   anchor nBits: 0x1802aee8+##   start height: 2+##   start time: 1200+##   iterations: 1000+# iteration,height,time,target+1 2 1200 0x1802aee8+2 3 1310 0x1802ad91+3 4 1327 0x1802abf8+4 5 1739 0x1802ab73+5 6 2219 0x1802ab20+6 7 2604 0x1802aa89+7 8 2746 0x1802a94b+8 9 7099 0x1802b39c+9 10 7099 0x1802b1f2+10 11 7657 0x1802b1d4+11 12 8141 0x1802b181+12 13 8236 0x1802b01c+13 14 8644 0x1802af93+14 15 9230 0x1802af8b+15 16 9691 0x1802af28+16 17 9708 0x1802ad8f+17 18 10327 0x1802ad9c+18 19 10899 0x1802ad88+19 20 11478 0x1802ad7b+20 21 11548 0x1802ac07+21 22 12701 0x1802ad89+22 23 12849 0x1802ac4d+23 24 13186 0x1802ab95+24 25 13319 0x1802aa4e+25 26 13494 0x1802a925+26 27 13540 0x1802a7a4+27 28 13726 0x1802a684+28 29 13764 0x1802a4fd+29 30 13787 0x1802a36d+30 31 14159 0x1802a2cf+31 32 14203 0x1802a14e+32 33 14474 0x1802a06d+33 34 14582 0x18029f19+34 35 14741 0x18029dea+35 36 14971 0x18029ceb+36 37 15181 0x18029be0+37 38 15402 0x18029adf+38 39 15886 0x18029a8e+39 40 16041 0x1802995f+40 41 16219 0x1802983e+41 42 16407 0x18029727+42 43 16513 0x180295d7+43 44 16633 0x18029491+44 45 16698 0x18029326+45 46 16755 0x180291b8+46 47 16782 0x18029036+47 48 16795 0x18028eab+48 49 16869 0x18028d4a+49 50 16870 0x18028bbb+50 51 16876 0x18028a2d+51 52 16888 0x180288a6+52 53 16958 0x18028746+53 54 17306 0x1802869d+54 55 17410 0x18028556+55 56 17474 0x180283f4+56 57 17580 0x180282ac+57 58 17640 0x18028147+58 59 17652 0x18027fc5+59 60 17694 0x18027e58+60 61 17772 0x18027d02+61 62 17817 0x18027b98+62 63 17923 0x18027a56+63 64 17965 0x180278ea+64 65 17982 0x18027770+65 66 18116 0x18027642+66 67 18236 0x1802750c+67 68 18236 0x1802738a+68 69 18237 0x18027209+69 70 18303 0x180270b0+70 71 18332 0x18026f43+71 72 18404 0x18026df1+72 73 18404 0x18026c72+73 74 18504 0x18026b35+74 75 18587 0x180269ec+75 76 18618 0x18026883+76 77 18641 0x18026715+77 78 18649 0x180265a1+78 79 18650 0x18026428+79 80 18658 0x180262b3+80 81 18824 0x180261a4+81 82 18830 0x1802602f+82 83 18861 0x18025ecd+83 84 18865 0x18025d5b+84 85 18927 0x18025c0d+85 86 18987 0x18025abd+86 87 19001 0x18025952+87 88 19059 0x18025804+88 89 19308 0x1802572c+89 90 19316 0x180255bf+90 91 19333 0x1802545a+91 92 19378 0x18025306+92 93 19559 0x18025206+93 94 19658 0x180250d6+94 95 19663 0x18024f6a+95 96 19746 0x18024e30+96 97 19917 0x18024d2c+97 98 20213 0x18024c74+98 99 20250 0x18024b1f+99 100 20363 0x180249fb+100 101 20372 0x18024899+101 102 20488 0x18024775+102 103 20529 0x18024626+103 104 20555 0x180244cf+104 105 20606 0x18024387+105 106 20643 0x18024239+106 107 20749 0x18024113+107 108 20772 0x18023fbd+108 109 20782 0x18023e62+109 110 20819 0x18023d15+110 111 20845 0x18023bc2+111 112 20935 0x18023a98+112 113 20945 0x1802393e+113 114 20963 0x180237ea+114 115 20966 0x1802368d+115 116 21080 0x18023574+116 117 21122 0x1802342f+117 118 21126 0x180232d5+118 119 21145 0x18023187+119 120 21158 0x18023033+120 121 21170 0x18022ee1+121 122 21173 0x18022d8b+122 123 21186 0x18022c3a+123 124 21221 0x18022af8+124 125 21227 0x180229a6+125 126 21521 0x180228f8+126 127 21547 0x180227b2+127 128 21656 0x1802269c+128 129 21665 0x1802254d+129 130 21686 0x18022407+130 131 21708 0x180222c2+131 132 21715 0x18022174+132 133 21790 0x1802204e+133 134 21885 0x18021f33+134 135 21918 0x18021df8+135 136 21985 0x18021ccf+136 137 22011 0x18021b90+137 138 22033 0x18021a4f+138 139 22112 0x1802192f+139 140 22130 0x180217f0+140 141 22184 0x180216c3+141 142 22222 0x1802158f+142 143 22241 0x1802144f+143 144 22262 0x18021314+144 145 22263 0x180211cd+145 146 22266 0x18021088+146 147 22281 0x18020f4a+147 148 22305 0x18020e13+148 149 22321 0x18020cd7+149 150 22346 0x18020ba2+150 151 22358 0x18020a65+151 152 22358 0x18020924+152 153 22375 0x180207ec+153 154 22397 0x180206b7+154 155 22528 0x180205be+155 156 22647 0x180204bd+156 157 22647 0x1802037f+157 158 22659 0x18020248+158 159 22691 0x1802011d+159 160 22709 0x1801ffea+160 161 22749 0x1801fec4+161 162 22760 0x1801fd8e+162 163 22775 0x1801fc5d+163 164 22790 0x1801fb2c+164 165 22797 0x1801f9f7+165 166 22800 0x1801f8c0+166 167 22823 0x1801f797+167 168 22862 0x1801f674+168 169 22883 0x1801f549+169 170 22891 0x1801f41a+170 171 23016 0x1801f326+171 172 23025 0x1801f1f6+172 173 23070 0x1801f0db+173 174 23103 0x1801efb9+174 175 23116 0x1801ee8e+175 176 23201 0x1801ed8a+176 177 23269 0x1801ec7c+177 178 23307 0x1801eb60+178 179 23395 0x1801ea5e+179 180 23397 0x1801e930+180 181 23457 0x1801e821+181 182 23488 0x1801e705+182 183 23534 0x1801e5ef+183 184 23535 0x1801e4c5+184 185 23550 0x1801e3a2+185 186 23686 0x1801e2bc+186 187 23700 0x1801e19a+187 188 23872 0x1801e0c7+188 189 23879 0x1801dfa3+189 190 23888 0x1801de80+190 191 23908 0x1801dd63+191 192 23971 0x1801dc5c+192 193 24055 0x1801db5f+193 194 24085 0x1801da49+194 195 24089 0x1801d927+195 196 24103 0x1801d80c+196 197 24190 0x1801d712+197 198 24230 0x1801d605+198 199 24254 0x1801d4ee+199 200 24330 0x1801d3f3+200 201 24359 0x1801d2e0+201 202 24391 0x1801d1d1+202 203 24406 0x1801d0ba+203 204 24431 0x1801cfa7+204 205 24437 0x1801ce8d+205 206 24464 0x1801cd7d+206 207 24464 0x1801cc62+207 208 24493 0x1801cb54+208 209 24531 0x1801ca4a+209 210 24563 0x1801c941+210 211 24571 0x1801c82b+211 212 24602 0x1801c720+212 213 24633 0x1801c616+213 214 24714 0x1801c526+214 215 24748 0x1801c41e+215 216 24749 0x1801c309+216 217 24807 0x1801c20d+217 218 24815 0x1801c0fd+218 219 24817 0x1801bfea+219 220 24875 0x1801bef0+220 221 24939 0x1801bdfb+221 222 24942 0x1801bcea+222 223 24953 0x1801bbdd+223 224 24968 0x1801bad3+224 225 25071 0x1801b9f2+225 226 25127 0x1801b8fc+226 227 25174 0x1801b801+227 228 25182 0x1801b6f6+228 229 25184 0x1801b5ea+229 230 25193 0x1801b4e0+230 231 25197 0x1801b3d5+231 232 25211 0x1801b2d0+232 233 25227 0x1801b1cc+233 234 25231 0x1801b0c3+234 235 25274 0x1801afcb+235 236 25289 0x1801aec8+236 237 25296 0x1801adc4+237 238 25334 0x1801accc+238 239 25362 0x1801abcf+239 240 25374 0x1801aace+240 241 25464 0x1801a9ef+241 242 25531 0x1801a907+242 243 25532 0x1801a803+243 244 25609 0x1801a71f+244 245 25609 0x1801a61c+245 246 25672 0x1801a532+246 247 25737 0x1801a44c+247 248 25760 0x1801a353+248 249 25765 0x1801a254+249 250 25770 0x1801a154+250 251 25790 0x1801a05c+251 252 25796 0x18019f60+252 253 25885 0x18019e86+253 254 25925 0x18019d99+254 255 25927 0x18019c9b+255 256 25930 0x18019b9e+256 257 25930 0x18019aa2+257 258 25963 0x180199b3+258 259 25966 0x180198b8+259 260 26000 0x180197cc+260 261 26026 0x180196dc+261 262 26048 0x180195ea+262 263 26052 0x180194f3+263 264 26118 0x18019415+264 265 26119 0x1801931e+265 266 26122 0x18019227+266 267 26129 0x18019133+267 268 26148 0x18019043+268 269 26150 0x18018f4e+269 270 26153 0x18018e5a+270 271 26175 0x18018d6e+271 272 26186 0x18018c7f+272 273 26243 0x18018ba2+273 274 26244 0x18018aaf+274 275 26247 0x180189be+275 276 26252 0x180188cd+276 277 26252 0x180187dc+277 278 26264 0x180186f0+278 279 26290 0x1801860a+279 280 26316 0x18018525+280 281 26351 0x18018444+281 282 26395 0x18018366+282 283 26402 0x1801827a+283 284 26420 0x18018195+284 285 26508 0x180180ca+285 286 26522 0x18017fe2+286 287 26552 0x18017f02+287 288 26572 0x18017e1f+288 289 26593 0x18017d3d+289 290 26618 0x18017c5c+290 291 26633 0x18017b77+291 292 26675 0x18017a9e+292 293 26711 0x180179c3+293 294 26719 0x180178df+294 295 26719 0x180177f6+295 296 26738 0x18017717+296 297 26769 0x1801763c+297 298 26831 0x1801756e+298 299 26846 0x1801748d+299 300 26847 0x180173a8+300 301 26908 0x180172db+301 302 26909 0x180171f7+302 303 26964 0x18017128+303 304 26997 0x18017050+304 305 27034 0x18016f7c+305 306 27043 0x18016e9d+306 307 27058 0x18016dc1+307 308 27064 0x18016ce3+308 309 27084 0x18016c09+309 310 27088 0x18016b2b+310 311 27144 0x18016a60+311 312 27172 0x1801698b+312 313 27188 0x180168b3+313 314 27241 0x180167e8+314 315 27250 0x1801670d+315 316 27250 0x18016630+316 317 27286 0x18016562+317 318 27287 0x18016485+318 319 27295 0x180163ac+319 320 27304 0x180162d4+320 321 27315 0x180161fd+321 322 27321 0x18016125+322 323 27340 0x18016053+323 324 27361 0x18015f80+324 325 27372 0x18015eac+325 326 27406 0x18015de0+326 327 27428 0x18015d10+327 328 27435 0x18015c3b+328 329 27455 0x18015b6b+329 330 27465 0x18015a9a+330 331 27474 0x180159c6+331 332 27490 0x180158f7+332 333 27493 0x18015822+333 334 27501 0x18015751+334 335 27504 0x18015680+335 336 27522 0x180155b4+336 337 27548 0x180154eb+337 338 27559 0x1801541e+338 339 27561 0x1801534e+339 340 27570 0x18015280+340 341 27582 0x180151b4+341 342 27608 0x180150ee+342 343 27613 0x18015021+343 344 27633 0x18014f59+344 345 27637 0x18014e8c+345 346 27645 0x18014dc2+346 347 27647 0x18014cf5+347 348 27648 0x18014c29+348 349 27670 0x18014b64+349 350 27705 0x18014aa4+350 351 27716 0x180149dd+351 352 27745 0x1801491c+352 353 27753 0x18014854+353 354 27769 0x18014790+354 355 27779 0x180146cb+355 356 27789 0x18014605+356 357 27792 0x1801453d+357 358 27795 0x18014476+358 359 27848 0x180143c0+359 360 27866 0x180142ff+360 361 27867 0x18014239+361 362 27889 0x1801417a+362 363 27894 0x180140b6+363 364 27909 0x18013ff6+364 365 27914 0x18013f32+365 366 27942 0x18013e77+366 367 27945 0x18013db4+367 368 27966 0x18013cf7+368 369 27977 0x18013c38+369 370 27978 0x18013b76+370 371 27983 0x18013ab5+371 372 27983 0x180139f4+372 373 27993 0x18013936+373 374 28003 0x18013878+374 375 28017 0x180137bd+375 376 28020 0x180136fd+376 377 28022 0x1801363f+377 378 28055 0x1801358b+378 379 28060 0x180134cd+379 380 28084 0x18013418+380 381 28085 0x18013359+381 382 28100 0x180132a1+382 383 28113 0x180131e9+383 384 28133 0x18013132+384 385 28181 0x18013086+385 386 28181 0x18012fca+386 387 28186 0x18012f10+387 388 28251 0x18012e6a+388 389 28264 0x18012db4+389 390 28271 0x18012cfc+390 391 28276 0x18012c45+391 392 28287 0x18012b8f+392 393 28290 0x18012ad7+393 394 28299 0x18012a22+394 395 28315 0x1801296f+395 396 28328 0x180128bc+396 397 28342 0x1801280a+397 398 28343 0x18012754+398 399 28360 0x180126a3+399 400 28361 0x180125ed+400 401 28367 0x1801253b+401 402 28376 0x18012489+402 403 28377 0x180123d5+403 404 28390 0x18012325+404 405 28396 0x18012274+405 406 28401 0x180121c2+406 407 28406 0x18012112+407 408 28432 0x18012067+408 409 28453 0x18011fbb+409 410 28454 0x18011f0a+410 411 28483 0x18011e63+411 412 28484 0x18011db2+412 413 28502 0x18011d07+413 414 28503 0x18011c58+414 415 28509 0x18011baa+415 416 28534 0x18011b03+416 417 28539 0x18011a56+417 418 28539 0x180119a9+418 419 28570 0x18011904+419 420 28580 0x18011859+420 421 28589 0x180117af+421 422 28599 0x18011706+422 423 28602 0x1801165b+423 424 28609 0x180115b1+424 425 28626 0x1801150b+425 426 28667 0x1801146c+426 427 28683 0x180113c7+427 428 28711 0x18011325+428 429 28716 0x1801127d+429 430 28778 0x180111e5+430 431 28779 0x1801113d+431 432 28791 0x18011098+432 433 28791 0x18010fef+433 434 28798 0x18010f49+434 435 28805 0x18010ea4+435 436 28811 0x18010dff+436 437 28857 0x18010d66+437 438 28872 0x18010cc4+438 439 28876 0x18010c1f+439 440 28881 0x18010b7b+440 441 28890 0x18010ad9+441 442 28911 0x18010a3b+442 443 28912 0x18010997+443 444 28920 0x180108f5+444 445 28923 0x18010854+445 446 28939 0x180107b5+446 447 28958 0x18010718+447 448 28971 0x18010679+448 449 28976 0x180105d8+449 450 28978 0x18010538+450 451 28985 0x18010499+451 452 28997 0x180103fc+452 453 29002 0x1801035d+453 454 29003 0x180102bd+454 455 29013 0x18010221+455 456 29015 0x18010182+456 457 29025 0x180100e6+457 458 29033 0x1801004a+458 459 29064 0x1800ffb4+459 460 29068 0x1800ff18+460 461 29070 0x1800fe7b+461 462 29074 0x1800fddf+462 463 29102 0x1800fd4a+463 464 29112 0x1800fcb1+464 465 29120 0x1800fc17+465 466 29131 0x1800fb80+466 467 29135 0x1800fae5+467 468 29144 0x1800fa4e+468 469 29171 0x1800f9ba+469 470 29202 0x1800f929+470 471 29215 0x1800f892+471 472 29228 0x1800f7fc+472 473 29235 0x1800f765+473 474 29237 0x1800f6ce+474 475 29239 0x1800f637+475 476 29245 0x1800f5a0+476 477 29260 0x1800f50d+477 478 29261 0x1800f476+478 479 29275 0x1800f3e3+479 480 29290 0x1800f351+480 481 29319 0x1800f2c2+481 482 29353 0x1800f235+482 483 29354 0x1800f1a1+483 484 29354 0x1800f10c+484 485 29380 0x1800f07e+485 486 29384 0x1800efeb+486 487 29384 0x1800ef57+487 488 29420 0x1800eecc+488 489 29425 0x1800ee3b+489 490 29433 0x1800edaa+490 491 29447 0x1800ed1b+491 492 29469 0x1800ec8f+492 493 29472 0x1800ebfe+493 494 29495 0x1800eb72+494 495 29497 0x1800eae2+495 496 29498 0x1800ea51+496 497 29517 0x1800e9c6+497 498 29534 0x1800e93a+498 499 29534 0x1800e8aa+499 500 29547 0x1800e81f+500 501 29563 0x1800e793+501 502 29608 0x1800e710+502 503 29608 0x1800e682+503 504 29617 0x1800e5f6+504 505 29633 0x1800e56d+505 506 29637 0x1800e4e0+506 507 29643 0x1800e454+507 508 29679 0x1800e3d1+508 509 29686 0x1800e346+509 510 29688 0x1800e2bb+510 511 29704 0x1800e233+511 512 29707 0x1800e1a9+512 513 29715 0x1800e120+513 514 29732 0x1800e099+514 515 29743 0x1800e012+515 516 29754 0x1800df8a+516 517 29760 0x1800df02+517 518 29760 0x1800de79+518 519 29791 0x1800ddf8+519 520 29795 0x1800dd70+520 521 29804 0x1800dce9+521 522 29808 0x1800dc62+522 523 29813 0x1800dbdc+523 524 29866 0x1800db61+524 525 29888 0x1800dadf+525 526 29892 0x1800da59+526 527 29904 0x1800d9d6+527 528 29904 0x1800d950+528 529 29904 0x1800d8ca+529 530 29919 0x1800d849+530 531 29928 0x1800d7c5+531 532 29929 0x1800d741+532 533 29933 0x1800d6bd+533 534 29936 0x1800d63a+534 535 29976 0x1800d5bf+535 536 29983 0x1800d53d+536 537 30004 0x1800d4be+537 538 30004 0x1800d43c+538 539 30013 0x1800d3bb+539 540 30017 0x1800d33b+540 541 30033 0x1800d2bc+541 542 30042 0x1800d23c+542 543 30066 0x1800d1c0+543 544 30084 0x1800d143+544 545 30089 0x1800d0c3+545 546 30098 0x1800d045+546 547 30118 0x1800cfc9+547 548 30120 0x1800cf4a+548 549 30136 0x1800cece+549 550 30138 0x1800ce4f+550 551 30164 0x1800cdd6+551 552 30240 0x1800cd68+552 553 30244 0x1800cceb+553 554 30247 0x1800cc6c+554 555 30288 0x1800cbf8+555 556 30296 0x1800cb7c+556 557 30297 0x1800caff+557 558 30337 0x1800ca8b+558 559 30369 0x1800ca15+559 560 30387 0x1800c99d+560 561 30388 0x1800c921+561 562 30395 0x1800c8a7+562 563 30398 0x1800c82c+563 564 30405 0x1800c7b2+564 565 30427 0x1800c73c+565 566 30429 0x1800c6c2+566 567 30437 0x1800c649+567 568 30481 0x1800c5d8+568 569 30492 0x1800c561+569 570 30508 0x1800c4eb+570 571 30510 0x1800c472+571 572 30526 0x1800c3fd+572 573 30530 0x1800c385+573 574 30532 0x1800c30d+574 575 30589 0x1800c2a0+575 576 30594 0x1800c22a+576 577 30608 0x1800c1b6+577 578 30615 0x1800c140+578 579 30620 0x1800c0ca+579 580 30628 0x1800c055+580 581 30629 0x1800bfdf+581 582 30632 0x1800bf6a+582 583 30632 0x1800bef4+583 584 30648 0x1800be82+584 585 30662 0x1800be0f+585 586 30663 0x1800bd9a+586 587 30669 0x1800bd27+587 588 30674 0x1800bcb4+588 589 30674 0x1800bc40+589 590 30696 0x1800bbd0+590 591 30699 0x1800bb5d+591 592 30708 0x1800baec+592 593 30715 0x1800ba79+593 594 30721 0x1800ba08+594 595 30750 0x1800b99b+595 596 30750 0x1800b929+596 597 30752 0x1800b8b7+597 598 30758 0x1800b847+598 599 30758 0x1800b7d5+599 600 30765 0x1800b765+600 601 30769 0x1800b6f5+601 602 30771 0x1800b685+602 603 30789 0x1800b619+603 604 30790 0x1800b5a8+604 605 30799 0x1800b53a+605 606 30801 0x1800b4ca+606 607 30801 0x1800b45c+607 608 30804 0x1800b3ec+608 609 30805 0x1800b37e+609 610 30815 0x1800b311+610 611 30818 0x1800b2a4+611 612 30829 0x1800b238+612 613 30830 0x1800b1ca+613 614 30834 0x1800b15d+614 615 30835 0x1800b0f0+615 616 30857 0x1800b087+616 617 30862 0x1800b01b+617 618 30871 0x1800afb0+618 619 30873 0x1800af44+619 620 30896 0x1800aedc+620 621 30910 0x1800ae72+621 622 30911 0x1800ae07+622 623 30920 0x1800ad9d+623 624 30924 0x1800ad33+624 625 30927 0x1800acc9+625 626 30929 0x1800ac5f+626 627 30974 0x1800abfc+627 628 30974 0x1800ab92+628 629 30978 0x1800ab2a+629 630 30995 0x1800aac4+630 631 30995 0x1800aa5a+631 632 30995 0x1800a9f2+632 633 30995 0x1800a98a+633 634 30999 0x1800a922+634 635 31009 0x1800a8bc+635 636 31012 0x1800a855+636 637 31014 0x1800a7ee+637 638 31020 0x1800a788+638 639 31020 0x1800a721+639 640 31031 0x1800a6bc+640 641 31032 0x1800a656+641 642 31036 0x1800a5f0+642 643 31050 0x1800a58c+643 644 31053 0x1800a527+644 645 31054 0x1800a4c2+645 646 31069 0x1800a45f+646 647 31109 0x1800a401+647 648 31121 0x1800a39e+648 649 31145 0x1800a33d+649 650 31148 0x1800a2da+650 651 31161 0x1800a278+651 652 31167 0x1800a215+652 653 31171 0x1800a1b2+653 654 31190 0x1800a151+654 655 31190 0x1800a0ee+655 656 31190 0x1800a08b+656 657 31193 0x1800a029+657 658 31224 0x18009fcc+658 659 31229 0x18009f6a+659 660 31241 0x18009f0a+660 661 31244 0x18009ea9+661 662 31256 0x18009e49+662 663 31259 0x18009de8+663 664 31270 0x18009d89+664 665 31293 0x18009d2c+665 666 31302 0x18009ccc+666 667 31306 0x18009c6c+667 668 31316 0x18009c0e+668 669 31332 0x18009bb0+669 670 31373 0x18009b57+670 671 31373 0x18009af7+671 672 31375 0x18009a98+672 673 31375 0x18009a39+673 674 31394 0x180099de+674 675 31402 0x18009980+675 676 31402 0x18009921+676 677 31409 0x180098c5+677 678 31415 0x18009867+678 679 31423 0x1800980b+679 680 31442 0x180097b0+680 681 31446 0x18009753+681 682 31454 0x180096f7+682 683 31458 0x1800969b+683 684 31460 0x1800963f+684 685 31468 0x180095e3+685 686 31468 0x18009587+686 687 31474 0x1800952c+687 688 31478 0x180094d1+688 689 31485 0x18009476+689 690 31487 0x1800941b+690 691 31488 0x180093c0+691 692 31492 0x18009365+692 693 31506 0x1800930d+693 694 31514 0x180092b3+694 695 31518 0x18009259+695 696 31528 0x18009201+696 697 31530 0x180091a7+697 698 31539 0x1800914f+698 699 31542 0x180090f6+699 700 31568 0x180090a0+700 701 31568 0x18009047+701 702 31586 0x18008ff1+702 703 31637 0x18008fa0+703 704 31643 0x18008f48+704 705 31660 0x18008ef2+705 706 31678 0x18008e9d+706 707 31678 0x18008e45+707 708 31692 0x18008df0+708 709 31692 0x18008d98+709 710 31692 0x18008d41+710 711 31712 0x18008ced+711 712 31716 0x18008c97+712 713 31719 0x18008c40+713 714 31730 0x18008bec+714 715 31735 0x18008b96+715 716 31762 0x18008b44+716 717 31780 0x18008af1+717 718 31783 0x18008a9c+718 719 31783 0x18008a46+719 720 31785 0x180089f1+720 721 31803 0x1800899f+721 722 31805 0x1800894a+722 723 31805 0x180088f6+723 724 31831 0x180088a5+724 725 31847 0x18008853+725 726 31859 0x18008801+726 727 31862 0x180087ae+727 728 31862 0x1800875a+728 729 31862 0x18008707+729 730 31864 0x180086b4+730 731 31864 0x18008660+731 732 31877 0x1800860f+732 733 31886 0x180085be+733 734 31892 0x1800856d+734 735 31909 0x1800851d+735 736 31911 0x180084cb+736 737 31911 0x18008479+737 738 31911 0x18008428+738 739 31911 0x180083d6+739 740 31912 0x18008385+740 741 31918 0x18008335+741 742 31922 0x180082e5+742 743 31943 0x18008297+743 744 31943 0x18008247+744 745 31964 0x180081f9+745 746 31965 0x180081a9+746 747 31967 0x1800815a+747 748 31973 0x1800810b+748 749 31974 0x180080bb+749 750 31977 0x1800806c+750 751 31991 0x1800801f+751 752 31992 0x177fd0f4+752 753 31997 0x177f82cd+753 754 31998 0x177f3451+754 755 31999 0x177ee62b+755 756 32008 0x177e995d+756 757 32011 0x177e4b8c+757 758 32027 0x177e0015+758 759 32042 0x177db448+759 760 32046 0x177d6779+760 761 32059 0x177d1bac+761 762 32064 0x177ccf89+762 763 32068 0x177c8366+763 764 32069 0x177c36ed+764 765 32070 0x177bea1f+765 766 32072 0x177b9e52+766 767 32076 0x177b52da+767 768 32079 0x177b070d+768 769 32083 0x177abbec+769 770 32092 0x177a7176+770 771 32117 0x177a295a+771 772 32121 0x1779de8e+772 773 32121 0x1779936d+773 774 32128 0x1779494d+774 775 32129 0x1778fed7+775 776 32130 0x1778b4b8+776 777 32132 0x17786a98+777 778 32139 0x17782124+778 779 32140 0x1777d75a+779 780 32142 0x17778de6+780 781 32143 0x17774472+781 782 32156 0x1776fd01+782 783 32160 0x1776b3e3+783 784 32162 0x17766b70+784 785 32174 0x177623ff+785 786 32191 0x1775dd3a+786 787 32205 0x1775961f+787 788 32217 0x17754f5a+788 789 32217 0x1775073d+789 790 32217 0x1774bf21+790 791 32218 0x1774775a+791 792 32218 0x17742f93+792 793 32225 0x1773e97a+793 794 32239 0x1773a360+794 795 32259 0x17735e9e+795 796 32259 0x177317d9+796 797 32260 0x1772d114+797 798 32267 0x17728b51+798 799 32268 0x17724537+799 800 32271 0x1771ff1e+800 801 32280 0x1771ba06+801 802 32285 0x17717498+802 803 32285 0x17712ed5+803 804 32285 0x1770e911+804 805 32294 0x1770a4a5+805 806 32300 0x17706039+806 807 32302 0x17701b77+807 808 32303 0x176fd6b5+808 809 32314 0x176f92f5+809 810 32315 0x176f4e88+810 811 32327 0x176f0b74+811 812 32339 0x176ec85f+812 813 32345 0x176e84f5+813 814 32346 0x176e4134+814 815 32347 0x176dfd74+815 816 32350 0x176dba09+816 817 32357 0x176d774b+817 818 32359 0x176d348c+818 819 32359 0x176cf177+819 820 32367 0x176caf0e+820 821 32387 0x176c6ea8+821 822 32389 0x176c2c3f+822 823 32394 0x176bea2c+823 824 32394 0x176ba7c3+824 825 32405 0x176b6708+825 826 32407 0x176b24f5+826 827 32408 0x176ae338+827 828 32411 0x176aa226+828 829 32415 0x176a6115+829 830 32417 0x176a1fad+830 831 32427 0x1769df9d+831 832 32430 0x17699ee2+832 833 32444 0x17695f28+833 834 32452 0x17691f6e+834 835 32479 0x1768e1b7+835 836 32484 0x1768a1fd+836 837 32486 0x17686197+837 838 32503 0x1768238a+838 839 32505 0x1767e37b+839 840 32511 0x1767a46c+840 841 32512 0x176764b2+841 842 32514 0x1767254e+842 843 32518 0x1766e696+843 844 32534 0x1766a8df+844 845 32540 0x17666a7c+845 846 32547 0x17662c1a+846 847 32551 0x1765edb7+847 848 32551 0x1765af54+848 849 32552 0x1765709c+849 850 32554 0x176532e5+850 851 32554 0x1764f483+851 852 32555 0x1764b676+852 853 32557 0x176478bf+853 854 32564 0x17643bb4+854 855 32564 0x1763fe53+855 856 32572 0x1763c19e+856 857 32577 0x176384e8+857 858 32581 0x176347dd+858 859 32584 0x17630b28+859 860 32585 0x1762ce73+860 861 32588 0x176291be+861 862 32589 0x1762555e+862 863 32615 0x17621bae+863 864 32619 0x1761dffa+864 865 32619 0x1761a39b+865 866 32623 0x176167e7+866 867 32623 0x17612bdd+867 868 32627 0x1760f080+868 869 32633 0x1760b5ce+869 870 32637 0x17607ac6+870 871 32650 0x17604069+871 872 32652 0x176005b7+872 873 32652 0x175fcaaf+873 874 32654 0x175f8fa8+874 875 32657 0x175f554b+875 876 32658 0x175f1aef+876 877 32664 0x175ee0e9+877 878 32664 0x175ea68d+878 879 32667 0x175e6c86+879 880 32671 0x175e32d6+880 881 32672 0x175df925+881 882 32672 0x175dbf75+882 883 32677 0x175d861a+883 884 32677 0x175d4c6a+884 885 32691 0x175d1466+885 886 32694 0x175cdbb8+886 887 32716 0x175ca4b6+887 888 32729 0x175c6cb3+888 889 32730 0x175c3404+889 890 32735 0x175bfbab+890 891 32741 0x175bc3a8+891 892 32762 0x175b8cfc+892 893 32764 0x175b54f9+893 894 32770 0x175b1d4c+894 895 32772 0x175ae548+895 896 32779 0x175aadf1+896 897 32782 0x175a7699+897 898 32789 0x175a3f42+898 899 32789 0x175a07eb+899 900 32789 0x1759d03d+900 901 32792 0x1759993c+901 902 32793 0x1759623a+902 903 32797 0x17592b39+903 904 32803 0x1758f4e3+904 905 32804 0x1758be37+905 906 32807 0x175887e1+906 907 32809 0x1758518b+907 908 32822 0x17581c37+908 909 32827 0x1757e637+909 910 32834 0x1757b0e3+910 911 32834 0x17577a8d+911 912 32848 0x1757463a+912 913 32854 0x17571090+913 914 32869 0x1756dc93+914 915 32870 0x1756a693+915 916 32874 0x17567195+916 917 32876 0x17563c97+917 918 32889 0x17560844+918 919 32892 0x1755d3c6+919 920 32894 0x17559f49+920 921 32904 0x17556b77+921 922 32905 0x17553724+922 923 32908 0x175502fc+923 924 32909 0x1754ced5+924 925 32913 0x17549b2e+925 926 32940 0x1754698a+926 927 32940 0x175435b8+927 928 32944 0x1754023c+928 929 32944 0x1753cec0+929 930 32944 0x17539b44+930 931 32946 0x175367f3+931 932 32952 0x1753354e+932 933 32954 0x17530252+933 934 32954 0x1752cf57+934 935 32958 0x17529cdd+935 936 32958 0x17526a0c+936 937 32960 0x17523767+937 938 32962 0x17520542+938 939 32962 0x1751d2c8+939 940 32962 0x1751a04e+940 941 32999 0x17517159+941 942 33003 0x17513fb5+942 943 33003 0x17510d90+943 944 33005 0x1750dc18+944 945 33007 0x1750aa74+945 946 33022 0x17507a28+946 947 33024 0x175048af+947 948 33033 0x1750180d+948 949 33034 0x174fe6ea+949 950 33040 0x174fb61c+950 951 33045 0x174f85a5+951 952 33055 0x174f5584+952 953 33056 0x174f24e2+953 954 33058 0x174ef43f+954 955 33059 0x174ec3c8+955 956 33072 0x174e9453+956 957 33083 0x174e64dd+957 958 33088 0x174e3511+958 959 33099 0x174e05c7+959 960 33119 0x174dd77e+960 961 33121 0x174da7b2+961 962 33121 0x174d77e7+962 963 33123 0x174d4871+963 964 33135 0x174d19d2+964 965 33136 0x174cea5c+965 966 33137 0x174cbb11+966 967 33137 0x174c8bf2+967 968 33145 0x174c5d53+968 969 33157 0x174c2f35+969 970 33165 0x174c0116+970 971 33166 0x174bd24c+971 972 33169 0x174ba3d8+972 973 33172 0x174b758f+973 974 33175 0x174b471b+974 975 33192 0x174b1a29+975 976 33195 0x174aec36+976 977 33198 0x174abe43+977 978 33201 0x174a907b+978 979 33201 0x174a6287+979 980 33210 0x174a356b+980 981 33210 0x174a07cd+981 982 33214 0x1749da86+982 983 33245 0x1749af6c+983 984 33254 0x174982a6+984 985 33260 0x174955b4+985 986 33275 0x174929c4+986 987 33284 0x1748fd53+987 988 33299 0x1748d18e+988 989 33305 0x1748a51d+989 990 33306 0x17487881+990 991 33309 0x17484c10+991 992 33311 0x17481f9f+992 993 33338 0x1747f55c+993 994 33340 0x1747c917+994 995 33343 0x17479cfc+995 996 33346 0x17477136+996 997 33347 0x17474546+997 998 33353 0x174719ac+998 999 33354 0x1746ede7+999 1000 33360 0x1746c2cd+1000 1001 33365 0x17469789+
+ data/test_vectors_aserti3-2d_run08.txt view
@@ -0,0 +1,509 @@+## description: run8 - deterministically random solvetimes for down-ramping hashrate around a recent real life nBits+##   anchor height: 1+##   anchor parent time: 0+##   anchor nBits: 0x1802aee8+##   start height: 2+##   start time: 1200+##   iterations: 500+# iteration,height,time,target+1 2 1200 0x1802aee8+2 3 1310 0x1802ad91+3 4 1327 0x1802abf8+4 5 1739 0x1802ab73+5 6 2219 0x1802ab20+6 7 2604 0x1802aa89+7 8 2746 0x1802a94b+8 9 7099 0x1802b39c+9 10 7099 0x1802b1f2+10 11 7657 0x1802b1d4+11 12 9595 0x1802b58d+12 13 9975 0x1802b4ee+13 14 11607 0x1802b7d3+14 15 13951 0x1802bcb8+15 16 15797 0x1802c03e+16 17 15865 0x1802bebb+17 18 18343 0x1802c40e+18 19 20631 0x1802c8de+19 20 22947 0x1802cdce+20 21 23229 0x1802cce4+21 22 33606 0x1802e994+22 23 34944 0x1802ebca+23 24 37977 0x1802f31e+24 25 39180 0x1802f4f1+25 26 40755 0x1802f7e8+26 27 41175 0x1802f75d+27 28 42855 0x1802faa7+28 29 43200 0x1802f9de+29 30 43410 0x1802f8af+30 31 46761 0x1803011f+31 32 47473 0x18030177+32 33 51809 0x18030d12+33 34 53537 0x1803109d+34 35 56089 0x180316c4+35 36 59769 0x18032096+36 37 63137 0x18032984+37 38 66673 0x18033318+38 39 74425 0x18034ae9+39 40 76909 0x1803514b+40 41 79761 0x180358ff+41 42 84471 0x1803673b+42 43 87141 0x18036e7c+43 44 90151 0x18037704+44 45 91796 0x18037abc+45 46 93236 0x18037dbe+46 47 93916 0x18037e07+47 48 94246 0x18037d10+48 49 96101 0x1803818f+49 50 96126 0x18037f7c+50 51 96281 0x18037de7+51 52 96719 0x18037d50+52 53 99245 0x1803843e+53 54 111773 0x1803b056+54 55 115529 0x1803bc5f+55 56 117845 0x1803c2fc+56 57 121667 0x1803cf82+57 58 123839 0x1803d5af+58 59 124271 0x1803d508+59 60 125795 0x1803d8ab+60 61 128627 0x1803e189+61 62 130874 0x1803e81e+62 63 136075 0x1803fac1+63 64 138133 0x180400bd+64 65 138966 0x180401b4+65 66 145532 0x18041a91+66 67 151454 0x18043142+67 68 151454 0x18042eab+68 69 151510 0x18042c58+69 70 154786 0x180437e2+70 71 156228 0x18043b88+71 72 160876 0x18044d49+72 73 160932 0x18044ae2+73 74 167388 0x18046509+74 75 172700 0x18047a86+75 76 174700 0x180480fd+76 77 176172 0x18048509+77 78 176740 0x180484e1+78 79 176820 0x18048278+79 80 177348 0x18048224+80 81 188020 0x1804b1c2+81 82 188533 0x1804b157+82 83 191098 0x1804bad9+83 84 191449 0x1804b9a4+84 85 196534 0x1804cf9c+85 86 201439 0x1804e50e+86 87 202573 0x1804e7c0+87 88 207289 0x1804fca6+88 89 227503 0x18056506+89 90 228205 0x18056596+90 91 229636 0x18056a38+91 92 234216 0x18058096+92 93 252336 0x1805e733+93 94 262256 0x180620b3+94 95 262756 0x18062012+95 96 271086 0x1806516b+96 97 288256 0x1806c070+97 98 317896 0x180795e1+98 99 321636 0x1807ae83+99 100 333016 0x18080571+100 101 333916 0x180807f0+101 102 347996 0x18087a4a+102 103 352990 0x1808a0e7+103 104 356246 0x1808b897+104 105 362428 0x1808eb32+105 106 367004 0x18090ff1+106 107 379852 0x180984da+107 108 382745 0x18099b63+108 109 384065 0x1809a27e+109 110 388564 0x1809c961+110 111 391754 0x1809e38a+111 112 404822 0x180a6542+112 113 406274 0x180a6e5b+113 114 408962 0x180a84c3+114 115 409442 0x180a8377+115 116 425930 0x180b34b4+116 117 431978 0x180b7423+117 118 432638 0x180b74d9+118 119 435434 0x180b8ecd+119 120 437390 0x180b9ef1+120 121 439130 0x180bac92+121 122 439715 0x180bac67+122 123 442042 0x180bc127+123 124 448087 0x180c0390+124 125 449192 0x180c09c5+125 126 498904 0x180ea828+126 127 503376 0x180ee2f0+127 128 521797 0x180ffd6e+128 129 523396 0x18100de8+129 130 527010 0x18103fec+130 131 530741 0x1810748a+131 132 532169 0x18108296+132 133 546925 0x181179c7+133 134 565671 0x1812cbe8+134 135 572167 0x18133f13+135 136 585439 0x18143ff4+136 137 590605 0x18149fab+137 138 594917 0x1814eec8+138 139 610569 0x18163c62+139 140 614111 0x1816800d+140 141 624863 0x18176f87+141 142 633533 0x181834d4+142 143 637988 0x18189542+143 144 642758 0x1818ff54+144 145 643043 0x1818f747+145 146 643838 0x1818fc3a+146 147 647378 0x18194807+147 148 652958 0x1819ca80+148 149 656573 0x181a1aa9+149 150 662303 0x181aa584+150 151 665153 0x181ae33b+151 152 665217 0x181ad479+152 153 669809 0x181b433e+153 154 675441 0x181bd174+154 155 709105 0x181fc36f+155 156 739713 0x1823d4b2+156 157 739953 0x1823c772+157 158 743137 0x182426f4+158 159 751377 0x1825473b+159 160 756049 0x1825e474+160 161 766465 0x18276a1c+161 162 769848 0x1827db4e+162 163 774421 0x18287f27+163 164 778824 0x18291e62+164 165 781017 0x182961cd+165 166 781935 0x18296f37+166 167 788667 0x182a76ed+167 168 800142 0x182c5c16+168 169 806381 0x182d605c+169 170 808965 0x182dbd45+170 171 845345 0x1834ca94+171 172 848441 0x1835526a+172 173 863057 0x18386749+173 174 874001 0x183acab9+174 175 878285 0x183baac6+175 176 906131 0x18429001+176 177 928253 0x1848922a+177 178 940691 0x184c1a3e+178 179 969311 0x185525de+179 180 970031 0x1855306f+180 181 989723 0x185bfa53+181 182 1001047 0x1860050c+182 183 1017805 0x18667131+183 184 1018432 0x186673e0+184 185 1023885 0x186876b9+185 186 1073114 0x187ef4ed+186 187 1078358 0x19008158+187 188 1140450 0x1900a58c+188 189 1143129 0x1900a6ef+189 190 1146530 0x1900a8d1+190 191 1153750 0x1900ad5b+191 192 1179150 0x1900bf80+192 193 1212890 0x1900dab2+193 194 1224950 0x1900e4fa+194 195 1226790 0x1900e61e+195 196 1232690 0x1900eb10+196 197 1267710 0x19010de3+197 198 1283910 0x19011f55+198 199 1293770 0x19012a37+199 200 1324330 0x19015048+200 201 1336050 0x19015fa0+201 202 1350435 0x190173a3+202 203 1357323 0x19017d1f+203 204 1368411 0x19018d7d+204 205 1371057 0x190190c2+205 206 1382985 0x1901a35d+206 207 1383132 0x1901a29a+207 208 1396047 0x1901b7c8+208 209 1412889 0x1901d55e+209 210 1427358 0x1901f03a+210 211 1431075 0x1901f679+211 212 1446233 0x190214b8+212 213 1461589 0x1902353d+213 214 1501123 0x190294cc+214 215 1517755 0x1902c0ae+215 216 1518239 0x1902c05b+216 217 1546553 0x1903132e+217 218 1550821 0x19031ed6+218 219 1551811 0x19032015+219 220 1580213 0x19037e5f+220 221 1611497 0x1903f383+221 222 1613567 0x1903f97f+222 223 1619846 0x190410f7+223 224 1627988 0x190430fa+224 225 1682705 0x19053535+225 226 1712835 0x1905dce8+226 227 1737836 0x1906771c+227 228 1742551 0x1906929d+228 229 1743655 0x19069607+229 230 1748922 0x1906b5da+230 231 1751314 0x1906c238+231 232 1759570 0x1906f822+232 233 1768858 0x19073761+233 234 1771642 0x190747a0+234 235 1796434 0x1908059c+235 236 1805578 0x19084d48+236 237 1809922 0x19086d7b+237 238 1832170 0x19093166+238 239 1848538 0x1909cb1e+239 240 1855858 0x190a0f95+240 241 1907986 0x190c5ed6+241 242 1950436 0x190ea128+242 243 1951261 0x190ea482+243 244 1999686 0x1911bddd+244 245 2000211 0x1911bc7b+245 246 2039836 0x1914be10+246 247 2080561 0x19185d54+247 248 2095061 0x1919c29e+248 249 2098211 0x191a0649+249 250 2101636 0x191a522b+250 251 2114311 0x191ba03b+251 252 2118367 0x191c02ec+252 253 2178765 0x19239bd8+253 254 2206325 0x1927ad86+254 255 2208301 0x1927e59f+255 256 2210979 0x19283b3b+256 257 2211109 0x192827b2+257 258 2234067 0x192bebe5+258 259 2236381 0x192c398b+259 260 2259703 0x1930720b+260 261 2277357 0x1933df20+261 262 2293854 0x19374884+262 263 2296878 0x1937d2b3+263 264 2345100 0x194393f1+264 265 2346180 0x1943b550+265 266 2348610 0x1944351a+266 267 2353740 0x19457594+267 268 2367645 0x19494570+268 269 2369319 0x1949966f+269 270 2371722 0x194a1f48+270 271 2387814 0x194ee076+271 272 2397166 0x1951b169+272 273 2442330 0x1961ae56+273 274 2443282 0x1961d1e4+274 275 2445886 0x19629bce+275 276 2450310 0x1964218b+276 277 2450450 0x1963f1ea+277 278 2459998 0x19679803+278 279 2480942 0x197064eb+279 280 2501718 0x1979dde2+280 281 2529858 0x1a00881c+281 282 2567210 0x1a009dbf+282 283 2573300 0x1a00a142+283 284 2588844 0x1a00ab35+284 285 2663403 0x1a00e650+285 286 2675641 0x1a00f152+286 287 2701248 0x1a010acd+287 288 2718503 0x1a011d42+288 289 2736599 0x1a013202+289 290 2758001 0x1a014ca0+290 291 2771080 0x1a015db3+291 292 2808940 0x1a019610+292 293 2841880 0x1a01ce41+293 294 2849530 0x1a01db83+294 295 2849740 0x1a01dac4+295 296 2867290 0x1a01fc2e+296 297 2895370 0x1a023777+297 298 2951740 0x1a02c5c1+298 299 2965600 0x1a02ec8e+299 300 2967070 0x1a02ef2a+300 301 3021970 0x1a03a5c8+301 302 3023086 0x1a03a7b9+302 303 3076778 0x1a0485fd+303 304 3109142 0x1a05235e+304 305 3145226 0x1a05ec96+305 306 3154650 0x1a062332+306 307 3169530 0x1a067fb4+307 308 3175327 0x1a06a2b6+308 309 3194609 0x1a0726cc+309 310 3198639 0x1a074024+310 311 3252548 0x1a08fb0b+311 312 3282116 0x1a0a1665+312 313 3298596 0x1a0abffc+313 314 3353700 0x1a0d6062+314 315 3363236 0x1a0ddd3d+315 316 3363684 0x1a0ddb0f+316 317 3401476 0x1a1015d4+317 318 3402884 0x1a102334+318 319 3411748 0x1a10ae90+319 320 3421732 0x1a115289+320 321 3433732 0x1a122246+321 322 3441124 0x1a12a29c+322 323 3462640 0x1a14441a+323 324 3485872 0x1a163110+324 325 3498445 0x1a1748ca+325 326 3535537 0x1a1af36a+326 327 3559594 0x1a1d9bb2+327 328 3567580 0x1a1e7fb0+328 329 3590350 0x1a215657+329 330 3601966 0x1a22d84e+330 331 3612064 0x1a24331c+331 332 3631240 0x1a27005f+332 333 3634844 0x1a277974+333 334 3644874 0x1a28fec6+334 335 3648648 0x1a29851a+335 336 3670204 0x1a2d2986+336 337 3701076 0x1a32fd66+337 338 3713860 0x1a358a6d+338 339 3716478 0x1a35f99d+339 340 3728004 0x1a386444+340 341 3742590 0x1a3ba512+341 342 3775490 0x1a43e6c9+342 343 3782035 0x1a458ab6+343 344 3806885 0x1a4ca71d+344 345 3811960 0x1a4e0a79+345 346 3822635 0x1a51420e+346 347 3826135 0x1a523539+347 348 3827955 0x1a529c5c+348 349 3855430 0x1a5c0463+349 350 3898795 0x1a6d383c+350 351 3912900 0x1a734b80+351 352 3950808 0x1b0085ea+352 353 3961644 0x1b008b89+353 354 3983604 0x1b009807+354 355 3997608 0x1b00a06d+355 356 4011072 0x1b00a8e9+356 357 4015212 0x1b00ab52+357 358 4020360 0x1b00ae7b+358 359 4089768 0x1b00e5e9+359 360 4113384 0x1b00fc27+360 361 4114896 0x1b00fd14+361 362 4145939 0x1b011dfc+362 363 4154079 0x1b0126c7+363 364 4175169 0x1b014008+364 365 4182902 0x1b014950+365 366 4221234 0x1b017f21+366 367 4225341 0x1b01848d+367 368 4254423 0x1b01b386+368 369 4269926 0x1b01ce56+369 370 4272294 0x1b01d1a1+370 371 4279546 0x1b01de38+371 372 4279774 0x1b01dd82+372 373 4294214 0x1b01f8c8+373 374 4309452 0x1b021754+374 375 4330048 0x1b024417+375 376 4335064 0x1b024e77+376 377 4338902 0x1b025631+377 378 4386706 0x1b02d2e3+378 379 4395332 0x1b02ea8b+379 380 4430710 0x1b035a31+380 381 4432876 0x1b035f97+381 382 4455847 0x1b03b0a1+382 383 4476088 0x1b03fe19+383 384 4507912 0x1b04869e+384 385 4581934 0x1b06136e+385 386 4583416 0x1b0618f2+386 387 4592074 0x1b064c28+387 388 4691875 0x1b096030+388 389 4711882 0x1b0a227e+389 390 4723075 0x1b0a92f5+390 391 4731889 0x1b0aedaf+391 392 4751009 0x1b0bc599+392 393 4756169 0x1b0bfd31+393 394 4770929 0x1b0cb061+394 395 4797289 0x1b0e119a+395 396 4819329 0x1b0f5504+396 397 4842089 0x1b10c22f+397 398 4844009 0x1b10d8fe+398 399 4871489 0x1b12c487+399 400 4873449 0x1b12ded0+400 401 4883769 0x1b139eeb+401 402 4899882 0x1b14e128+402 403 4901686 0x1b14faf0+403 404 4924277 0x1b16eaa0+404 405 4935060 0x1b17df22+405 406 4944203 0x1b18b408+406 407 4953674 0x1b1998c6+407 408 4997462 0x1b1e6f81+408 409 5033255 0x1b230de3+409 410 5035633 0x1b234e1d+410 411 5085243 0x1b2af93b+411 412 5088519 0x1b2b704d+412 413 5121909 0x1b318bc8+413 414 5124933 0x1b3207b7+414 415 5135979 0x1b342b18+415 416 5180877 0x1b3e4ec8+416 417 5190537 0x1b409d98+417 418 5190789 0x1b408673+418 419 5246103 0x1b505f7d+419 420 5264121 0x1b562e80+420 421 5280459 0x1b5bceb9+421 422 5299680 0x1b62ec4d+422 423 5305958 0x1b6532e5+423 424 5320320 0x1b6aefa1+424 425 5352914 0x1b79926b+425 426 5430357 0x1c00a57c+426 427 5460070 0x1c00b9fd+427 428 5512315 0x1c00e4c3+428 429 5523108 0x1c00ee4f+429 430 5638176 0x1c017941+430 431 5641659 0x1c017da2+431 432 5666343 0x1c01a44e+432 433 5668191 0x1c01a668+433 434 5683371 0x1c01bfd3+434 435 5697319 0x1c01d873+435 436 5709375 0x1c01eeac+436 437 5798783 0x1c02c276+437 438 5828967 0x1c031b78+438 439 5837635 0x1c03359a+439 440 5848459 0x1c0357f3+440 441 5866191 0x1c0394cd+441 442 5910066 0x1c0442ad+442 443 5912496 0x1c044aba+443 444 5929326 0x1c0494ba+444 445 5936076 0x1c04b208+445 446 5969421 0x1c055aa0+446 447 6009786 0x1c0647b6+447 448 6037011 0x1c06fcba+448 449 6047316 0x1c0743b5+449 450 6053211 0x1c076b9e+450 451 6069321 0x1c07e59a+451 452 6094989 0x1c08bbac+452 453 6106765 0x1c092249+453 454 6109111 0x1c0932b8+454 455 6132249 0x1c0a1193+455 456 6137263 0x1c0a3f96+456 457 6160447 0x1c0b385a+457 458 6177513 0x1c0bfc7a+458 459 6244765 0x1c0fa85d+459 460 6254195 0x1c1038f7+460 461 6259071 0x1c1080e8+461 462 6269223 0x1c112623+462 463 6331686 0x1c15fae5+463 464 6354951 0x1c18129e+464 465 6374315 0x1c19f3c1+465 466 6399836 0x1c1cada5+466 467 6409471 0x1c1dbc7b+467 468 6431467 0x1c206707+468 469 6491768 0x1c292ce4+469 470 6562409 0x1c36867c+470 471 6592536 0x1c3d6125+471 472 6623496 0x1c4555e2+472 473 6640584 0x1c4a14e2+473 474 6646680 0x1c4bbbd4+474 475 6652008 0x1c4d2f1e+475 476 6666120 0x1c517b3e+476 477 6701784 0x1c5dc9da+477 478 6705288 0x1c5ee342+478 479 6737688 0x1c6bc7f6+479 480 6774168 0x1c7c75fc+480 481 6842136 0x1d00a31a+481 482 6924554 0x1d00e26d+482 483 6928915 0x1d00e5dd+483 484 6930238 0x1d00e688+484 485 6993399 0x1d00ffff+485 486 7003150 0x1d00ffff+486 487 7004914 0x1d00ffff+487 488 7092820 0x1d00ffff+488 489 7106491 0x1d00ffff+489 490 7126140 0x1d00ffff+490 491 7160783 0x1d00ffff+491 492 7216733 0x1d00ffff+492 493 7226083 0x1d00ffff+493 494 7284983 0x1d00ffff+494 495 7290833 0x1d00ffff+495 496 7293683 0x1d00ffff+496 497 7343383 0x1d00ffff+497 498 7387333 0x1d00ffff+498 499 7388383 0x1d00ffff+499 500 7421433 0x1d00ffff+500 501 7462933 0x1d00ffff+
+ data/test_vectors_aserti3-2d_run09.txt view
@@ -0,0 +1,19 @@+## description: run9 - a sequence of 300s blocks across signed 32-bit max integer height+##   anchor height: 2147483642+##   anchor parent time: 1234567290+##   anchor nBits: 0x1802aee8+##   start height: 2147483643+##   start time: 1234568190+##   iterations: 10+# iteration,height,time,target+1 2147483643 1234568190 0x1802ae16+2 2147483644 1234568490 0x1802ad44+3 2147483645 1234568790 0x1802ac71+4 2147483646 1234569090 0x1802ab9e+5 2147483647 1234569390 0x1802aacd+6 2147483648 1234569690 0x1802a9fa+7 2147483649 1234569990 0x1802a929+8 2147483650 1234570290 0x1802a858+9 2147483651 1234570590 0x1802a787+10 2147483652 1234570890 0x1802a6b7+
+ data/test_vectors_aserti3-2d_run10.txt view
@@ -0,0 +1,19 @@+## description: run10 - a sequence of 900s blocks across signed 64-bit max integer height and signed 32-bit max integer time +##   anchor height: 9223372036854775802+##   anchor parent time: 2147483047+##   anchor nBits: 0x1802aee8+##   start height: 9223372036854775803+##   start time: 2147484547+##   iterations: 10+# iteration,height,time,target+1 9223372036854775803 2147484547 0x1802afbb+2 9223372036854775804 2147485447 0x1802b08f+3 9223372036854775805 2147486347 0x1802b166+4 9223372036854775806 2147487247 0x1802b23a+5 9223372036854775807 2147488147 0x1802b30e+6 9223372036854775808 2147489047 0x1802b3e5+7 9223372036854775809 2147489947 0x1802b4bb+8 9223372036854775810 2147490847 0x1802b592+9 9223372036854775811 2147491747 0x1802b669+10 9223372036854775812 2147492647 0x1802b73d+
+ data/test_vectors_aserti3-2d_run11.txt view
@@ -0,0 +1,1009 @@+## description: run11 - deterministically uniform random solvetimes with negative time+##   anchor height: 1+##   anchor parent time: 0+##   anchor nBits: 0x1802aee8+##   start height: 2+##   start time: 1200+##   iterations: 1000+# iteration,height,time,target+1 2 1200 0x1802aee8+2 3 1496 0x1802ae12+3 4 1398 0x1802ac28+4 5 1996 0x1802ac28+5 6 1731 0x1802a9cb+6 7 1816 0x1802a863+7 8 2113 0x1802a790+8 9 2318 0x1802a67e+9 10 2679 0x1802a5d7+10 11 3289 0x1802a5df+11 12 3557 0x1802a4f8+12 13 3438 0x1802a305+13 14 3391 0x1802a147+14 15 3417 0x18029fba+15 16 4317 0x1802a08a+16 17 4073 0x18029e45+17 18 3774 0x18029bdc+18 19 3619 0x180299d8+19 20 4046 0x18029961+20 21 4571 0x1802992f+21 22 5233 0x18029959+22 23 5432 0x18029848+23 24 5458 0x180296c1+24 25 5399 0x18029501+25 26 5991 0x180294fc+26 27 6856 0x180295b1+27 28 7586 0x18029608+28 29 7706 0x180294c2+29 30 8349 0x180294e0+30 31 8407 0x18029371+31 32 8174 0x1802913f+32 33 8866 0x1802917d+33 34 9614 0x180291e0+34 35 9779 0x180290bc+35 36 10501 0x1802910e+36 37 10776 0x18029033+37 38 11502 0x18029088+38 39 11548 0x18028f14+39 40 11500 0x18028d60+40 41 11239 0x18028b1f+41 42 12136 0x18028be7+42 43 12921 0x18028c62+43 44 13251 0x18028baf+44 45 13017 0x18028980+45 46 13695 0x180289b5+46 47 13758 0x1802884f+47 48 14043 0x1802877f+48 49 14520 0x1802872b+49 50 14920 0x180286a7+50 51 14779 0x180284bc+51 52 14728 0x1802830d+52 53 14753 0x18028193+53 54 15181 0x18028122+54 55 15735 0x18028104+55 56 15645 0x18027f3f+56 57 16234 0x18027f37+57 58 16067 0x18027d41+58 59 16120 0x18027bdb+59 60 16834 0x18027c26+60 61 16996 0x18027b08+61 62 17004 0x18027986+62 63 17454 0x18027924+63 64 18157 0x18027967+64 65 17920 0x18027749+65 66 18236 0x18027690+66 67 18091 0x180274ae+67 68 18577 0x18027464+68 69 18790 0x1802736b+69 70 19431 0x18027386+70 71 19352 0x180271d0+71 72 19958 0x180271d6+72 73 20307 0x18027135+73 74 20933 0x18027143+74 75 21541 0x18027149+75 76 22394 0x180271eb+76 77 23139 0x18027249+77 78 23613 0x180271f9+78 79 23884 0x18027125+79 80 24419 0x180270fb+80 81 25272 0x1802719d+81 82 25923 0x180271be+82 83 26492 0x180271ab+83 84 26900 0x1802712f+84 85 27144 0x1802704b+85 86 27605 0x18026ff1+86 87 28045 0x18026f8a+87 88 28252 0x18026e8f+88 89 28138 0x18026cc8+89 90 28296 0x18026baf+90 91 28162 0x180269dd+91 92 28107 0x1802683c+92 93 28726 0x18026848+93 94 28552 0x18026660+94 95 29393 0x180266f7+95 96 29647 0x1802661e+96 97 30505 0x180266c0+97 98 30215 0x1802648e+98 99 30194 0x18026309+99 100 29955 0x180260fc+100 101 29800 0x18025f23+101 102 29638 0x18025d48+102 103 29347 0x18025b1f+103 104 29653 0x18025a6a+104 105 29683 0x1802590b+105 106 30547 0x180259ad+106 107 30413 0x180257e6+107 108 30890 0x1802579c+108 109 30699 0x180255b5+109 110 30969 0x180254eb+110 111 31199 0x18025407+111 112 31684 0x180253c2+112 113 32275 0x180253bb+113 114 32519 0x180252e2+114 115 33083 0x180252cd+115 116 32920 0x180250fa+116 117 32905 0x18024f83+117 118 33019 0x18024e5e+118 119 33176 0x18024d51+119 120 33530 0x18024cbb+120 121 33796 0x18024bf2+121 122 33993 0x18024aff+122 123 34841 0x18024b94+123 124 35394 0x18024b78+124 125 35811 0x18024b0a+125 126 35977 0x18024a03+126 127 36103 0x180248e5+127 128 36019 0x1802474c+128 129 35828 0x18024572+129 130 36477 0x18024590+130 131 36483 0x1802442c+131 132 37091 0x18024432+132 133 37854 0x18024492+133 134 37554 0x1802427b+134 135 37268 0x1802406d+135 136 36984 0x18023e62+136 137 37498 0x18023e2f+137 138 37848 0x18023d9b+138 139 38669 0x18023e1f+139 140 38719 0x18023cda+140 141 39597 0x18023d7e+141 142 40042 0x18023d22+142 143 39885 0x18023b65+143 144 39599 0x1802395e+144 145 40432 0x180239e5+145 146 41020 0x180239e0+146 147 41490 0x18023992+147 148 41994 0x1802395b+148 149 42268 0x1802389b+149 150 42377 0x1802377d+150 151 42236 0x180235cd+151 152 42253 0x1802347b+152 153 42130 0x180232d9+153 154 41964 0x1802311d+154 155 42299 0x18023085+155 156 42018 0x18022e8a+156 157 42136 0x18022d76+157 158 41961 0x18022bba+158 159 42603 0x18022bd2+159 160 43374 0x18022c34+160 161 44180 0x18022caa+161 162 43989 0x18022ae6+162 163 44850 0x18022b7b+163 164 45005 0x18022a7d+164 165 45852 0x18022b0a+165 166 45629 0x18022934+166 167 46154 0x1802290b+167 168 46586 0x180228aa+168 169 47003 0x18022843+169 170 47420 0x180227db+170 171 47985 0x180227c6+171 172 47912 0x1802264a+172 173 48768 0x180226da+173 174 49167 0x18022669+174 175 49100 0x180224f0+175 176 49972 0x18022589+176 177 50730 0x180225e3+177 178 50577 0x1802243a+178 179 50465 0x180222a9+179 180 50444 0x1802214d+180 181 50989 0x1802212e+181 182 51330 0x1802209d+182 183 51444 0x18021f8c+183 184 52286 0x18022013+184 185 52731 0x18021fbd+185 186 53402 0x18021fe4+186 187 53983 0x18021fdb+187 188 53779 0x18021e19+188 189 54536 0x18021e72+189 190 55137 0x18021e72+190 191 55664 0x18021e48+191 192 56496 0x18021eca+192 193 56744 0x18021e07+193 194 57636 0x18021ea9+194 195 58132 0x18021e6f+195 196 58390 0x18021db1+196 197 58294 0x18021c2e+197 198 59014 0x18021c70+198 199 59385 0x18021bf1+199 200 59252 0x18021a5a+200 201 59734 0x18021a1a+201 202 60492 0x18021a70+202 203 61248 0x18021ac8+203 204 61585 0x18021a36+204 205 61408 0x18021889+205 206 61576 0x1802179a+206 207 61567 0x1802164a+207 208 61997 0x180215ee+208 209 62779 0x18021652+209 210 63079 0x180215ad+210 211 63753 0x180215d6+211 212 64482 0x1802161d+212 213 65054 0x1802160d+213 214 65180 0x1802150a+214 215 65954 0x18021569+215 216 65871 0x180213f3+216 217 66122 0x18021334+217 218 66415 0x1802128c+218 219 67002 0x18021286+219 220 67163 0x18021196+220 221 67140 0x18021044+221 222 67845 0x1802107d+222 223 68006 0x18020f8e+223 224 67926 0x18020e1f+224 225 68677 0x18020e6f+225 226 68752 0x18020d54+226 227 68739 0x18020c09+227 228 68517 0x18020a4f+228 229 68844 0x180209bd+229 230 69623 0x18020a1c+230 231 70389 0x18020a76+231 232 70771 0x18020a00+232 233 71391 0x18020a0c+233 234 71197 0x18020863+234 235 70997 0x180206b7+235 236 71084 0x180205a7+236 237 71262 0x180204c7+237 238 71248 0x18020381+238 239 71418 0x1802029d+239 240 71376 0x18020149+240 241 72137 0x1802019e+241 242 71924 0x1801fff2+242 243 72723 0x1802005a+243 244 72733 0x1801ff24+244 245 73287 0x1801ff0c+245 246 73635 0x1801fe89+246 247 73347 0x1801fcb7+247 248 73207 0x1801fb34+248 249 73162 0x1801f9e6+249 250 74060 0x1801fa80+250 251 74354 0x1801f9e0+251 252 74578 0x1801f91e+252 253 74420 0x1801f793+253 254 75207 0x1801f7f5+254 255 75117 0x1801f690+255 256 75039 0x1801f533+256 257 75543 0x1801f502+257 258 75688 0x1801f418+258 259 75698 0x1801f2e9+259 260 75855 0x1801f206+260 261 76080 0x1801f147+261 262 75969 0x1801efdb+262 263 76421 0x1801ef90+263 264 77114 0x1801efbf+264 265 77380 0x1801ef14+265 266 78152 0x1801ef6d+266 267 78608 0x1801ef23+267 268 78954 0x1801eea2+268 269 79243 0x1801ee04+269 270 79896 0x1801ee1f+270 271 80189 0x1801ed83+271 272 80441 0x1801ecd2+272 273 80632 0x1801ec04+273 274 80814 0x1801eb31+274 275 81489 0x1801eb56+275 276 82203 0x1801eb90+276 277 82617 0x1801eb32+277 278 82730 0x1801ea3d+278 279 82989 0x1801e992+279 280 83556 0x1801e981+280 281 84222 0x1801e9a1+281 282 84665 0x1801e953+282 283 84767 0x1801e85a+283 284 84936 0x1801e780+284 285 85292 0x1801e708+285 286 85486 0x1801e63c+286 287 86046 0x1801e628+287 288 85785 0x1801e47a+288 289 86591 0x1801e4e0+289 290 86393 0x1801e355+290 291 86577 0x1801e286+291 292 86668 0x1801e18b+292 293 87430 0x1801e1da+293 294 87685 0x1801e130+294 295 88110 0x1801e0d9+295 296 88208 0x1801dfe2+296 297 88868 0x1801dfff+297 298 89133 0x1801df5a+298 299 90001 0x1801dfde+299 300 90027 0x1801dec3+300 301 89785 0x1801dd27+301 302 89493 0x1801db73+302 303 89555 0x1801da6c+303 304 89711 0x1801d994+304 305 90224 0x1801d969+305 306 90457 0x1801d8b6+306 307 90665 0x1801d7f8+307 308 91530 0x1801d879+308 309 91706 0x1801d7ab+309 310 92557 0x1801d826+310 311 92946 0x1801d7be+311 312 92677 0x1801d61c+312 313 93246 0x1801d60c+313 314 93286 0x1801d4fe+314 315 93637 0x1801d486+315 316 94029 0x1801d422+316 317 94460 0x1801d3d1+317 318 94623 0x1801d2ff+318 319 95252 0x1801d30c+319 320 95747 0x1801d2dc+320 321 95499 0x1801d145+321 322 95538 0x1801d039+322 323 95651 0x1801cf52+323 324 95785 0x1801ce75+324 325 95569 0x1801ccf1+325 326 95690 0x1801cc10+326 327 95734 0x1801cb09+327 328 95667 0x1801c9cf+328 329 96011 0x1801c956+329 330 95815 0x1801c7e1+330 331 96294 0x1801c7a9+331 332 96574 0x1801c714+332 333 97006 0x1801c6c5+333 334 97517 0x1801c69b+334 335 98194 0x1801c6bf+335 336 98444 0x1801c61d+336 337 98484 0x1801c517+337 338 98453 0x1801c3f3+338 339 99067 0x1801c3f8+339 340 99934 0x1801c475+340 341 100298 0x1801c407+341 342 100298 0x1801c2f0+342 343 100644 0x1801c27b+343 344 101235 0x1801c277+344 345 101096 0x1801c121+345 346 101561 0x1801c0e3+346 347 102143 0x1801c0db+347 348 102236 0x1801bff2+348 349 102877 0x1801c006+349 350 103686 0x1801c065+350 351 103842 0x1801bf99+351 352 104588 0x1801bfdc+352 353 105259 0x1801bffd+353 354 105780 0x1801bfd8+354 355 105569 0x1801be65+355 356 106385 0x1801bec7+356 357 106138 0x1801bd44+357 358 105916 0x1801bbcd+358 359 106049 0x1801baf9+359 360 106756 0x1801bb29+360 361 107253 0x1801bafa+361 362 107709 0x1801bab8+362 363 108165 0x1801ba77+363 364 108789 0x1801ba83+364 365 108836 0x1801b988+365 366 108602 0x1801b80d+366 367 108367 0x1801b696+367 368 108523 0x1801b5ce+368 369 109272 0x1801b611+369 370 110157 0x1801b692+370 371 110833 0x1801b6b3+371 372 110976 0x1801b5e7+372 373 110746 0x1801b472+373 374 111641 0x1801b4f6+374 375 111873 0x1801b451+375 376 111825 0x1801b32f+376 377 112406 0x1801b327+377 378 112416 0x1801b220+378 379 113140 0x1801b257+379 380 113403 0x1801b1c2+380 381 113418 0x1801b0be+381 382 114174 0x1801b102+382 383 114250 0x1801b01a+383 384 115078 0x1801b07f+384 385 114959 0x1801af41+385 386 115420 0x1801af03+386 387 115754 0x1801ae8d+387 388 115625 0x1801ad4c+388 389 116160 0x1801ad2f+389 390 116327 0x1801ac70+390 391 117137 0x1801accd+391 392 117973 0x1801ad34+392 393 117987 0x1801ac34+393 394 118576 0x1801ac2f+394 395 118484 0x1801aafe+395 396 118857 0x1801aa9b+396 397 119475 0x1801aaa3+397 398 120281 0x1801aafe+398 399 120659 0x1801aa9c+399 400 121107 0x1801aa59+400 401 121807 0x1801aa85+401 402 122324 0x1801aa61+402 403 123027 0x1801aa8f+403 404 123233 0x1801a9e2+404 405 123550 0x1801a966+405 406 123952 0x1801a910+406 407 124779 0x1801a974+407 408 124707 0x1801a84e+408 409 125199 0x1801a81f+409 410 125634 0x1801a7d8+410 411 125938 0x1801a757+411 412 125653 0x1801a5d7+412 413 125719 0x1801a4f1+413 414 126189 0x1801a4b8+414 415 127008 0x1801a518+415 416 127593 0x1801a511+416 417 128434 0x1801a579+417 418 128954 0x1801a555+418 419 129593 0x1801a567+419 420 129600 0x1801a466+420 421 130389 0x1801a4b8+421 422 130909 0x1801a497+422 423 130897 0x1801a38e+423 424 131423 0x1801a36e+424 425 131825 0x1801a318+425 426 132665 0x1801a380+426 427 132788 0x1801a2b2+427 428 133348 0x1801a2a2+428 429 133310 0x1801a191+429 430 133053 0x1801a022+430 431 132962 0x18019efb+431 432 133722 0x18019f40+432 433 133774 0x18019e56+433 434 133806 0x18019d66+434 435 134178 0x18019d05+435 436 133966 0x18019bae+436 437 134247 0x18019b26+437 438 135008 0x18019b6b+438 439 135749 0x18019ba7+439 440 136537 0x18019bf6+440 441 136606 0x18019b16+441 442 136474 0x180199e2+442 443 137255 0x18019a2e+443 444 137394 0x1801996c+444 445 137515 0x180198a2+445 446 137751 0x1801980b+446 447 137788 0x18019720+447 448 138666 0x18019793+448 449 138670 0x1801969a+449 450 139435 0x180196e0+450 451 140141 0x1801970c+451 452 140845 0x18019737+452 453 140651 0x180195ec+453 454 140699 0x18019506+454 455 141248 0x180194f1+455 456 141041 0x180193a3+456 457 141325 0x1801931f+457 458 141406 0x1801924a+458 459 141462 0x18019168+459 460 141354 0x18019047+460 461 141577 0x18018fac+461 462 141801 0x18018f11+462 463 141511 0x18018da6+463 464 141781 0x18018d1f+464 465 142602 0x18018d79+465 466 142513 0x18018c60+466 467 142329 0x18018b22+467 468 143015 0x18018b45+468 469 143201 0x18018a9e+469 470 143465 0x18018a16+470 471 144100 0x18018a24+471 472 144113 0x18018936+472 473 144259 0x18018880+473 474 144903 0x18018891+474 475 145171 0x1801880c+475 476 145686 0x180187e9+476 477 146413 0x1801881c+477 478 146592 0x18018773+478 479 146913 0x18018704+479 480 147781 0x1801876f+480 481 147497 0x1801860d+481 482 148327 0x18018668+482 483 148251 0x1801855b+483 484 148015 0x1801840e+484 485 147949 0x18018304+485 486 148802 0x18018369+486 487 148701 0x18018253+487 488 148443 0x18018100+488 489 148243 0x18017fc3+489 490 149058 0x18018019+490 491 149534 0x18017fe7+491 492 150355 0x1801803f+492 493 150134 0x18017efb+493 494 150578 0x18017ebe+494 495 150987 0x18017e74+495 496 150915 0x18017d6b+496 497 151369 0x18017d32+497 498 151314 0x18017c33+498 499 151391 0x18017b67+499 500 151776 0x18017b12+500 501 151798 0x18017a32+501 502 151773 0x18017941+502 503 151649 0x18017829+503 504 152525 0x18017892+504 505 152634 0x180177d5+505 506 153329 0x180177fb+506 507 153368 0x18017722+507 508 153211 0x180175fe+508 509 153807 0x180175fd+509 510 153698 0x180174ee+510 511 154349 0x18017501+511 512 154059 0x180173ac+512 513 154893 0x18017406+513 514 155518 0x1801740f+514 515 155934 0x180173ca+515 516 155800 0x180172b1+516 517 156150 0x18017253+517 518 156781 0x1801725e+518 519 156891 0x180171a5+519 520 157404 0x18017183+520 521 157412 0x180170a2+521 522 157879 0x18017070+522 523 158231 0x18017012+523 524 158136 0x18016f0d+524 525 158289 0x18016e64+525 526 159128 0x18016ebe+526 527 159579 0x18016e87+527 528 159356 0x18016d4f+528 529 159729 0x18016cfb+529 530 159596 0x18016be8+530 531 160403 0x18016c36+531 532 160429 0x18016b5f+532 533 160233 0x18016a36+533 534 160625 0x180169e9+534 535 161076 0x180169b2+535 536 160894 0x18016890+536 537 161553 0x180168a5+537 538 161418 0x18016795+538 539 162309 0x18016800+539 540 162765 0x180167cc+540 541 163435 0x180167e5+541 542 163647 0x18016756+542 543 163732 0x18016699+543 544 164551 0x180166e9+544 545 165037 0x180166c0+545 546 164803 0x1801658b+546 547 164865 0x180164c6+547 548 164737 0x180163bb+548 549 165458 0x180163e9+549 550 165436 0x18016304+550 551 165181 0x180161cd+551 552 164892 0x1801608a+552 553 165406 0x1801606b+553 554 165627 0x18015fe1+554 555 165615 0x18015f03+555 556 166171 0x18015ef5+556 557 166381 0x18015e68+557 558 166933 0x18015e55+558 559 167185 0x18015dd8+559 560 167861 0x18015df4+560 561 167606 0x18015cc1+561 562 168366 0x18015cfb+562 563 168642 0x18015c86+563 564 168697 0x18015bc2+564 565 168630 0x18015ad3+565 566 169039 0x18015a90+566 567 168832 0x18015970+567 568 169716 0x180159d5+568 569 169600 0x180158d7+569 570 169906 0x1801586e+570 571 170711 0x180158b7+571 572 171336 0x180158bf+572 573 171304 0x180157e0+573 574 171759 0x180157ac+574 575 171566 0x18015695+575 576 172042 0x1801566a+576 577 172470 0x1801562d+577 578 173020 0x1801561c+578 579 173716 0x1801563e+579 580 174051 0x180155e1+580 581 174724 0x180155fa+581 582 175478 0x18015631+582 583 175724 0x180155b5+583 584 176432 0x180155da+584 585 177061 0x180155e5+585 586 177177 0x1801553b+586 587 177339 0x180154a2+587 588 177053 0x1801536d+588 589 177513 0x1801533d+589 590 177961 0x18015308+590 591 177771 0x180151f5+591 592 178420 0x18015207+592 593 179226 0x1801524e+593 594 180074 0x180152a4+594 595 180571 0x18015280+595 596 180916 0x18015228+596 597 180882 0x1801514c+597 598 180677 0x18015037+598 599 181194 0x1801501a+599 600 182038 0x1801506e+600 601 181782 0x18014f47+601 602 181553 0x18014e2c+602 603 181606 0x18014d70+603 604 182440 0x18014dc0+604 605 182169 0x18014c97+605 606 182859 0x18014cb5+606 607 183389 0x18014c9d+607 608 184117 0x18014cc9+608 609 184016 0x18014bda+609 610 184193 0x18014b4a+610 611 184979 0x18014b89+611 612 185340 0x18014b38+612 613 185048 0x18014a0a+613 614 185070 0x18014946+614 615 185653 0x18014940+615 616 186128 0x18014916+616 617 186692 0x1801490a+617 618 186626 0x1801482a+618 619 187357 0x18014856+619 620 188191 0x180148a5+620 621 187977 0x18014793+621 622 188620 0x180147a1+622 623 188362 0x18014681+623 624 188754 0x1801463c+624 625 188695 0x18014560+625 626 189527 0x180145ad+626 627 190303 0x180145e7+627 628 190473 0x18014558+628 629 190617 0x180144c0+629 630 190346 0x1801439e+630 631 191013 0x180143b4+631 632 191746 0x180143e1+632 633 192222 0x180143b8+633 634 192007 0x180142a9+634 635 192477 0x1801427e+635 636 192621 0x180141e7+636 637 192328 0x180140c1+637 638 192515 0x18014039+638 639 193101 0x18014035+639 640 193359 0x18013fc4+640 641 193452 0x18013f1e+641 642 193526 0x18013e71+642 643 193272 0x18013d5b+643 644 193768 0x18013d39+644 645 194503 0x18013d65+645 646 195032 0x18013d4e+646 647 195810 0x18013d89+647 648 196075 0x18013d1b+648 649 196515 0x18013ce7+649 650 197231 0x18013d0c+650 651 198079 0x18013d5e+651 652 198624 0x18013d4c+652 653 198571 0x18013c78+653 654 198670 0x18013bd5+654 655 198378 0x18013ab3+655 656 198898 0x18013a9a+656 657 199342 0x18013a68+657 658 199575 0x180139f2+658 659 200350 0x18013a2a+659 660 200530 0x180139a2+660 661 201300 0x180139d9+661 662 201732 0x180139a2+662 663 201462 0x1801388b+663 664 201357 0x180137a9+664 665 201612 0x1801373b+665 666 201778 0x180136b0+666 667 202649 0x18013706+667 668 202552 0x18013628+668 669 203405 0x18013679+669 670 203888 0x18013654+670 671 204592 0x18013675+671 672 205228 0x18013681+672 673 205139 0x180135a5+673 674 205398 0x18013539+674 675 205809 0x180134fc+675 676 205654 0x1801340e+676 677 205990 0x180133ba+677 678 206209 0x18013342+678 679 206486 0x180132dc+679 680 206800 0x18013282+680 681 207279 0x1801325c+681 682 207929 0x1801326c+682 683 208569 0x18013278+683 684 208591 0x180131c3+684 685 208930 0x18013171+685 686 209463 0x1801315b+686 687 209808 0x1801310b+687 688 209767 0x18013042+688 689 209747 0x18012f81+689 690 209925 0x18012efe+690 691 209923 0x18012e42+691 692 209807 0x18012d64+692 693 209783 0x18012ca4+693 694 209582 0x18012bac+694 695 209507 0x18012add+695 696 210083 0x18012ad6+696 697 210349 0x18012a6f+697 698 211097 0x18012a9c+698 699 211417 0x18012a46+699 700 211705 0x180129e7+700 701 212539 0x18012a2e+701 702 213232 0x18012a4b+702 703 213766 0x18012a37+703 704 213940 0x180129b5+704 705 214395 0x18012988+705 706 214862 0x1801295f+706 707 215035 0x180128dd+707 708 215308 0x18012879+708 709 215276 0x180127b9+709 710 215533 0x18012751+710 711 215848 0x180126fa+711 712 215758 0x1801262a+712 713 215977 0x180125b7+713 714 215933 0x180124f4+714 715 216232 0x18012499+715 716 217084 0x180124e6+716 717 217382 0x1801248a+717 718 217880 0x1801246c+718 719 218590 0x1801248c+719 720 218372 0x18012398+720 721 218717 0x1801234a+721 722 219321 0x1801234c+722 723 219465 0x180122c4+723 724 219534 0x18012226+724 725 219397 0x1801214a+725 726 219833 0x18012119+726 727 220123 0x180120bd+727 728 220177 0x1801201b+728 729 221015 0x18012061+729 730 221702 0x1801207b+730 731 222268 0x18012072+731 732 222171 0x18011fa3+732 733 222380 0x18011f2f+733 734 222957 0x18011f29+734 735 223219 0x18011ec5+735 736 223523 0x18011e6e+736 737 223451 0x18011da8+737 738 223273 0x18011cc4+738 739 223525 0x18011c5e+739 740 223560 0x18011bba+740 741 224001 0x18011b8b+741 742 223714 0x18011a89+742 743 223762 0x180119e9+743 744 224025 0x18011988+744 745 224162 0x18011902+745 746 224447 0x180118a7+746 747 224441 0x180117f8+747 748 224855 0x180117c2+748 749 224736 0x180116f4+749 750 224673 0x18011636+750 751 224462 0x1801154e+751 752 224349 0x18011483+752 753 224820 0x1801145f+753 754 225573 0x1801148a+754 755 225560 0x180113dc+755 756 225639 0x18011348+756 757 225642 0x180112a0+757 758 226288 0x180112ad+758 759 226896 0x180112af+759 760 226705 0x180111d0+760 761 226894 0x1801115c+761 762 227345 0x18011132+762 763 227421 0x180110a0+763 764 227816 0x18011066+764 765 227865 0x18010fcc+765 766 228457 0x18010fca+766 767 229104 0x18010fd7+767 768 229954 0x1801101d+768 769 230055 0x18010f91+769 770 229829 0x18010eaa+770 771 229868 0x18010e10+771 772 229940 0x18010d7d+772 773 230326 0x18010d42+773 774 231210 0x18010d90+774 775 231050 0x18010cbe+775 776 231440 0x18010c84+776 777 231500 0x18010bef+777 778 231656 0x18010b75+778 779 232039 0x18010b3a+779 780 232116 0x18010aaa+780 781 232071 0x180109f9+781 782 232431 0x180109b8+782 783 233040 0x180109ba+783 784 232972 0x18010904+784 785 233661 0x1801091c+785 786 233912 0x180108bd+786 787 233761 0x180107f1+787 788 234261 0x180107d6+788 789 234319 0x18010743+789 790 234740 0x18010712+790 791 235326 0x1801070f+791 792 235762 0x180106e2+792 793 235480 0x180105f5+793 794 235272 0x1801051b+794 795 235368 0x18010494+795 796 235887 0x1801047f+796 797 235668 0x180103a4+797 798 235463 0x180102cd+798 799 235897 0x180102a1+799 800 236017 0x18010222+800 801 236085 0x18010194+801 802 236362 0x1801013f+802 803 236126 0x18010062+803 804 235911 0x1800ff8c+804 805 236796 0x1800ffd6+805 806 237218 0x1800ffa8+806 807 237126 0x1800fef2+807 808 237253 0x1800fe77+808 809 238102 0x1800feb8+809 810 238176 0x1800fe2e+810 811 238920 0x1800fe54+811 812 239004 0x1800fdcd+812 813 239098 0x1800fd4a+813 814 239796 0x1800fd63+814 815 239748 0x1800fcba+815 816 240368 0x1800fcc0+816 817 241065 0x1800fcd9+817 818 240875 0x1800fc0c+818 819 241058 0x1800fba0+819 820 241799 0x1800fbc4+820 821 242428 0x1800fbcc+821 822 242274 0x1800fb09+822 823 242943 0x1800fb1c+823 824 243524 0x1800fb16+824 825 243498 0x1800fa75+825 826 243335 0x1800f9b1+826 827 243089 0x1800f8d8+827 828 243216 0x1800f85f+828 829 243103 0x1800f7a9+829 830 243801 0x1800f7c2+830 831 243557 0x1800f6ec+831 832 243639 0x1800f669+832 833 244419 0x1800f696+833 834 244131 0x1800f5b6+834 835 244258 0x1800f53f+835 836 245025 0x1800f569+836 837 245735 0x1800f585+837 838 246049 0x1800f53c+838 839 246190 0x1800f4c9+839 840 246463 0x1800f476+840 841 246179 0x1800f399+841 842 246510 0x1800f356+842 843 247162 0x1800f363+843 844 247122 0x1800f2c3+844 845 247736 0x1800f2c6+845 846 247949 0x1800f266+846 847 248502 0x1800f25a+847 848 249063 0x1800f250+848 849 249494 0x1800f227+849 850 250335 0x1800f262+850 851 250795 0x1800f240+851 852 251316 0x1800f22c+852 853 251598 0x1800f1dc+853 854 252340 0x1800f200+854 855 252895 0x1800f1f5+855 856 253006 0x1800f17c+856 857 253621 0x1800f180+857 858 254508 0x1800f1c6+858 859 254766 0x1800f172+859 860 254619 0x1800f0b9+860 861 254870 0x1800f063+861 862 255300 0x1800f038+862 863 255809 0x1800f022+863 864 255590 0x1800ef58+864 865 256461 0x1800ef9b+865 866 256686 0x1800ef3f+866 867 257249 0x1800ef36+867 868 257442 0x1800eed2+868 869 257726 0x1800ee84+869 870 257866 0x1800ee14+870 871 257926 0x1800ed90+871 872 258786 0x1800edcf+872 873 259075 0x1800ed83+873 874 259593 0x1800ed6f+874 875 259728 0x1800ecff+875 876 259765 0x1800ec75+876 877 260127 0x1800ec3c+877 878 260232 0x1800ebc3+878 879 261088 0x1800ec02+879 880 261375 0x1800ebb6+880 881 262144 0x1800ebdf+881 882 262808 0x1800ebee+882 883 263025 0x1800eb92+883 884 262829 0x1800ead2+884 885 263530 0x1800eaea+885 886 263787 0x1800ea97+886 887 263654 0x1800e9e7+887 888 263620 0x1800e94e+888 889 264365 0x1800e971+889 890 264917 0x1800e966+890 891 264742 0x1800e8ac+891 892 265373 0x1800e8b4+892 893 265080 0x1800e7df+893 894 265543 0x1800e7be+894 895 265337 0x1800e6ff+895 896 265418 0x1800e684+896 897 266029 0x1800e686+897 898 265965 0x1800e5e9+898 899 265892 0x1800e54b+899 900 266131 0x1800e4f6+900 901 266653 0x1800e4e4+901 902 267080 0x1800e4bb+902 903 266974 0x1800e416+903 904 267091 0x1800e3a5+904 905 267526 0x1800e37e+905 906 267325 0x1800e2c4+906 907 267711 0x1800e291+907 908 268093 0x1800e25f+908 909 268256 0x1800e1f9+909 910 268156 0x1800e158+910 911 268205 0x1800e0d8+911 912 268587 0x1800e0a6+912 913 268535 0x1800e010+913 914 268789 0x1800dfc0+914 915 268632 0x1800df13+915 916 269428 0x1800df40+916 917 269331 0x1800dea0+917 918 269378 0x1800de22+918 919 269204 0x1800dd72+919 920 269099 0x1800dcd2+920 921 269884 0x1800dcfc+921 922 269947 0x1800dc82+922 923 270104 0x1800dc1e+923 924 270874 0x1800dc45+924 925 270786 0x1800dba9+925 926 271160 0x1800db76+926 927 271362 0x1800db1c+927 928 271659 0x1800dad9+928 929 272018 0x1800daa3+929 930 272433 0x1800da79+930 931 273008 0x1800da73+931 932 273056 0x1800d9f8+932 933 273012 0x1800d968+933 934 273362 0x1800d931+934 935 273706 0x1800d8f8+935 936 274465 0x1800d91b+936 937 274703 0x1800d8ca+937 938 275506 0x1800d8f8+938 939 276199 0x1800d90c+939 940 276687 0x1800d8f3+940 941 277331 0x1800d8fd+941 942 277822 0x1800d8e5+942 943 277862 0x1800d868+943 944 278099 0x1800d818+944 945 277897 0x1800d767+945 946 277826 0x1800d6d2+946 947 278318 0x1800d6ba+947 948 278221 0x1800d621+948 949 278603 0x1800d5f1+949 950 278768 0x1800d592+950 951 279309 0x1800d585+951 952 279862 0x1800d57b+952 953 280580 0x1800d594+953 954 280418 0x1800d4ee+954 955 280759 0x1800d4b5+955 956 281270 0x1800d4a2+956 957 281030 0x1800d3ea+957 958 281592 0x1800d3e3+958 959 281971 0x1800d3b3+959 960 282758 0x1800d3db+960 961 282510 0x1800d323+961 962 283338 0x1800d355+962 963 284029 0x1800d368+963 964 283894 0x1800d2c9+964 965 284258 0x1800d296+965 966 284011 0x1800d1e0+966 967 284474 0x1800d1c2+967 968 285234 0x1800d1e4+968 969 285138 0x1800d14e+969 970 285178 0x1800d0d7+970 971 285432 0x1800d08d+971 972 285202 0x1800cfdb+972 973 285799 0x1800cfdb+973 974 286639 0x1800d00e+974 975 286862 0x1800cfbe+975 976 287009 0x1800cf5d+976 977 287866 0x1800cf94+977 978 288151 0x1800cf50+978 979 288077 0x1800cec2+979 980 288608 0x1800ceb3+980 981 289036 0x1800ce8f+981 982 289751 0x1800cea7+982 983 289547 0x1800cdfd+983 984 289568 0x1800cd82+984 985 289563 0x1800cd03+985 986 290295 0x1800cd1f+986 987 290354 0x1800ccae+987 988 290267 0x1800cc1d+988 989 290973 0x1800cc33+989 990 290783 0x1800cb8e+990 991 291640 0x1800cbc4+991 992 292311 0x1800cbd3+992 993 292068 0x1800cb22+993 994 292691 0x1800cb28+994 995 293156 0x1800cb0c+995 996 292897 0x1800ca59+996 997 293056 0x1800c9fd+997 998 293558 0x1800c9e9+998 999 293356 0x1800c943+999 1000 293956 0x1800c943+1000 1001 293713 0x1800c896+
+ data/test_vectors_aserti3-2d_run12.txt view
@@ -0,0 +1,10009 @@+## description: run12 - each block 1 second before the previous one+##   anchor height: 1+##   anchor parent time: 10000+##   anchor nBits: 0x1802aee8+##   start height: 2+##   start time: 11200+##   iterations: 10000+# iteration,height,time,target+1 2 11200 0x1802aee8+2 3 11199 0x1802ad44+3 4 11198 0x1802ab9e+4 5 11197 0x1802a9f9+5 6 11196 0x1802a855+6 7 11195 0x1802a6b3+7 8 11194 0x1802a511+8 9 11193 0x1802a371+9 10 11192 0x1802a1d1+10 11 11191 0x1802a033+11 12 11190 0x18029e94+12 13 11189 0x18029cf8+13 14 11188 0x18029b5d+14 15 11187 0x180299c1+15 16 11186 0x18029828+16 17 11185 0x1802968e+17 18 11184 0x180294f8+18 19 11183 0x18029361+19 20 11182 0x180291cb+20 21 11181 0x18029036+21 22 11180 0x18028ea2+22 23 11179 0x18028d0f+23 24 11178 0x18028b7d+24 25 11177 0x180289ec+25 26 11176 0x1802885b+26 27 11175 0x180286cb+27 28 11174 0x1802853c+28 29 11173 0x180283af+29 30 11172 0x18028222+30 31 11171 0x18028096+31 32 11170 0x18027f0c+32 33 11169 0x18027d84+33 34 11168 0x18027bfb+34 35 11167 0x18027a73+35 36 11166 0x180278ec+36 37 11165 0x18027765+37 38 11164 0x180275e0+38 39 11163 0x1802745c+39 40 11162 0x180272d9+40 41 11161 0x18027156+41 42 11160 0x18026fd5+42 43 11159 0x18026e54+43 44 11158 0x18026cd4+44 45 11157 0x18026b56+45 46 11156 0x180269d8+46 47 11155 0x1802685b+47 48 11154 0x180266df+48 49 11153 0x18026565+49 50 11152 0x180263ea+50 51 11151 0x18026271+51 52 11150 0x180260f8+52 53 11149 0x18025f81+53 54 11148 0x18025e0a+54 55 11147 0x18025c94+55 56 11146 0x18025b1f+56 57 11145 0x180259ab+57 58 11144 0x18025838+58 59 11143 0x180256c6+59 60 11142 0x18025555+60 61 11141 0x180253e4+61 62 11140 0x18025274+62 63 11139 0x18025104+63 64 11138 0x18024f99+64 65 11137 0x18024e2c+65 66 11136 0x18024cbf+66 67 11135 0x18024b55+67 68 11134 0x180249e9+68 69 11133 0x18024880+69 70 11132 0x18024717+70 71 11131 0x180245b0+71 72 11130 0x18024448+72 73 11129 0x180242e2+73 74 11128 0x1802417d+74 75 11127 0x18024018+75 76 11126 0x18023eb5+76 77 11125 0x18023d53+77 78 11124 0x18023bf1+78 79 11123 0x18023a91+79 80 11122 0x18023932+80 81 11121 0x180237d2+81 82 11120 0x18023674+82 83 11119 0x18023516+83 84 11118 0x180233b9+84 85 11117 0x1802325d+85 86 11116 0x18023102+86 87 11115 0x18022fa8+87 88 11114 0x18022e4e+88 89 11113 0x18022cf5+89 90 11112 0x18022b9d+90 91 11111 0x18022a47+91 92 11110 0x180228f1+92 93 11109 0x1802279b+93 94 11108 0x18022646+94 95 11107 0x180224f4+95 96 11106 0x180223a2+96 97 11105 0x18022250+97 98 11104 0x180220fe+98 99 11103 0x18021faf+99 100 11102 0x18021e5e+100 101 11101 0x18021d10+101 102 11100 0x18021bc2+102 103 11099 0x18021a75+103 104 11098 0x18021928+104 105 11097 0x180217dd+105 106 11096 0x18021691+106 107 11095 0x18021547+107 108 11094 0x180213ff+108 109 11093 0x180212b6+109 110 11092 0x18021170+110 111 11091 0x1802102a+111 112 11090 0x18020ee4+112 113 11089 0x18020d9e+113 114 11088 0x18020c59+114 115 11087 0x18020b16+115 116 11086 0x180209d4+116 117 11085 0x18020892+117 118 11084 0x18020750+118 119 11083 0x1802060f+119 120 11082 0x180204d0+120 121 11081 0x18020391+121 122 11080 0x18020253+122 123 11079 0x18020116+123 124 11078 0x1801ffda+124 125 11077 0x1801fe9e+125 126 11076 0x1801fd63+126 127 11075 0x1801fc29+127 128 11074 0x1801faef+128 129 11073 0x1801f9b7+129 130 11072 0x1801f87f+130 131 11071 0x1801f748+131 132 11070 0x1801f611+132 133 11069 0x1801f4db+133 134 11068 0x1801f3a6+134 135 11067 0x1801f272+135 136 11066 0x1801f13e+136 137 11065 0x1801f00b+137 138 11064 0x1801eed9+138 139 11063 0x1801eda9+139 140 11062 0x1801ec78+140 141 11061 0x1801eb49+141 142 11060 0x1801ea1a+142 143 11059 0x1801e8ec+143 144 11058 0x1801e7be+144 145 11057 0x1801e692+145 146 11056 0x1801e565+146 147 11055 0x1801e43a+147 148 11054 0x1801e310+148 149 11053 0x1801e1e6+149 150 11052 0x1801e0bc+150 151 11051 0x1801df94+151 152 11050 0x1801de6d+152 153 11049 0x1801dd46+153 154 11048 0x1801dc1e+154 155 11047 0x1801dafa+155 156 11046 0x1801d9d6+156 157 11045 0x1801d8b1+157 158 11044 0x1801d78e+158 159 11043 0x1801d66c+159 160 11042 0x1801d549+160 161 11041 0x1801d429+161 162 11040 0x1801d308+162 163 11039 0x1801d1e8+163 164 11038 0x1801d0c9+164 165 11037 0x1801cfa9+165 166 11036 0x1801ce8c+166 167 11035 0x1801cd6f+167 168 11034 0x1801cc53+168 169 11033 0x1801cb36+169 170 11032 0x1801ca1b+170 171 11031 0x1801c903+171 172 11030 0x1801c7e9+172 173 11029 0x1801c6cf+173 174 11028 0x1801c5b7+174 175 11027 0x1801c4a0+175 176 11026 0x1801c389+176 177 11025 0x1801c273+177 178 11024 0x1801c15e+178 179 11023 0x1801c048+179 180 11022 0x1801bf35+180 181 11021 0x1801be20+181 182 11020 0x1801bd0d+182 183 11019 0x1801bbfc+183 184 11018 0x1801baea+184 185 11017 0x1801b9da+185 186 11016 0x1801b8c9+186 187 11015 0x1801b7ba+187 188 11014 0x1801b6ab+188 189 11013 0x1801b59e+189 190 11012 0x1801b490+190 191 11011 0x1801b382+191 192 11010 0x1801b276+192 193 11009 0x1801b16b+193 194 11008 0x1801b060+194 195 11007 0x1801af55+195 196 11006 0x1801ae4b+196 197 11005 0x1801ad43+197 198 11004 0x1801ac3b+198 199 11003 0x1801ab32+199 200 11002 0x1801aa2c+200 201 11001 0x1801a925+201 202 11000 0x1801a820+202 203 10999 0x1801a71b+203 204 10998 0x1801a616+204 205 10997 0x1801a512+205 206 10996 0x1801a40f+206 207 10995 0x1801a30c+207 208 10994 0x1801a20b+208 209 10993 0x1801a109+209 210 10992 0x1801a009+210 211 10991 0x18019f09+211 212 10990 0x18019e08+212 213 10989 0x18019d09+213 214 10988 0x18019c0b+214 215 10987 0x18019b0d+215 216 10986 0x18019a11+216 217 10985 0x18019915+217 218 10984 0x18019818+218 219 10983 0x1801971d+219 220 10982 0x18019623+220 221 10981 0x18019529+221 222 10980 0x1801942f+222 223 10979 0x18019336+223 224 10978 0x1801923e+224 225 10977 0x18019146+225 226 10976 0x1801904f+226 227 10975 0x18018f59+227 228 10974 0x18018e62+228 229 10973 0x18018d6d+229 230 10972 0x18018c79+230 231 10971 0x18018b84+231 232 10970 0x18018a90+232 233 10969 0x1801899f+233 234 10968 0x180188ac+234 235 10967 0x180187ba+235 236 10966 0x180186c9+236 237 10965 0x180185d9+237 238 10964 0x180184e9+238 239 10963 0x180183f9+239 240 10962 0x1801830a+240 241 10961 0x1801821b+241 242 10960 0x1801812d+242 243 10959 0x18018040+243 244 10958 0x18017f54+244 245 10957 0x18017e68+245 246 10956 0x18017d7c+246 247 10955 0x18017c91+247 248 10954 0x18017ba7+248 249 10953 0x18017abd+249 250 10952 0x180179d3+250 251 10951 0x180178eb+251 252 10950 0x18017803+252 253 10949 0x1801771b+253 254 10948 0x18017634+254 255 10947 0x1801754d+255 256 10946 0x18017467+256 257 10945 0x18017381+257 258 10944 0x1801729c+258 259 10943 0x180171b8+259 260 10942 0x180170d4+260 261 10941 0x18016ff0+261 262 10940 0x18016f0d+262 263 10939 0x18016e2b+263 264 10938 0x18016d4a+264 265 10937 0x18016c69+265 266 10936 0x18016b87+266 267 10935 0x18016aa7+267 268 10934 0x180169c7+268 269 10933 0x180168e8+269 270 10932 0x1801680a+270 271 10931 0x1801672b+271 272 10930 0x1801664e+272 273 10929 0x18016570+273 274 10928 0x18016494+274 275 10927 0x180163b8+275 276 10926 0x180162dc+276 277 10925 0x18016202+277 278 10924 0x18016127+278 279 10923 0x1801604d+279 280 10922 0x18015f73+280 281 10921 0x18015e9b+281 282 10920 0x18015dc1+282 283 10919 0x18015ce9+283 284 10918 0x18015c11+284 285 10917 0x18015b3b+285 286 10916 0x18015a64+286 287 10915 0x1801598d+287 288 10914 0x180158b8+288 289 10913 0x180157e3+289 290 10912 0x1801570f+290 291 10911 0x1801563c+291 292 10910 0x18015569+292 293 10909 0x18015497+293 294 10908 0x180153c6+294 295 10907 0x180152f6+295 296 10906 0x18015225+296 297 10905 0x18015154+297 298 10904 0x18015085+298 299 10903 0x18014fb6+299 300 10902 0x18014ee7+300 301 10901 0x18014e19+301 302 10900 0x18014d4b+302 303 10899 0x18014c7e+303 304 10898 0x18014bb2+304 305 10897 0x18014ae6+305 306 10896 0x18014a1a+306 307 10895 0x1801494e+307 308 10894 0x18014884+308 309 10893 0x180147ba+309 310 10892 0x180146f0+310 311 10891 0x18014627+311 312 10890 0x1801455e+312 313 10889 0x18014496+313 314 10888 0x180143ce+314 315 10887 0x18014306+315 316 10886 0x1801423f+316 317 10885 0x18014179+317 318 10884 0x180140b2+318 319 10883 0x18013fed+319 320 10882 0x18013f28+320 321 10881 0x18013e63+321 322 10880 0x18013d9f+322 323 10879 0x18013cdb+323 324 10878 0x18013c18+324 325 10877 0x18013b56+325 326 10876 0x18013a93+326 327 10875 0x180139d1+327 328 10874 0x1801390f+328 329 10873 0x1801384f+329 330 10872 0x1801378e+330 331 10871 0x180136ce+331 332 10870 0x1801360e+332 333 10869 0x1801354f+333 334 10868 0x18013490+334 335 10867 0x180133d2+335 336 10866 0x18013314+336 337 10865 0x18013257+337 338 10864 0x1801319a+338 339 10863 0x180130dd+339 340 10862 0x18013022+340 341 10861 0x18012f66+341 342 10860 0x18012eab+342 343 10859 0x18012df1+343 344 10858 0x18012d36+344 345 10857 0x18012c7c+345 346 10856 0x18012bc3+346 347 10855 0x18012b0a+347 348 10854 0x18012a52+348 349 10853 0x1801299a+349 350 10852 0x180128e2+350 351 10851 0x1801282b+351 352 10850 0x18012774+352 353 10849 0x180126be+353 354 10848 0x18012608+354 355 10847 0x18012553+355 356 10846 0x1801249e+356 357 10845 0x180123e9+357 358 10844 0x18012336+358 359 10843 0x18012282+359 360 10842 0x180121cf+360 361 10841 0x1801211c+361 362 10840 0x18012069+362 363 10839 0x18011fb7+363 364 10838 0x18011f06+364 365 10837 0x18011e55+365 366 10836 0x18011da3+366 367 10835 0x18011cf3+367 368 10834 0x18011c43+368 369 10833 0x18011b94+369 370 10832 0x18011ae5+370 371 10831 0x18011a37+371 372 10830 0x18011989+372 373 10829 0x180118db+373 374 10828 0x1801182e+374 375 10827 0x18011781+375 376 10826 0x180116d4+376 377 10825 0x18011628+377 378 10824 0x1801157d+378 379 10823 0x180114d2+379 380 10822 0x18011426+380 381 10821 0x1801137c+381 382 10820 0x180112d2+382 383 10819 0x18011228+383 384 10818 0x1801117f+384 385 10817 0x180110d6+385 386 10816 0x1801102e+386 387 10815 0x18010f87+387 388 10814 0x18010edf+388 389 10813 0x18010e38+389 390 10812 0x18010d91+390 391 10811 0x18010cea+391 392 10810 0x18010c45+392 393 10809 0x18010b9f+393 394 10808 0x18010afa+394 395 10807 0x18010a55+395 396 10806 0x180109b1+396 397 10805 0x1801090c+397 398 10804 0x18010869+398 399 10803 0x180107c6+399 400 10802 0x18010723+400 401 10801 0x18010682+401 402 10800 0x180105df+402 403 10799 0x1801053e+403 404 10798 0x1801049d+404 405 10797 0x180103fc+405 406 10796 0x1801035b+406 407 10795 0x180102bb+407 408 10794 0x1801021b+408 409 10793 0x1801017c+409 410 10792 0x180100dd+410 411 10791 0x1801003f+411 412 10790 0x1800ffa1+412 413 10789 0x1800ff03+413 414 10788 0x1800fe65+414 415 10787 0x1800fdc8+415 416 10786 0x1800fd2c+416 417 10785 0x1800fc91+417 418 10784 0x1800fbf5+418 419 10783 0x1800fb59+419 420 10782 0x1800fabe+420 421 10781 0x1800fa23+421 422 10780 0x1800f989+422 423 10779 0x1800f8ef+423 424 10778 0x1800f856+424 425 10777 0x1800f7bc+425 426 10776 0x1800f723+426 427 10775 0x1800f68b+427 428 10774 0x1800f5f3+428 429 10773 0x1800f55b+429 430 10772 0x1800f4c4+430 431 10771 0x1800f42d+431 432 10770 0x1800f397+432 433 10769 0x1800f301+433 434 10768 0x1800f26b+434 435 10767 0x1800f1d6+435 436 10766 0x1800f140+436 437 10765 0x1800f0ac+437 438 10764 0x1800f017+438 439 10763 0x1800ef83+439 440 10762 0x1800eeef+440 441 10761 0x1800ee5c+441 442 10760 0x1800edc9+442 443 10759 0x1800ed36+443 444 10758 0x1800eca4+444 445 10757 0x1800ec13+445 446 10756 0x1800eb81+446 447 10755 0x1800eaf0+447 448 10754 0x1800ea5f+448 449 10753 0x1800e9cf+449 450 10752 0x1800e93f+450 451 10751 0x1800e8af+451 452 10750 0x1800e820+452 453 10749 0x1800e790+453 454 10748 0x1800e702+454 455 10747 0x1800e673+455 456 10746 0x1800e5e5+456 457 10745 0x1800e557+457 458 10744 0x1800e4ca+458 459 10743 0x1800e43d+459 460 10742 0x1800e3b1+460 461 10741 0x1800e324+461 462 10740 0x1800e298+462 463 10739 0x1800e20d+463 464 10738 0x1800e182+464 465 10737 0x1800e0f7+465 466 10736 0x1800e06c+466 467 10735 0x1800dfe2+467 468 10734 0x1800df58+468 469 10733 0x1800dece+469 470 10732 0x1800de45+470 471 10731 0x1800ddbc+471 472 10730 0x1800dd34+472 473 10729 0x1800dcac+473 474 10728 0x1800dc23+474 475 10727 0x1800db9c+475 476 10726 0x1800db14+476 477 10725 0x1800da8d+477 478 10724 0x1800da07+478 479 10723 0x1800d981+479 480 10722 0x1800d8fb+480 481 10721 0x1800d876+481 482 10720 0x1800d7f0+482 483 10719 0x1800d76b+483 484 10718 0x1800d6e6+484 485 10717 0x1800d662+485 486 10716 0x1800d5de+486 487 10715 0x1800d55a+487 488 10714 0x1800d4d6+488 489 10713 0x1800d454+489 490 10712 0x1800d3d1+490 491 10711 0x1800d34f+491 492 10710 0x1800d2cd+492 493 10709 0x1800d24b+493 494 10708 0x1800d1ca+494 495 10707 0x1800d148+495 496 10706 0x1800d0c7+496 497 10705 0x1800d047+497 498 10704 0x1800cfc6+498 499 10703 0x1800cf46+499 500 10702 0x1800cec7+500 501 10701 0x1800ce47+501 502 10700 0x1800cdc9+502 503 10699 0x1800cd4a+503 504 10698 0x1800cccb+504 505 10697 0x1800cc4d+505 506 10696 0x1800cbd0+506 507 10695 0x1800cb52+507 508 10694 0x1800cad5+508 509 10693 0x1800ca59+509 510 10692 0x1800c9dc+510 511 10691 0x1800c960+511 512 10690 0x1800c8e4+512 513 10689 0x1800c867+513 514 10688 0x1800c7ec+514 515 10687 0x1800c771+515 516 10686 0x1800c6f7+516 517 10685 0x1800c67c+517 518 10684 0x1800c602+518 519 10683 0x1800c588+519 520 10682 0x1800c50e+520 521 10681 0x1800c495+521 522 10680 0x1800c41b+522 523 10679 0x1800c3a3+523 524 10678 0x1800c32b+524 525 10677 0x1800c2b2+525 526 10676 0x1800c23a+526 527 10675 0x1800c1c3+527 528 10674 0x1800c14c+528 529 10673 0x1800c0d4+529 530 10672 0x1800c05d+530 531 10671 0x1800bfe7+531 532 10670 0x1800bf71+532 533 10669 0x1800befb+533 534 10668 0x1800be85+534 535 10667 0x1800be10+535 536 10666 0x1800bd9a+536 537 10665 0x1800bd26+537 538 10664 0x1800bcb2+538 539 10663 0x1800bc3e+539 540 10662 0x1800bbca+540 541 10661 0x1800bb55+541 542 10660 0x1800bae2+542 543 10659 0x1800ba6f+543 544 10658 0x1800b9fc+544 545 10657 0x1800b989+545 546 10656 0x1800b917+546 547 10655 0x1800b8a5+547 548 10654 0x1800b833+548 549 10653 0x1800b7c1+549 550 10652 0x1800b750+550 551 10651 0x1800b6df+551 552 10650 0x1800b66e+552 553 10649 0x1800b5fe+553 554 10648 0x1800b58e+554 555 10647 0x1800b51e+555 556 10646 0x1800b4ae+556 557 10645 0x1800b43f+557 558 10644 0x1800b3d0+558 559 10643 0x1800b360+559 560 10642 0x1800b2f2+560 561 10641 0x1800b284+561 562 10640 0x1800b215+562 563 10639 0x1800b1a7+563 564 10638 0x1800b13a+564 565 10637 0x1800b0cc+565 566 10636 0x1800b05f+566 567 10635 0x1800aff1+567 568 10634 0x1800af85+568 569 10633 0x1800af19+569 570 10632 0x1800aead+570 571 10631 0x1800ae41+571 572 10630 0x1800add5+572 573 10629 0x1800ad69+573 574 10628 0x1800acfe+574 575 10627 0x1800ac94+575 576 10626 0x1800ac29+576 577 10625 0x1800abbe+577 578 10624 0x1800ab55+578 579 10623 0x1800aaeb+579 580 10622 0x1800aa82+580 581 10621 0x1800aa19+581 582 10620 0x1800a9b1+582 583 10619 0x1800a948+583 584 10618 0x1800a8e0+584 585 10617 0x1800a878+585 586 10616 0x1800a811+586 587 10615 0x1800a7a9+587 588 10614 0x1800a742+588 589 10613 0x1800a6db+589 590 10612 0x1800a675+590 591 10611 0x1800a60e+591 592 10610 0x1800a5a8+592 593 10609 0x1800a542+593 594 10608 0x1800a4dc+594 595 10607 0x1800a476+595 596 10606 0x1800a411+596 597 10605 0x1800a3ac+597 598 10604 0x1800a347+598 599 10603 0x1800a2e3+599 600 10602 0x1800a27e+600 601 10601 0x1800a21b+601 602 10600 0x1800a1b7+602 603 10599 0x1800a153+603 604 10598 0x1800a0f0+604 605 10597 0x1800a08d+605 606 10596 0x1800a02a+606 607 10595 0x18009fc7+607 608 10594 0x18009f64+608 609 10593 0x18009f02+609 610 10592 0x18009ea0+610 611 10591 0x18009e3f+611 612 10590 0x18009ddd+612 613 10589 0x18009d7c+613 614 10588 0x18009d1a+614 615 10587 0x18009cba+615 616 10586 0x18009c5a+616 617 10585 0x18009bf9+617 618 10584 0x18009b99+618 619 10583 0x18009b39+619 620 10582 0x18009ad9+620 621 10581 0x18009a7a+621 622 10580 0x18009a1a+622 623 10579 0x180099bb+623 624 10578 0x1800995c+624 625 10577 0x180098fe+625 626 10576 0x180098a0+626 627 10575 0x18009841+627 628 10574 0x180097e3+628 629 10573 0x18009786+629 630 10572 0x18009728+630 631 10571 0x180096cb+631 632 10570 0x1800966f+632 633 10569 0x18009612+633 634 10568 0x180095b5+634 635 10567 0x18009559+635 636 10566 0x180094fd+636 637 10565 0x180094a1+637 638 10564 0x18009445+638 639 10563 0x180093ea+639 640 10562 0x1800938e+640 641 10561 0x18009333+641 642 10560 0x180092d8+642 643 10559 0x1800927e+643 644 10558 0x18009223+644 645 10557 0x180091c9+645 646 10556 0x1800916f+646 647 10555 0x18009116+647 648 10554 0x180090bc+648 649 10553 0x18009063+649 650 10552 0x1800900a+650 651 10551 0x18008fb1+651 652 10550 0x18008f58+652 653 10549 0x18008f00+653 654 10548 0x18008ea8+654 655 10547 0x18008e4f+655 656 10546 0x18008df7+656 657 10545 0x18008da0+657 658 10544 0x18008d48+658 659 10543 0x18008cf1+659 660 10542 0x18008c9a+660 661 10541 0x18008c43+661 662 10540 0x18008bed+662 663 10539 0x18008b97+663 664 10538 0x18008b41+664 665 10537 0x18008aeb+665 666 10536 0x18008a95+666 667 10535 0x18008a40+667 668 10534 0x180089ea+668 669 10533 0x18008995+669 670 10532 0x18008940+670 671 10531 0x180088ec+671 672 10530 0x18008897+672 673 10529 0x18008842+673 674 10528 0x180087ef+674 675 10527 0x1800879a+675 676 10526 0x18008747+676 677 10525 0x180086f3+677 678 10524 0x180086a1+678 679 10523 0x1800864d+679 680 10522 0x180085fb+680 681 10521 0x180085a8+681 682 10520 0x18008555+682 683 10519 0x18008503+683 684 10518 0x180084b1+684 685 10517 0x1800845f+685 686 10516 0x1800840d+686 687 10515 0x180083bc+687 688 10514 0x1800836a+688 689 10513 0x18008319+689 690 10512 0x180082c8+690 691 10511 0x18008278+691 692 10510 0x18008227+692 693 10509 0x180081d7+693 694 10508 0x18008187+694 695 10507 0x18008137+695 696 10506 0x180080e8+696 697 10505 0x18008098+697 698 10504 0x18008049+698 699 10503 0x177ff9df+699 700 10502 0x177faab7+700 701 10501 0x177f5c3b+701 702 10500 0x177f0d69+702 703 10499 0x177ebeed+703 704 10498 0x177e70c7+704 705 10497 0x177e22f7+705 706 10496 0x177dd527+706 707 10495 0x177d8756+707 708 10494 0x177d3a32+708 709 10493 0x177ced0d+709 710 10492 0x177c9fe9+710 711 10491 0x177c531a+711 712 10490 0x177c064b+712 713 10489 0x177bb9d2+713 714 10488 0x177b6db0+714 715 10487 0x177b218d+715 716 10486 0x177ad56a+716 717 10485 0x177a899c+717 718 10484 0x177a3e25+718 719 10483 0x1779f2ae+719 720 10482 0x1779a78d+720 721 10481 0x17795c6b+721 722 10480 0x177911a0+722 723 10479 0x1778c6d4+723 724 10478 0x17787cb4+724 725 10477 0x17783295+725 726 10476 0x1777e875+726 727 10475 0x17779e55+727 728 10474 0x1777548b+728 729 10473 0x17770b17+729 730 10472 0x1776c1a3+730 731 10471 0x17767885+731 732 10470 0x17762f67+732 733 10469 0x1775e69e+733 734 10468 0x17759dd6+734 735 10467 0x17755564+735 736 10466 0x17750cf1+736 737 10465 0x1774c4d4+737 738 10464 0x17747cb8+738 739 10463 0x17743547+739 740 10462 0x1773edd6+740 741 10461 0x1773a665+741 742 10460 0x17735ef4+742 743 10459 0x177317d9+743 744 10458 0x1772d114+744 745 10457 0x17728a4f+745 746 10456 0x1772438a+746 747 10455 0x1771fd1b+747 748 10454 0x1771b701+748 749 10453 0x177170e8+749 750 10452 0x17712ace+750 751 10451 0x1770e50b+751 752 10450 0x17709f9d+752 753 10449 0x17705a2f+753 754 10448 0x17701517+754 755 10447 0x176fd056+755 756 10446 0x176f8b3e+756 757 10445 0x176f467c+757 758 10444 0x176f0210+758 759 10443 0x176ebda3+759 760 10442 0x176e7937+760 761 10441 0x176e3577+761 762 10440 0x176df161+762 763 10439 0x176dada0+763 764 10438 0x176d6a36+764 765 10437 0x176d26cb+765 766 10436 0x176ce361+766 767 10435 0x176ca04c+767 768 10434 0x176c5d8d+768 769 10433 0x176c1acf+769 770 10432 0x176bd866+770 771 10431 0x176b95fd+771 772 10430 0x176b5394+772 773 10429 0x176b1181+773 774 10428 0x176acfc4+774 775 10427 0x176a8e06+775 776 10426 0x176a4c49+776 777 10425 0x176a0ae2+777 778 10424 0x1769c97a+778 779 10423 0x17698869+779 780 10422 0x17694757+780 781 10421 0x1769069c+781 782 10420 0x1768c5e0+782 783 10419 0x17688525+783 784 10418 0x17684515+784 785 10417 0x17680505+785 786 10416 0x1767c4f5+786 787 10415 0x176784e5+787 788 10414 0x1767452b+788 789 10413 0x176705c7+789 790 10412 0x1766c663+790 791 10411 0x176686ff+791 792 10410 0x176647f0+792 793 10409 0x176608e2+793 794 10408 0x1765c9d4+794 795 10407 0x17658b71+795 796 10406 0x17654cb9+796 797 10405 0x17650e56+797 798 10404 0x1764d049+798 799 10403 0x176491e7+799 800 10402 0x17645486+800 801 10401 0x17641679+801 802 10400 0x1763d918+802 803 10399 0x17639b61+803 804 10398 0x17635e00+804 805 10397 0x176320f5+805 806 10396 0x1762e3ea+806 807 10395 0x1762a6df+807 808 10394 0x17626a2a+808 809 10393 0x17622d74+809 810 10392 0x1761f115+810 811 10391 0x1761b4b6+811 812 10390 0x176178ac+812 813 10389 0x17613ca3+813 814 10388 0x17610099+814 815 10387 0x1760c4e5+815 816 10386 0x17608988+816 817 10385 0x17604e2a+817 818 10384 0x176012cc+818 819 10383 0x175fd76e+819 820 10382 0x175f9c66+820 821 10381 0x175f61b4+821 822 10380 0x175f26ac+822 823 10379 0x175eec50+823 824 10378 0x175eb19e+824 825 10377 0x175e7742+825 826 10376 0x175e3d3b+826 827 10375 0x175e02df+827 828 10374 0x175dc92f+828 829 10373 0x175d8f28+829 830 10372 0x175d5578+830 831 10371 0x175d1c73+831 832 10370 0x175ce2c3+832 833 10369 0x175ca9be+833 834 10368 0x175c7063+834 835 10367 0x175c375f+835 836 10366 0x175bfeb0+836 837 10365 0x175bc601+837 838 10364 0x175b8d52+838 839 10363 0x175b54f9+839 840 10362 0x175b1ca0+840 841 10361 0x175ae447+841 842 10360 0x175aac44+842 843 10359 0x175a7440+843 844 10358 0x175a3c93+844 845 10357 0x175a04e6+845 846 10356 0x1759cd8e+846 847 10355 0x175995e1+847 848 10354 0x17595e8a+848 849 10353 0x17592788+849 850 10352 0x1758f086+850 851 10351 0x1758b985+851 852 10350 0x175882d9+852 853 10349 0x17584c2d+853 854 10348 0x17581582+854 855 10347 0x1757df2c+855 856 10346 0x1757a8d6+856 857 10345 0x17577280+857 858 10344 0x17573c80+858 859 10343 0x17570680+859 860 10342 0x1756d0d6+860 861 10341 0x17569b2c+861 862 10340 0x175665d8+862 863 10339 0x17563083+863 864 10338 0x1755fb2f+864 865 10337 0x1755c606+865 866 10336 0x1755915d+866 867 10335 0x17555cb5+867 868 10334 0x17552837+868 869 10333 0x1754f3ba+869 870 10332 0x1754bf92+870 871 10331 0x17548b6a+871 872 10330 0x1754576d+872 873 10329 0x17542370+873 874 10328 0x1753ef9f+874 875 10327 0x1753bbf8+875 876 10326 0x1753887c+876 877 10325 0x17535556+877 878 10324 0x17532204+878 879 10323 0x1752eede+879 880 10322 0x1752bbb8+880 881 10321 0x175288bd+881 882 10320 0x175255ed+882 883 10319 0x17522347+883 884 10318 0x1751f0a2+884 885 10317 0x1751be27+885 886 10316 0x17518bd8+886 887 10315 0x17515988+887 888 10314 0x1751278f+888 889 10313 0x1750f56a+889 890 10312 0x1750c39c+890 891 10311 0x175091cd+891 892 10310 0x1750607f+892 893 10309 0x17502edb+893 894 10308 0x174ffd8d+894 895 10307 0x174fcc3f+895 896 10306 0x174f9af2+896 897 10305 0x174f69f9+897 898 10304 0x174f3901+898 899 10303 0x174f0834+899 900 10302 0x174ed767+900 901 10301 0x174ea6c5+901 902 10300 0x174e764e+902 903 10299 0x174e4602+903 904 10298 0x174e15b5+904 905 10297 0x174de594+905 906 10296 0x174db572+906 907 10295 0x174d85a7+907 908 10294 0x174d5606+908 909 10293 0x174d263b+909 910 10292 0x174cf6c5+910 911 10291 0x174cc750+911 912 10290 0x174c97da+912 913 10289 0x174c68ba+913 914 10288 0x174c399a+914 915 10287 0x174c0a7a+915 916 10286 0x174bdb86+916 917 10285 0x174bacbc+917 918 10284 0x174b7e1d+918 919 10283 0x174b4f7e+919 920 10282 0x174b210a+920 921 10281 0x174af2c1+921 922 10280 0x174ac477+922 923 10279 0x174a9684+923 924 10278 0x174a6891+924 925 10277 0x174a3a9e+925 926 10276 0x174a0cd5+926 927 10275 0x1749df38+927 928 10274 0x1749b19a+928 929 10273 0x17498428+929 930 10272 0x174956b5+930 931 10271 0x1749296e+931 932 10270 0x1748fc51+932 933 10269 0x1748cf35+933 934 10268 0x1748a243+934 935 10267 0x1748757c+935 936 10266 0x174848b6+936 937 10265 0x17481c1a+937 938 10264 0x1747efd4+938 939 10263 0x1747c363+939 940 10262 0x1747971d+940 941 10261 0x17476b02+941 942 10260 0x17473ee7+942 943 10259 0x174712f7+943 944 10258 0x1746e707+944 945 10257 0x1746bb42+945 946 10256 0x17468fa7+946 947 10255 0x1746640d+947 948 10254 0x1746389e+948 949 10253 0x17460d59+949 950 10252 0x1745e215+950 951 10251 0x1745b6fc+951 952 10250 0x17458be2+952 953 10249 0x174560f4+953 954 10248 0x1745365b+954 955 10247 0x17450b97+955 956 10246 0x1744e0ff+956 957 10245 0x1744b666+957 958 10244 0x17448bf9+958 959 10243 0x174461b6+959 960 10242 0x17443773+960 961 10241 0x17440d5b+961 962 10240 0x1743e36e+962 963 10239 0x1743b981+963 964 10238 0x17438fbf+964 965 10237 0x174365fd+965 966 10236 0x17433c66+966 967 10235 0x174312cf+967 968 10234 0x1742e98e+968 969 10233 0x1742c04d+969 970 10232 0x17429737+970 971 10231 0x17426e20+971 972 10230 0x1742450a+972 973 10229 0x17421c1f+973 974 10228 0x1741f35e+974 975 10227 0x1741ca9e+975 976 10226 0x1741a209+976 977 10225 0x1741799e+977 978 10224 0x17415134+978 979 10223 0x174128c9+979 980 10222 0x17410089+980 981 10221 0x1740d875+981 982 10220 0x1740b08b+982 983 10219 0x174088a1+983 984 10218 0x174060e2+984 985 10217 0x17403924+985 986 10216 0x17401190+986 987 10215 0x173fe9fc+987 988 10214 0x173fc293+988 989 10213 0x173f9b2a+989 990 10212 0x173f73ec+990 991 10211 0x173f4cd9+991 992 10210 0x173f25c6+992 993 10209 0x173efede+993 994 10208 0x173ed7f6+994 995 10207 0x173eb138+995 996 10206 0x173e8a7b+996 997 10205 0x173e63e9+997 998 10204 0x173e3d82+998 999 10203 0x173e171a+999 1000 10202 0x173df0de+1000 1001 10201 0x173dcaa1+1001 1002 10200 0x173da490+1002 1003 10199 0x173d7e7e+1003 1004 10198 0x173d5898+1004 1005 10197 0x173d32b1+1005 1006 10196 0x173d0cf6+1006 1007 10195 0x173ce765+1007 1008 10194 0x173cc1d4+1008 1009 10193 0x173c9c44+1009 1010 10192 0x173c7709+1010 1011 10191 0x173c51a3+1011 1012 10190 0x173c2c68+1012 1013 10189 0x173c0758+1013 1014 10188 0x173be248+1014 1015 10187 0x173bbd8e+1015 1016 10186 0x173b98aa+1016 1017 10185 0x173b73ef+1017 1018 10184 0x173b4f60+1018 1019 10183 0x173b2ad1+1019 1020 10182 0x173b0642+1020 1021 10181 0x173ae1de+1021 1022 10180 0x173abda5+1022 1023 10179 0x173a996c+1023 1024 10178 0x173a7532+1024 1025 10177 0x173a5124+1025 1026 10176 0x173a2d41+1026 1027 10175 0x173a095d+1027 1028 10174 0x1739e5a5+1028 1029 10173 0x1739c1ec+1029 1030 10172 0x17399e8a+1030 1031 10171 0x17397afc+1031 1032 10170 0x1739579a+1032 1033 10169 0x17393437+1033 1034 10168 0x17391100+1034 1035 10167 0x1738edc8+1035 1036 10166 0x1738cabb+1036 1037 10165 0x1738a7ae+1037 1038 10164 0x173884cd+1038 1039 10163 0x173861eb+1039 1040 10162 0x17383f34+1040 1041 10161 0x17381c7d+1041 1042 10160 0x1737f9f1+1042 1043 10159 0x1737d765+1043 1044 10158 0x1737b504+1044 1045 10157 0x173792ce+1045 1046 10156 0x17377098+1046 1047 10155 0x17374e8d+1047 1048 10154 0x17372c57+1048 1049 10153 0x17370a77+1049 1050 10152 0x1736e897+1050 1051 10151 0x1736c6b6+1051 1052 10150 0x1736a501+1052 1053 10149 0x1736834c+1053 1054 10148 0x173661c2+1054 1055 10147 0x17364037+1055 1056 10146 0x17361ed8+1056 1057 10145 0x1735fd79+1057 1058 10144 0x1735dc19+1058 1059 10143 0x1735bb10+1059 1060 10142 0x173599db+1060 1061 10141 0x173578fd+1061 1062 10140 0x1735581e+1062 1063 10139 0x1735373f+1063 1064 10138 0x1735168c+1064 1065 10137 0x1734f5d8+1065 1066 10136 0x1734d524+1066 1067 10135 0x1734b49c+1067 1068 10134 0x17349413+1068 1069 10133 0x173473b5+1069 1070 10132 0x17345382+1070 1071 10131 0x1734334f+1071 1072 10130 0x1734131c+1072 1073 10129 0x1733f314+1073 1074 10128 0x1733d30d+1074 1075 10127 0x1733b330+1075 1076 10126 0x1733937d+1076 1077 10125 0x173373a0+1077 1078 10124 0x173353ee+1078 1079 10123 0x17333467+1079 1080 10122 0x173314e0+1080 1081 10121 0x1732f559+1081 1082 10120 0x1732d5fd+1082 1083 10119 0x1732b6a0+1083 1084 10118 0x1732976f+1084 1085 10117 0x1732783e+1085 1086 10116 0x17325938+1086 1087 10115 0x17323a31+1087 1088 10114 0x17321b56+1088 1089 10113 0x1731fc7a+1089 1090 10112 0x1731dd9f+1090 1091 10111 0x1731beee+1091 1092 10110 0x1731a069+1092 1093 10109 0x173181e3+1093 1094 10108 0x1731635e+1094 1095 10107 0x17314503+1095 1096 10106 0x173126a8+1096 1097 10105 0x1731084e+1097 1098 10104 0x1730ea1e+1098 1099 10103 0x1730cbee+1099 1100 10102 0x1730adea+1100 1101 10101 0x17308fe5+1101 1102 10100 0x1730720b+1102 1103 10099 0x17305431+1103 1104 10098 0x17303657+1104 1105 10097 0x173018a9+1105 1106 10096 0x172ffb25+1106 1107 10095 0x172fdda1+1107 1108 10094 0x172fc01d+1108 1109 10093 0x172fa2c4+1109 1110 10092 0x172f856b+1110 1111 10091 0x172f6812+1111 1112 10090 0x172f4ae3+1112 1113 10089 0x172f2db5+1113 1114 10088 0x172f10b2+1114 1115 10087 0x172ef3af+1115 1116 10086 0x172ed6ac+1116 1117 10085 0x172eb9d4+1117 1118 10084 0x172e9cfb+1118 1119 10083 0x172e804e+1119 1120 10082 0x172e63a1+1120 1121 10081 0x172e46f3+1121 1122 10080 0x172e2a9c+1122 1123 10079 0x172e0e1a+1123 1124 10078 0x172df1c2+1124 1125 10077 0x172dd56b+1125 1126 10076 0x172db913+1126 1127 10075 0x172d9ce7+1127 1128 10074 0x172d80ba+1128 1129 10073 0x172d64b9+1129 1130 10072 0x172d48b7+1130 1131 10071 0x172d2cb5+1131 1132 10070 0x172d10df+1132 1133 10069 0x172cf508+1133 1134 10068 0x172cd95c+1134 1135 10067 0x172cbdb1+1135 1136 10066 0x172ca205+1136 1137 10065 0x172c8684+1137 1138 10064 0x172c6b03+1138 1139 10063 0x172c4fae+1139 1140 10062 0x172c3458+1140 1141 10061 0x172c1902+1141 1142 10060 0x172bfdac+1142 1143 10059 0x172be281+1143 1144 10058 0x172bc781+1144 1145 10057 0x172bac56+1145 1146 10056 0x172b9156+1146 1147 10055 0x172b7681+1147 1148 10054 0x172b5bac+1148 1149 10053 0x172b40d7+1149 1150 10052 0x172b2602+1150 1151 10051 0x172b0b58+1151 1152 10050 0x172af0ae+1152 1153 10049 0x172ad66f+1153 1154 10048 0x172abc1b+1154 1155 10047 0x172aa1c6+1155 1156 10046 0x172a8787+1156 1157 10045 0x172a6d5e+1157 1158 10044 0x172a534a+1158 1159 10043 0x172a3936+1159 1160 10042 0x172a1f4d+1160 1161 10041 0x172a054f+1161 1162 10040 0x1729eb7c+1162 1163 10039 0x1729d1a8+1163 1164 10038 0x1729b7ea+1164 1165 10037 0x17299e42+1165 1166 10036 0x17298499+1166 1167 10035 0x17296b1b+1167 1168 10034 0x172951b3+1168 1169 10033 0x17293836+1169 1170 10032 0x17291ee3+1170 1171 10031 0x17290590+1171 1172 10030 0x1728ec3e+1172 1173 10029 0x1728d316+1173 1174 10028 0x1728b9ee+1174 1175 10027 0x1728a0c6+1175 1176 10026 0x172887ca+1176 1177 10025 0x17286ecd+1177 1178 10024 0x172855e5+1178 1179 10023 0x17283d14+1179 1180 10022 0x17282442+1180 1181 10021 0x17280b85+1181 1182 10020 0x1727f2de+1182 1183 10019 0x1727da37+1183 1184 10018 0x1727c1bb+1184 1185 10017 0x1727a93f+1185 1186 10016 0x172790d9+1186 1187 10015 0x17277872+1187 1188 10014 0x1727600c+1188 1189 10013 0x172747d0+1189 1190 10012 0x17272f94+1190 1191 10011 0x1727176e+1191 1192 10010 0x1726ff5e+1192 1193 10009 0x1726e74d+1193 1194 10008 0x1726cf52+1194 1195 10007 0x1726b756+1195 1196 10006 0x17269f86+1196 1197 10005 0x172687a0+1197 1198 10004 0x17266fe6+1198 1199 10003 0x17265856+1199 1200 10002 0x172640b0+1200 1201 10001 0x1726290b+1201 1202 10000 0x1726117b+1202 1203 9999 0x1725fa01+1203 1204 9998 0x1725e29c+1204 1205 9997 0x1725cb37+1205 1206 9996 0x1725b3e7+1206 1207 9995 0x17259cad+1207 1208 9994 0x17258573+1208 1209 9993 0x17256e4f+1209 1210 9992 0x1725573f+1210 1211 9991 0x17254030+1211 1212 9990 0x17252937+1212 1213 9989 0x1725123d+1213 1214 9988 0x1724fb84+1214 1215 9987 0x1724e4a0+1215 1216 9986 0x1724cde6+1216 1217 9985 0x1724b72d+1217 1218 9984 0x1724a089+1218 1219 9983 0x172489e6+1219 1220 9982 0x17247357+1220 1221 9981 0x17245cde+1221 1222 9980 0x17244666+1222 1223 9979 0x17243002+1223 1224 9978 0x172419b4+1224 1225 9977 0x17240366+1225 1226 9976 0x1723ed2e+1226 1227 9975 0x1723d70b+1227 1228 9974 0x1723c0e8+1228 1229 9973 0x1723aadb+1229 1230 9972 0x172394f8+1230 1231 9971 0x17237f00+1231 1232 9970 0x17236908+1232 1233 9969 0x1723533b+1233 1234 9968 0x17233d6e+1234 1235 9967 0x172327a1+1235 1236 9966 0x172311e9+1236 1237 9965 0x1722fc47+1237 1238 9964 0x1722e6ba+1238 1239 9963 0x1722d12d+1239 1240 9962 0x1722bba1+1240 1241 9961 0x1722a63f+1241 1242 9960 0x172290dd+1242 1243 9959 0x17227b7b+1243 1244 9958 0x1722662f+1244 1245 9957 0x1722510d+1245 1246 9956 0x17223bec+1246 1247 9955 0x172226cb+1247 1248 9954 0x172211a9+1248 1249 9953 0x1721fc9d+1249 1250 9952 0x1721e7a7+1250 1251 9951 0x1721d2c6+1251 1252 9950 0x1721bde5+1252 1253 9949 0x1721a904+1253 1254 9948 0x1721944e+1254 1255 9947 0x17217f82+1255 1256 9946 0x17216ae2+1256 1257 9945 0x17215641+1257 1258 9944 0x172141a1+1258 1259 9943 0x17212d2b+1259 1260 9942 0x172118cb+1260 1261 9941 0x17210455+1261 1262 9940 0x1720eff5+1262 1263 9939 0x1720db95+1263 1264 9938 0x1720c75f+1264 1265 9937 0x1720b315+1265 1266 9936 0x17209ef5+1266 1267 9935 0x17208ad5+1267 1268 9934 0x172076b5+1268 1269 9933 0x172062ab+1269 1270 9932 0x17204eb6+1270 1271 9931 0x17203ac1+1271 1272 9930 0x172026e2+1272 1273 9929 0x17201302+1273 1274 9928 0x171fff39+1274 1275 9927 0x171feb84+1275 1276 9926 0x171fd7e5+1276 1277 9925 0x171fc431+1277 1278 9924 0x171fb0a7+1278 1279 9923 0x171f9d1e+1279 1280 9922 0x171f8994+1280 1281 9921 0x171f7620+1281 1282 9920 0x171f62ac+1282 1283 9919 0x171f4f4d+1283 1284 9918 0x171f3c04+1284 1285 9917 0x171f28bb+1285 1286 9916 0x171f1587+1286 1287 9915 0x171f0254+1287 1288 9914 0x171eef35+1288 1289 9913 0x171edc2d+1289 1290 9912 0x171ec924+1290 1291 9911 0x171eb631+1291 1292 9910 0x171ea33d+1292 1293 9909 0x171e9060+1293 1294 9908 0x171e7d82+1294 1295 9907 0x171e6ab9+1295 1296 9906 0x171e57f1+1296 1297 9905 0x171e453e+1297 1298 9904 0x171e328b+1298 1299 9903 0x171e1fee+1299 1300 9902 0x171e0d51+1300 1301 9901 0x171dfac9+1301 1302 9900 0x171de856+1302 1303 9899 0x171dd5e4+1303 1304 9898 0x171dc387+1304 1305 9897 0x171db12a+1305 1306 9896 0x171d9ee2+1306 1307 9895 0x171d8cb0+1307 1308 9894 0x171d7a68+1308 1309 9893 0x171d684c+1309 1310 9892 0x171d561a+1310 1311 9891 0x171d4413+1311 1312 9890 0x171d31f6+1312 1313 9889 0x171d2004+1313 1314 9888 0x171d0e13+1314 1315 9887 0x171cfc21+1315 1316 9886 0x171cea45+1316 1317 9885 0x171cd868+1317 1318 9884 0x171cc6a2+1318 1319 9883 0x171cb4f0+1319 1320 9882 0x171ca33f+1320 1321 9881 0x171c918e+1321 1322 9880 0x171c8007+1322 1323 9879 0x171c6e81+1323 1324 9878 0x171c5cfb+1324 1325 9877 0x171c4b74+1325 1326 9876 0x171c3a03+1326 1327 9875 0x171c28a8+1327 1328 9874 0x171c174d+1328 1329 9873 0x171c0607+1329 1330 9872 0x171bf4c1+1330 1331 9871 0x171be37b+1331 1332 9870 0x171bd24a+1332 1333 9869 0x171bc12f+1333 1334 9868 0x171bb014+1334 1335 9867 0x171b9f0f+1335 1336 9866 0x171b8e09+1336 1337 9865 0x171b7d19+1337 1338 9864 0x171b6c29+1338 1339 9863 0x171b5b4e+1339 1340 9862 0x171b4a74+1340 1341 9861 0x171b3999+1341 1342 9860 0x171b28d4+1342 1343 9859 0x171b1824+1343 1344 9858 0x171b075f+1344 1345 9857 0x171af6c5+1345 1346 9856 0x171ae62b+1346 1347 9855 0x171ad590+1347 1348 9854 0x171ac50c+1348 1349 9853 0x171ab49c+1349 1350 9852 0x171aa417+1350 1351 9851 0x171a93be+1351 1352 9850 0x171a834e+1352 1353 9849 0x171a731f+1353 1354 9848 0x171a62c6+1354 1355 9847 0x171a5281+1355 1356 9846 0x171a4252+1356 1357 9845 0x171a3223+1357 1358 9844 0x171a220a+1358 1359 9843 0x171a11f1+1359 1360 9842 0x171a01d7+1360 1361 9841 0x1719f1d3+1361 1362 9840 0x1719e1e5+1362 1363 9839 0x1719d1f6+1363 1364 9838 0x1719c208+1364 1365 9837 0x1719b22f+1365 1366 9836 0x1719a256+1366 1367 9835 0x17199292+1367 1368 9834 0x171982e4+1368 1369 9833 0x17197336+1369 1370 9832 0x17196388+1370 1371 9831 0x171953da+1371 1372 9830 0x17194441+1372 1373 9829 0x171934be+1373 1374 9828 0x1719253b+1374 1375 9827 0x171915b7+1375 1376 9826 0x1719064a+1376 1377 9825 0x1718f6dc+1377 1378 9824 0x1718e76e+1378 1379 9823 0x1718d82b+1379 1380 9822 0x1718c8d3+1380 1381 9821 0x1718b990+1381 1382 9820 0x1718aa4e+1382 1383 9819 0x17189b36+1383 1384 9818 0x17188c08+1384 1385 9817 0x17187cf1+1385 1386 9816 0x17186dd9+1386 1387 9815 0x17185ed6+1387 1388 9814 0x17184fd4+1388 1389 9813 0x171840d2+1389 1390 9812 0x171831e5+1390 1391 9811 0x171822f8+1391 1392 9810 0x17181420+1392 1393 9809 0x17180549+1393 1394 9808 0x1717f671+1394 1395 9807 0x1717e7af+1395 1396 9806 0x1717d903+1396 1397 9805 0x1717ca41+1397 1398 9804 0x1717bb94+1398 1399 9803 0x1717ad13+1399 1400 9802 0x17179e7c+1400 1401 9801 0x17178fe5+1401 1402 9800 0x17178163+1402 1403 9799 0x171772e2+1403 1404 9798 0x17176475+1404 1405 9797 0x17175609+1405 1406 9796 0x1717479d+1406 1407 9795 0x17173947+1407 1408 9794 0x17172af0+1408 1409 9793 0x17171caf+1409 1410 9792 0x17170e6e+1410 1411 9791 0x1717002c+1411 1412 9790 0x1716f201+1412 1413 9789 0x1716e3ea+1413 1414 9788 0x1716d5d4+1414 1415 9787 0x1716c7be+1415 1416 9786 0x1716b9a8+1416 1417 9785 0x1716aba7+1417 1418 9784 0x17169da6+1418 1419 9783 0x17168fbb+1419 1420 9782 0x171681cf+1420 1421 9781 0x171673e4+1421 1422 9780 0x1716660e+1422 1423 9779 0x17165838+1423 1424 9778 0x17164a62+1424 1425 9777 0x17163ca2+1425 1426 9776 0x17162ee2+1426 1427 9775 0x17162137+1427 1428 9774 0x1716138c+1428 1429 9773 0x171605f6+1429 1430 9772 0x1715f861+1430 1431 9771 0x1715eacb+1431 1432 9770 0x1715dd36+1432 1433 9769 0x1715cfb6+1433 1434 9768 0x1715c24b+1434 1435 9767 0x1715b4cb+1435 1436 9766 0x1715a761+1436 1437 9765 0x171599f6+1437 1438 9764 0x17158ca1+1438 1439 9763 0x17157f4c+1439 1440 9762 0x1715720d+1440 1441 9761 0x171564d8+1441 1442 9760 0x171557ae+1442 1443 9759 0x17154a8e+1443 1444 9758 0x17153d7a+1444 1445 9757 0x17153070+1445 1446 9756 0x17152366+1446 1447 9755 0x17151666+1447 1448 9754 0x17150972+1448 1449 9753 0x1714fc7e+1449 1450 9752 0x1714ef94+1450 1451 9751 0x1714e2aa+1451 1452 9750 0x1714d5d6+1452 1453 9749 0x1714c902+1453 1454 9748 0x1714bc38+1454 1455 9747 0x1714af6e+1455 1456 9746 0x1714a2b0+1456 1457 9745 0x171495fc+1457 1458 9744 0x17148952+1458 1459 9743 0x17147ca9+1459 1460 9742 0x17147015+1460 1461 9741 0x17146381+1461 1462 9740 0x171456f8+1462 1463 9739 0x17144a6f+1463 1464 9738 0x17143df0+1464 1465 9737 0x17143172+1465 1466 9736 0x171424fe+1466 1467 9735 0x17141895+1467 1468 9734 0x17140c37+1468 1469 9733 0x1713ffd9+1469 1470 9732 0x1713f386+1470 1471 9731 0x1713e73d+1471 1472 9730 0x1713daf4+1472 1473 9729 0x1713ceb6+1473 1474 9728 0x1713c283+1474 1475 9727 0x1713b665+1475 1476 9726 0x1713aa3c+1476 1477 9725 0x17139e1f+1477 1478 9724 0x17139201+1478 1479 9723 0x171385f8+1479 1480 9722 0x171379e5+1480 1481 9721 0x17136de8+1481 1482 9720 0x171361ea+1482 1483 9719 0x171355f7+1483 1484 9718 0x17134a0f+1484 1485 9717 0x17133e27+1485 1486 9716 0x1713324a+1486 1487 9715 0x1713266c+1487 1488 9714 0x17131aa4+1488 1489 9713 0x17130edc+1489 1490 9712 0x17130314+1490 1491 9711 0x1712f76d+1491 1492 9710 0x1712ebba+1492 1493 9709 0x1712e008+1493 1494 9708 0x1712d46b+1494 1495 9707 0x1712c8c3+1495 1496 9706 0x1712bd31+1496 1497 9705 0x1712b19e+1497 1498 9704 0x1712a617+1498 1499 9703 0x17129a9a+1499 1500 9702 0x17128f1d+1500 1501 9701 0x171283ab+1501 1502 9700 0x17127839+1502 1503 9699 0x17126cd2+1503 1504 9698 0x17126175+1504 1505 9697 0x17125623+1505 1506 9696 0x17124adc+1506 1507 9695 0x17123f95+1507 1508 9694 0x1712344e+1508 1509 9693 0x17122911+1509 1510 9692 0x17121ddf+1510 1511 9691 0x171212ae+1511 1512 9690 0x17120787+1512 1513 9689 0x1711fc6b+1513 1514 9688 0x1711f14e+1514 1515 9687 0x1711e63d+1515 1516 9686 0x1711db2b+1516 1517 9685 0x1711d025+1517 1518 9684 0x1711c529+1518 1519 9683 0x1711ba2d+1519 1520 9682 0x1711af3b+1520 1521 9681 0x1711a460+1521 1522 9680 0x17119984+1522 1523 9679 0x17118e9d+1523 1524 9678 0x171183cc+1524 1525 9677 0x171178fb+1525 1526 9676 0x17116e35+1526 1527 9675 0x1711636e+1527 1528 9674 0x171158b3+1528 1529 9673 0x17114e02+1529 1530 9672 0x17114351+1530 1531 9671 0x171138ab+1531 1532 9670 0x17112e05+1532 1533 9669 0x17112369+1533 1534 9668 0x171118d8+1534 1535 9667 0x17110e48+1535 1536 9666 0x171103c2+1536 1537 9665 0x1710f951+1537 1538 9664 0x1710eed6+1538 1539 9663 0x1710e466+1539 1540 9662 0x1710d9f5+1540 1541 9661 0x1710cf8f+1541 1542 9660 0x1710c52a+1542 1543 9659 0x1710bace+1543 1544 9658 0x1710b07e+1544 1545 9657 0x1710a62e+1545 1546 9656 0x17109be8+1546 1547 9655 0x171091ad+1547 1548 9654 0x17108773+1548 1549 9653 0x17107d43+1549 1550 9652 0x17107312+1550 1551 9651 0x171068ed+1551 1552 9650 0x17105ed2+1552 1553 9649 0x171054b8+1553 1554 9648 0x17104aa8+1554 1555 9647 0x17104098+1555 1556 9646 0x17103693+1556 1557 9645 0x17102c8e+1557 1558 9644 0x17102293+1558 1559 9643 0x171018a4+1559 1560 9642 0x17100eb4+1560 1561 9641 0x171004c4+1561 1562 9640 0x170ffaea+1562 1563 9639 0x170ff105+1563 1564 9638 0x170fe735+1564 1565 9637 0x170fdd66+1565 1566 9636 0x170fd396+1566 1567 9635 0x170fc9dc+1567 1568 9634 0x170fc022+1568 1569 9633 0x170fb668+1569 1570 9632 0x170facb9+1570 1571 9631 0x170fa30a+1571 1572 9630 0x170f9965+1572 1573 9629 0x170f8fcb+1573 1574 9628 0x170f8632+1574 1575 9627 0x170f7c98+1575 1576 9626 0x170f7309+1576 1577 9625 0x170f6984+1577 1578 9624 0x170f6000+1578 1579 9623 0x170f5686+1579 1580 9622 0x170f4d0d+1580 1581 9621 0x170f439e+1581 1582 9620 0x170f3a39+1582 1583 9619 0x170f30e0+1583 1584 9618 0x170f277c+1584 1585 9617 0x170f1e22+1585 1586 9616 0x170f14d4+1586 1587 9615 0x170f0b85+1587 1588 9614 0x170f0241+1588 1589 9613 0x170ef8fd+1589 1590 9612 0x170eefc4+1590 1591 9611 0x170ee68b+1591 1592 9610 0x170edd5c+1592 1593 9609 0x170ed42e+1593 1594 9608 0x170ecb0a+1594 1595 9607 0x170ec1f1+1595 1596 9606 0x170eb8d8+1596 1597 9605 0x170eafbf+1597 1598 9604 0x170ea6bb+1598 1599 9603 0x170e9db8+1599 1600 9602 0x170e94b4+1600 1601 9601 0x170e8bb0+1601 1602 9600 0x170e82c2+1602 1603 9599 0x170e79c9+1603 1604 9598 0x170e70db+1604 1605 9597 0x170e67f8+1605 1606 9596 0x170e5f15+1606 1607 9595 0x170e563c+1607 1608 9594 0x170e4d63+1608 1609 9593 0x170e4495+1609 1610 9592 0x170e3bc7+1610 1611 9591 0x170e3304+1611 1612 9590 0x170e2a41+1612 1613 9589 0x170e2193+1613 1614 9588 0x170e18db+1614 1615 9587 0x170e102d+1615 1616 9586 0x170e0780+1616 1617 9585 0x170dfedd+1617 1618 9584 0x170df63a+1618 1619 9583 0x170deda1+1619 1620 9582 0x170de514+1620 1621 9581 0x170ddc7c+1621 1622 9580 0x170dd3f9+1622 1623 9579 0x170dcb6b+1623 1624 9578 0x170dc2f3+1624 1625 9577 0x170dba70+1625 1626 9576 0x170db203+1626 1627 9575 0x170da996+1627 1628 9574 0x170da128+1628 1629 9573 0x170d98c6+1629 1630 9572 0x170d906e+1630 1631 9571 0x170d880b+1631 1632 9570 0x170d7fbe+1632 1633 9569 0x170d7767+1633 1634 9568 0x170d6f19+1634 1635 9567 0x170d66d7+1635 1636 9566 0x170d5e95+1636 1637 9565 0x170d565d+1637 1638 9564 0x170d4e25+1638 1639 9563 0x170d45ee+1639 1640 9562 0x170d3dc1+1640 1641 9561 0x170d359f+1641 1642 9560 0x170d2d7c+1642 1643 9559 0x170d255a+1643 1644 9558 0x170d1d4d+1644 1645 9557 0x170d1536+1645 1646 9556 0x170d0d29+1646 1647 9555 0x170d051d+1647 1648 9554 0x170cfd1b+1648 1649 9553 0x170cf519+1649 1650 9552 0x170ced21+1650 1651 9551 0x170ce52a+1651 1652 9550 0x170cdd3e+1652 1653 9549 0x170cd551+1653 1654 9548 0x170ccd65+1654 1655 9547 0x170cc583+1655 1656 9546 0x170cbdac+1656 1657 9545 0x170cb5ca+1657 1658 9544 0x170cadfe+1658 1659 9543 0x170ca631+1659 1660 9542 0x170c9e65+1660 1661 9541 0x170c96a3+1661 1662 9540 0x170c8ee2+1662 1663 9539 0x170c872b+1663 1664 9538 0x170c7f74+1664 1665 9537 0x170c77bd+1665 1666 9536 0x170c7011+1666 1667 9535 0x170c6865+1667 1668 9534 0x170c60c4+1668 1669 9533 0x170c5922+1669 1670 9532 0x170c518c+1670 1671 9531 0x170c49f5+1671 1672 9530 0x170c425e+1672 1673 9529 0x170c3ad2+1673 1674 9528 0x170c3346+1674 1675 9527 0x170c2bd0+1675 1676 9526 0x170c244f+1676 1677 9525 0x170c1cd8+1677 1678 9524 0x170c1562+1678 1679 9523 0x170c0deb+1679 1680 9522 0x170c0680+1680 1681 9521 0x170bff14+1681 1682 9520 0x170bf7b3+1682 1683 9519 0x170bf052+1683 1684 9518 0x170be8fc+1684 1685 9517 0x170be1a5+1685 1686 9516 0x170bda4f+1686 1687 9515 0x170bd304+1687 1688 9514 0x170bcbb8+1688 1689 9513 0x170bc477+1689 1690 9512 0x170bbd37+1690 1691 9511 0x170bb600+1691 1692 9510 0x170baeca+1692 1693 9509 0x170ba794+1693 1694 9508 0x170ba05e+1694 1695 9507 0x170b9933+1695 1696 9506 0x170b9212+1696 1697 9505 0x170b8af2+1697 1698 9504 0x170b83d1+1698 1699 9503 0x170b7cbb+1699 1700 9502 0x170b75a5+1700 1701 9501 0x170b6e90+1701 1702 9500 0x170b6784+1702 1703 9499 0x170b6079+1703 1704 9498 0x170b5979+1704 1705 9497 0x170b5279+1705 1706 9496 0x170b4b83+1706 1707 9495 0x170b4482+1707 1708 9494 0x170b3d98+1708 1709 9493 0x170b36a2+1709 1710 9492 0x170b2fb7+1710 1711 9491 0x170b28cc+1711 1712 9490 0x170b21ec+1712 1713 9489 0x170b1b0c+1713 1714 9488 0x170b142b+1714 1715 9487 0x170b0d56+1715 1716 9486 0x170b0680+1716 1717 9485 0x170affb6+1717 1718 9484 0x170af8eb+1718 1719 9483 0x170af220+1719 1720 9482 0x170aeb60+1720 1721 9481 0x170ae4a0+1721 1722 9480 0x170addeb+1722 1723 9479 0x170ad736+1723 1724 9478 0x170ad080+1724 1725 9477 0x170ac9cb+1725 1726 9476 0x170ac321+1726 1727 9475 0x170abc76+1727 1728 9474 0x170ab5dc+1728 1729 9473 0x170aaf47+1729 1730 9472 0x170aa8b7+1730 1731 9471 0x170aa227+1731 1732 9470 0x170a9b9d+1732 1733 9469 0x170a9513+1733 1734 9468 0x170a8e93+1734 1735 9467 0x170a8813+1735 1736 9466 0x170a819f+1736 1737 9465 0x170a7b24+1737 1738 9464 0x170a74b5+1738 1739 9463 0x170a6e45+1739 1740 9462 0x170a67d6+1740 1741 9461 0x170a6171+1741 1742 9460 0x170a5b0c+1742 1743 9459 0x170a54ad+1743 1744 9458 0x170a4e4d+1744 1745 9457 0x170a47f3+1745 1746 9456 0x170a419f+1746 1747 9455 0x170a3b4f+1747 1748 9454 0x170a3500+1748 1749 9453 0x170a2ebc+1749 1750 9452 0x170a2872+1750 1751 9451 0x170a2238+1751 1752 9450 0x170a1bf9+1752 1753 9449 0x170a15bf+1753 1754 9448 0x170a0f8a+1754 1755 9447 0x170a0956+1755 1756 9446 0x170a0327+1756 1757 9445 0x1709fcfd+1757 1758 9444 0x1709f6d3+1758 1759 9443 0x1709f0af+1759 1760 9442 0x1709ea90+1760 1761 9441 0x1709e471+1761 1762 9440 0x1709de57+1762 1763 9439 0x1709d843+1763 1764 9438 0x1709d22f+1764 1765 9437 0x1709cc25+1765 1766 9436 0x1709c616+1766 1767 9435 0x1709c017+1767 1768 9434 0x1709ba13+1768 1769 9433 0x1709b414+1769 1770 9432 0x1709ae1b+1770 1771 9431 0x1709a822+1771 1772 9430 0x1709a22d+1772 1773 9429 0x17099c3f+1773 1774 9428 0x17099650+1774 1775 9427 0x17099067+1775 1776 9426 0x17098a7d+1776 1777 9425 0x1709849f+1777 1778 9424 0x17097ebb+1778 1779 9423 0x170978e2+1779 1780 9422 0x17097308+1780 1781 9421 0x17096d34+1781 1782 9420 0x1709676b+1782 1783 9419 0x1709619d+1783 1784 9418 0x17095bd4+1784 1785 9417 0x17095610+1785 1786 9416 0x1709504c+1786 1787 9415 0x17094a8e+1787 1788 9414 0x170944cf+1788 1789 9413 0x17093f16+1789 1790 9412 0x17093963+1790 1791 9411 0x170933b4+1791 1792 9410 0x17092e06+1792 1793 9409 0x1709285d+1793 1794 9408 0x170922b4+1794 1795 9407 0x17091d10+1795 1796 9406 0x17091772+1796 1797 9405 0x170911d9+1797 1798 9404 0x17090c41+1798 1799 9403 0x170906ad+1799 1800 9402 0x1709011a+1800 1801 9401 0x1708fb8c+1801 1802 9400 0x1708f5fd+1802 1803 9399 0x1708f07a+1803 1804 9398 0x1708eaf1+1804 1805 9397 0x1708e573+1805 1806 9396 0x1708dff5+1806 1807 9395 0x1708da7d+1807 1808 9394 0x1708d504+1808 1809 9393 0x1708cf91+1809 1810 9392 0x1708ca1d+1810 1811 9391 0x1708c4b5+1811 1812 9390 0x1708bf47+1812 1813 9389 0x1708b9e9+1813 1814 9388 0x1708b486+1814 1815 9387 0x1708af28+1815 1816 9386 0x1708a9ca+1816 1817 9385 0x1708a472+1817 1818 9384 0x17089f19+1818 1819 9383 0x170899cc+1819 1820 9382 0x17089479+1820 1821 9381 0x17088f30+1821 1822 9380 0x170889e8+1822 1823 9379 0x170884a0+1823 1824 9378 0x17087f5d+1824 1825 9377 0x17087a1f+1825 1826 9376 0x170874e7+1826 1827 9375 0x17086faf+1827 1828 9374 0x17086a7c+1828 1829 9373 0x17086549+1829 1830 9372 0x1708601b+1830 1831 9371 0x17085af3+1831 1832 9370 0x170855cb+1832 1833 9369 0x170850a3+1833 1834 9368 0x17084b80+1834 1835 9367 0x17084663+1835 1836 9366 0x1708414b+1836 1837 9365 0x17083c2d+1837 1838 9364 0x1708371a+1838 1839 9363 0x17083208+1839 1840 9362 0x17082cfa+1840 1841 9361 0x170827ed+1841 1842 9360 0x170822e5+1842 1843 9359 0x17081de8+1843 1844 9358 0x170818e5+1844 1845 9357 0x170813e3+1845 1846 9356 0x17080ee6+1846 1847 9355 0x170809ee+1847 1848 9354 0x170804fb+1848 1849 9353 0x17080003+1849 1850 9352 0x1707fb16+1850 1851 9351 0x1707f629+1851 1852 9350 0x1707f141+1852 1853 9349 0x1707ec5a+1853 1854 9348 0x1707e777+1854 1855 9347 0x1707e295+1855 1856 9346 0x1707ddb8+1856 1857 9345 0x1707d8e0+1857 1858 9344 0x1707d409+1858 1859 9343 0x1707cf36+1859 1860 9342 0x1707ca64+1860 1861 9341 0x1707c597+1861 1862 9340 0x1707c0ca+1862 1863 9339 0x1707bc03+1863 1864 9338 0x1707b740+1864 1865 9337 0x1707b27e+1865 1866 9336 0x1707adbc+1866 1867 9335 0x1707a8ff+1867 1868 9334 0x1707a448+1868 1869 9333 0x17079f90+1869 1870 9332 0x17079ade+1870 1871 9331 0x1707962c+1871 1872 9330 0x1707917f+1872 1873 9329 0x17078cd3+1873 1874 9328 0x17078831+1874 1875 9327 0x17078389+1875 1876 9326 0x17077ee7+1876 1877 9325 0x17077a4b+1877 1878 9324 0x170775ae+1878 1879 9323 0x17077112+1879 1880 9322 0x17076c7a+1880 1881 9321 0x170767e8+1881 1882 9320 0x17076357+1882 1883 9319 0x17075eca+1883 1884 9318 0x17075a3e+1884 1885 9317 0x170755b6+1885 1886 9316 0x1707512f+1886 1887 9315 0x17074cad+1887 1888 9314 0x1707482c+1888 1889 9313 0x170743b5+1889 1890 9312 0x17073f38+1890 1891 9311 0x17073ac1+1891 1892 9310 0x1707364f+1892 1893 9309 0x170731de+1893 1894 9308 0x17072d6c+1894 1895 9307 0x17072900+1895 1896 9306 0x17072499+1896 1897 9305 0x17072032+1897 1898 9304 0x17071bcb+1898 1899 9303 0x17071769+1899 1900 9302 0x1707130d+1900 1901 9301 0x17070eb1+1901 1902 9300 0x17070a55+1902 1903 9299 0x170705fe+1903 1904 9298 0x170701ac+1904 1905 9297 0x1706fd60+1905 1906 9296 0x1706f90f+1906 1907 9295 0x1706f4c2+1907 1908 9294 0x1706f07c+1908 1909 9293 0x1706ec35+1909 1910 9292 0x1706e7ee+1910 1911 9291 0x1706e3ad+1911 1912 9290 0x1706df71+1912 1913 9289 0x1706db35+1913 1914 9288 0x1706d6f9+1914 1915 9287 0x1706d2c2+1915 1916 9286 0x1706ce91+1916 1917 9285 0x1706ca60+1917 1918 9284 0x1706c62e+1918 1919 9283 0x1706c202+1919 1920 9282 0x1706bde1+1920 1921 9281 0x1706b9bb+1921 1922 9280 0x1706b594+1922 1923 9279 0x1706b173+1923 1924 9278 0x1706ad52+1924 1925 9277 0x1706a936+1925 1926 9276 0x1706a51a+1926 1927 9275 0x1706a104+1927 1928 9274 0x17069ced+1928 1929 9273 0x170698dc+1929 1930 9272 0x170694cb+1930 1931 9271 0x170690bf+1931 1932 9270 0x17068cb3+1932 1933 9269 0x170688a8+1933 1934 9268 0x170684a1+1934 1935 9267 0x170680a6+1935 1936 9266 0x17067ca5+1936 1937 9265 0x170678a4+1937 1938 9264 0x170674a8+1938 1939 9263 0x170670ac+1939 1940 9262 0x17066cb6+1940 1941 9261 0x170668c5+1941 1942 9260 0x170664cf+1942 1943 9259 0x170660de+1943 1944 9258 0x17065cf3+1944 1945 9257 0x17065907+1945 1946 9256 0x17065521+1946 1947 9255 0x1706513b+1947 1948 9254 0x17064d55+1948 1949 9253 0x17064974+1949 1950 9252 0x17064593+1950 1951 9251 0x170641bd+1951 1952 9250 0x17063de2+1952 1953 9249 0x17063a06+1953 1954 9248 0x17063635+1954 1955 9247 0x1706325f+1955 1956 9246 0x17062e8f+1956 1957 9245 0x17062abe+1957 1958 9244 0x170626f3+1958 1959 9243 0x17062327+1959 1960 9242 0x17061f61+1960 1961 9241 0x17061b9b+1961 1962 9240 0x170617db+1962 1963 9239 0x1706141a+1963 1964 9238 0x1706105a+1964 1965 9237 0x17060c9e+1965 1966 9236 0x170608e9+1966 1967 9235 0x1706052d+1967 1968 9234 0x1706017d+1968 1969 9233 0x1705fdc7+1969 1970 9232 0x1705fa16+1970 1971 9231 0x1705f666+1971 1972 9230 0x1705f2bb+1972 1973 9229 0x1705ef10+1973 1974 9228 0x1705eb6a+1974 1975 9227 0x1705e7c4+1975 1976 9226 0x1705e41e+1976 1977 9225 0x1705e07e+1977 1978 9224 0x1705dcde+1978 1979 9223 0x1705d943+1979 1980 9222 0x1705d5a8+1980 1981 9221 0x1705d212+1981 1982 9220 0x1705ce7c+1982 1983 9219 0x1705cae7+1983 1984 9218 0x1705c751+1984 1985 9217 0x1705c3c6+1985 1986 9216 0x1705c036+1986 1987 9215 0x1705bcab+1987 1988 9214 0x1705b920+1988 1989 9213 0x1705b59a+1989 1990 9212 0x1705b215+1990 1991 9211 0x1705ae8f+1991 1992 9210 0x1705ab0f+1992 1993 9209 0x1705a78f+1993 1994 9208 0x1705a40e+1994 1995 9207 0x1705a094+1995 1996 9206 0x17059d1e+1996 1997 9205 0x170599a9+1997 1998 9204 0x17059633+1998 1999 9203 0x170592c3+1999 2000 9202 0x17058f4e+2000 2001 9201 0x17058be3+2001 2002 9200 0x17058873+2002 2003 9199 0x17058508+2003 2004 9198 0x170581a3+2004 2005 9197 0x17057e38+2005 2006 9196 0x17057ad3+2006 2007 9195 0x17057773+2007 2008 9194 0x17057413+2008 2009 9193 0x170570b3+2009 2010 9192 0x17056d53+2010 2011 9191 0x170569f8+2011 2012 9190 0x170566a3+2012 2013 9189 0x1705634e+2013 2014 9188 0x17055ff8+2014 2015 9187 0x17055ca8+2015 2016 9186 0x1705595b+2016 2017 9185 0x17055611+2017 2018 9184 0x170552c9+2018 2019 9183 0x17054f81+2019 2020 9182 0x17054c3e+2020 2021 9181 0x170548fc+2021 2022 9180 0x170545b9+2022 2023 9179 0x1705427c+2023 2024 9178 0x17053f3f+2024 2025 9177 0x17053c05+2025 2026 9176 0x170538cd+2026 2027 9175 0x17053595+2027 2028 9174 0x17053263+2028 2029 9173 0x17052f31+2029 2030 9172 0x17052bfe+2030 2031 9171 0x170528d1+2031 2032 9170 0x170525a1+2032 2033 9169 0x17052277+2033 2034 9168 0x17051f4d+2034 2035 9167 0x17051c25+2035 2036 9166 0x17051900+2036 2037 9165 0x170515db+2037 2038 9164 0x170512b9+2038 2039 9163 0x17050f99+2039 2040 9162 0x17050c7c+2040 2041 9161 0x1705095f+2041 2042 9160 0x17050645+2042 2043 9159 0x17050330+2043 2044 9158 0x17050019+2044 2045 9157 0x1704fd04+2045 2046 9156 0x1704f9f2+2046 2047 9155 0x1704f6e0+2047 2048 9154 0x1704f3d0+2048 2049 9153 0x1704f0c3+2049 2050 9152 0x1704edb6+2050 2051 9151 0x1704eaac+2051 2052 9150 0x1704e7a5+2052 2053 9149 0x1704e4a0+2053 2054 9148 0x1704e19b+2054 2055 9147 0x1704de99+2055 2056 9146 0x1704db97+2056 2057 9145 0x1704d898+2057 2058 9144 0x1704d5a0+2058 2059 9143 0x1704d2a4+2059 2060 9142 0x1704cfaa+2060 2061 9141 0x1704ccb2+2061 2062 9140 0x1704c9be+2062 2063 9139 0x1704c6c9+2063 2064 9138 0x1704c3d7+2064 2065 9137 0x1704c0e8+2065 2066 9136 0x1704bdf8+2066 2067 9135 0x1704bb0c+2067 2068 9134 0x1704b81f+2068 2069 9133 0x1704b535+2069 2070 9132 0x1704b24e+2070 2071 9131 0x1704af69+2071 2072 9130 0x1704ac85+2072 2073 9129 0x1704a9a3+2073 2074 9128 0x1704a6c6+2074 2075 9127 0x1704a3e7+2075 2076 9126 0x1704a10b+2076 2077 9125 0x17049e2e+2077 2078 9124 0x17049b54+2078 2079 9123 0x1704987d+2079 2080 9122 0x170495a9+2080 2081 9121 0x170492d4+2081 2082 9120 0x17049000+2082 2083 9119 0x17048d31+2083 2084 9118 0x17048a61+2084 2085 9117 0x17048792+2085 2086 9116 0x170484c9+2086 2087 9115 0x170481ff+2087 2088 9114 0x17047f35+2088 2089 9113 0x17047c71+2089 2090 9112 0x170479ac+2090 2091 9111 0x170476eb+2091 2092 9110 0x17047429+2092 2093 9109 0x1704716a+2093 2094 9108 0x17046eab+2094 2095 9107 0x17046bef+2095 2096 9106 0x17046935+2096 2097 9105 0x1704667b+2097 2098 9104 0x170463c4+2098 2099 9103 0x1704610d+2099 2100 9102 0x17045e59+2100 2101 9101 0x17045ba8+2101 2102 9100 0x170458f6+2102 2103 9099 0x17045647+2103 2104 9098 0x1704539e+2104 2105 9097 0x170450f1+2105 2106 9096 0x17044e48+2106 2107 9095 0x17044b9e+2107 2108 9094 0x170448f7+2108 2109 9093 0x17044653+2109 2110 9092 0x170443af+2110 2111 9091 0x1704410e+2111 2112 9090 0x17043e6f+2112 2113 9089 0x17043bd0+2113 2114 9088 0x17043931+2114 2115 9087 0x17043698+2115 2116 9086 0x170433fe+2116 2117 9085 0x17043165+2117 2118 9084 0x17042ece+2118 2119 9083 0x17042c3a+2119 2120 9082 0x170429a9+2120 2121 9081 0x17042717+2121 2122 9080 0x17042489+2122 2123 9079 0x170421fa+2123 2124 9078 0x17041f6b+2124 2125 9077 0x17041cdf+2125 2126 9076 0x17041a56+2126 2127 9075 0x170417cf+2127 2128 9074 0x17041548+2128 2129 9073 0x170412c2+2129 2130 9072 0x1704103e+2130 2131 9071 0x17040dbc+2131 2132 9070 0x17040b3e+2132 2133 9069 0x170408bd+2133 2134 9068 0x17040641+2134 2135 9067 0x170403c7+2135 2136 9066 0x1704014e+2136 2137 9065 0x1703fed5+2137 2138 9064 0x1703fc5e+2138 2139 9063 0x1703f9e8+2139 2140 9062 0x1703f774+2140 2141 9061 0x1703f500+2141 2142 9060 0x1703f28f+2142 2143 9059 0x1703f020+2143 2144 9058 0x1703edb2+2144 2145 9057 0x1703eb46+2145 2146 9056 0x1703e8da+2146 2147 9055 0x1703e671+2147 2148 9054 0x1703e40b+2148 2149 9053 0x1703e1a4+2149 2150 9052 0x1703df40+2150 2151 9051 0x1703dcdd+2151 2152 9050 0x1703da7c+2152 2153 9049 0x1703d81a+2153 2154 9048 0x1703d5bc+2154 2155 9047 0x1703d35e+2155 2156 9046 0x1703d102+2156 2157 9045 0x1703cea9+2157 2158 9044 0x1703cc50+2158 2159 9043 0x1703c9f7+2159 2160 9042 0x1703c7a0+2160 2161 9041 0x1703c54d+2161 2162 9040 0x1703c2f9+2162 2163 9039 0x1703c0a5+2163 2164 9038 0x1703be57+2164 2165 9037 0x1703bc06+2165 2166 9036 0x1703b9bd+2166 2167 9035 0x1703b771+2167 2168 9034 0x1703b526+2168 2169 9033 0x1703b2dd+2169 2170 9032 0x1703b094+2170 2171 9031 0x1703ae4e+2171 2172 9030 0x1703ac0a+2172 2173 9029 0x1703a9c7+2173 2174 9028 0x1703a783+2174 2175 9027 0x1703a542+2175 2176 9026 0x1703a304+2176 2177 9025 0x1703a0c6+2177 2178 9024 0x17039e8a+2178 2179 9023 0x17039c4f+2179 2180 9022 0x17039a13+2180 2181 9021 0x170397dd+2181 2182 9020 0x170395a7+2182 2183 9019 0x17039371+2183 2184 9018 0x1703913d+2184 2185 9017 0x17038f0a+2185 2186 9016 0x17038cd9+2186 2187 9015 0x17038aa8+2187 2188 9014 0x1703887a+2188 2189 9013 0x1703864c+2189 2190 9012 0x17038420+2190 2191 9011 0x170381f5+2191 2192 9010 0x17037fcc+2192 2193 9009 0x17037da6+2193 2194 9008 0x17037b7d+2194 2195 9007 0x1703795a+2195 2196 9006 0x17037737+2196 2197 9005 0x17037516+2197 2198 9004 0x170372f3+2198 2199 9003 0x170370d5+2199 2200 9002 0x17036eb4+2200 2201 9001 0x17036c99+2201 2202 9000 0x17036a7b+2202 2203 8999 0x17036862+2203 2204 8998 0x17036647+2204 2205 8997 0x1703642e+2205 2206 8996 0x17036218+2206 2207 8995 0x17036002+2207 2208 8994 0x17035def+2208 2209 8993 0x17035bdb+2209 2210 8992 0x170359cb+2210 2211 8991 0x170357ba+2211 2212 8990 0x170355ac+2212 2213 8989 0x1703539e+2213 2214 8988 0x17035193+2214 2215 8987 0x17034f88+2215 2216 8986 0x17034d7d+2216 2217 8985 0x17034b74+2217 2218 8984 0x1703496e+2218 2219 8983 0x17034766+2219 2220 8982 0x17034563+2220 2221 8981 0x1703435f+2221 2222 8980 0x1703415c+2222 2223 8979 0x17033f5c+2223 2224 8978 0x17033d5b+2224 2225 8977 0x17033b5b+2225 2226 8976 0x17033960+2226 2227 8975 0x17033764+2227 2228 8974 0x17033569+2228 2229 8973 0x1703336e+2229 2230 8972 0x17033176+2230 2231 8971 0x17032f80+2231 2232 8970 0x17032d8a+2232 2233 8969 0x17032b94+2233 2234 8968 0x170329a1+2234 2235 8967 0x170327ae+2235 2236 8966 0x170325bb+2236 2237 8965 0x170323cb+2237 2238 8964 0x170321dd+2238 2239 8963 0x17031fef+2239 2240 8962 0x17031e02+2240 2241 8961 0x17031c17+2241 2242 8960 0x17031a2e+2242 2243 8959 0x17031846+2243 2244 8958 0x1703165e+2244 2245 8957 0x17031478+2245 2246 8956 0x17031292+2246 2247 8955 0x170310ad+2247 2248 8954 0x17030eca+2248 2249 8953 0x17030ce7+2249 2250 8952 0x17030b06+2250 2251 8951 0x17030926+2251 2252 8950 0x17030748+2252 2253 8949 0x1703056b+2253 2254 8948 0x1703038d+2254 2255 8947 0x170301b2+2255 2256 8946 0x1702ffd7+2256 2257 8945 0x1702fdff+2257 2258 8944 0x1702fc2a+2258 2259 8943 0x1702fa51+2259 2260 8942 0x1702f87c+2260 2261 8941 0x1702f6a9+2261 2262 8940 0x1702f4d3+2262 2263 8939 0x1702f303+2263 2264 8938 0x1702f130+2264 2265 8937 0x1702ef60+2265 2266 8936 0x1702ed93+2266 2267 8935 0x1702ebc2+2267 2268 8934 0x1702e9f7+2268 2269 8933 0x1702e82a+2269 2270 8932 0x1702e65f+2270 2271 8931 0x1702e497+2271 2272 8930 0x1702e2cc+2272 2273 8929 0x1702e107+2273 2274 8928 0x1702df41+2274 2275 8927 0x1702dd7c+2275 2276 8926 0x1702dbb6+2276 2277 8925 0x1702d9f4+2277 2278 8924 0x1702d831+2278 2279 8923 0x1702d671+2279 2280 8922 0x1702d4b1+2280 2281 8921 0x1702d2f0+2281 2282 8920 0x1702d133+2282 2283 8919 0x1702cf76+2283 2284 8918 0x1702cdbb+2284 2285 8917 0x1702cbfd+2285 2286 8916 0x1702ca45+2286 2287 8915 0x1702c88b+2287 2288 8914 0x1702c6d5+2288 2289 8913 0x1702c520+2289 2290 8912 0x1702c368+2290 2291 8911 0x1702c1b3+2291 2292 8910 0x1702c000+2292 2293 8909 0x1702be4d+2293 2294 8908 0x1702bc9a+2294 2295 8907 0x1702baea+2295 2296 8906 0x1702b93a+2296 2297 8905 0x1702b78a+2297 2298 8904 0x1702b5dd+2298 2299 8903 0x1702b430+2299 2300 8902 0x1702b283+2300 2301 8901 0x1702b0d8+2301 2302 8900 0x1702af2d+2302 2303 8899 0x1702ad88+2303 2304 8898 0x1702abe4+2304 2305 8897 0x1702aa3f+2305 2306 8896 0x1702a89b+2306 2307 8895 0x1702a6f8+2307 2308 8894 0x1702a557+2308 2309 8893 0x1702a3b6+2309 2310 8892 0x1702a216+2310 2311 8891 0x1702a077+2311 2312 8890 0x17029eda+2312 2313 8889 0x17029d3d+2313 2314 8888 0x17029ba1+2314 2315 8887 0x17029a05+2315 2316 8886 0x1702986c+2316 2317 8885 0x170296d3+2317 2318 8884 0x1702953b+2318 2319 8883 0x170293a6+2319 2320 8882 0x1702920f+2320 2321 8881 0x1702907a+2321 2322 8880 0x17028ee5+2322 2323 8879 0x17028d52+2323 2324 8878 0x17028bc0+2324 2325 8877 0x17028a2d+2325 2326 8876 0x1702889e+2326 2327 8875 0x1702870e+2327 2328 8874 0x1702857f+2328 2329 8873 0x170283f1+2329 2330 8872 0x17028265+2330 2331 8871 0x170280d9+2331 2332 8870 0x17027f4e+2332 2333 8869 0x17027dc3+2333 2334 8868 0x17027c3d+2334 2335 8867 0x17027ab4+2335 2336 8866 0x1702792d+2336 2337 8865 0x170277a7+2337 2338 8864 0x17027622+2338 2339 8863 0x1702749d+2339 2340 8862 0x17027319+2340 2341 8861 0x17027197+2341 2342 8860 0x17027016+2342 2343 8859 0x17026e95+2343 2344 8858 0x17026d15+2344 2345 8857 0x17026b95+2345 2346 8856 0x17026a17+2346 2347 8855 0x1702689a+2347 2348 8854 0x1702671e+2348 2349 8853 0x170265a2+2349 2350 8852 0x17026429+2350 2351 8851 0x170262b0+2351 2352 8850 0x17026137+2352 2353 8849 0x17025fc0+2353 2354 8848 0x17025e48+2354 2355 8847 0x17025cd2+2355 2356 8846 0x17025b5d+2356 2357 8845 0x170259e9+2357 2358 8844 0x17025876+2358 2359 8843 0x17025703+2359 2360 8842 0x17025592+2360 2361 8841 0x17025421+2361 2362 8840 0x170252b2+2362 2363 8839 0x17025142+2363 2364 8838 0x17024fd4+2364 2365 8837 0x17024e68+2365 2366 8836 0x17024cfd+2366 2367 8835 0x17024b91+2367 2368 8834 0x17024a26+2368 2369 8833 0x170248bd+2369 2370 8832 0x17024754+2370 2371 8831 0x170245eb+2371 2372 8830 0x17024485+2372 2373 8829 0x1702431f+2373 2374 8828 0x170241b8+2374 2375 8827 0x17024055+2375 2376 8826 0x17023ef0+2376 2377 8825 0x17023d8e+2377 2378 8824 0x17023c2c+2378 2379 8823 0x17023acb+2379 2380 8822 0x1702396b+2380 2381 8821 0x1702380c+2381 2382 8820 0x170236ae+2382 2383 8819 0x1702354f+2383 2384 8818 0x170233f3+2384 2385 8817 0x17023297+2385 2386 8816 0x1702313c+2386 2387 8815 0x17022fe1+2387 2388 8814 0x17022e87+2388 2389 8813 0x17022d2f+2389 2390 8812 0x17022bd7+2390 2391 8811 0x17022a80+2391 2392 8810 0x1702292a+2392 2393 8809 0x170227d3+2393 2394 8808 0x17022680+2394 2395 8807 0x1702252b+2395 2396 8806 0x170223da+2396 2397 8805 0x17022288+2397 2398 8804 0x17022136+2398 2399 8803 0x17021fe6+2399 2400 8802 0x17021e96+2400 2401 8801 0x17021d47+2401 2402 8800 0x17021bf9+2402 2403 8799 0x17021aac+2403 2404 8798 0x1702195f+2404 2405 8797 0x17021814+2405 2406 8796 0x170216c8+2406 2407 8795 0x1702157e+2407 2408 8794 0x17021436+2408 2409 8793 0x170212ed+2409 2410 8792 0x170211a6+2410 2411 8791 0x17021060+2411 2412 8790 0x17020f1a+2412 2413 8789 0x17020dd5+2413 2414 8788 0x17020c90+2414 2415 8787 0x17020b4c+2415 2416 8786 0x17020a0a+2416 2417 8785 0x170208c8+2417 2418 8784 0x17020786+2418 2419 8783 0x17020645+2419 2420 8782 0x17020506+2420 2421 8781 0x170203c6+2421 2422 8780 0x17020288+2422 2423 8779 0x1702014b+2423 2424 8778 0x1702000e+2424 2425 8777 0x1701fed1+2425 2426 8776 0x1701fd97+2426 2427 8775 0x1701fc5d+2427 2428 8774 0x1701fb23+2428 2429 8773 0x1701f9eb+2429 2430 8772 0x1701f8b2+2430 2431 8771 0x1701f77b+2431 2432 8770 0x1701f645+2432 2433 8769 0x1701f50f+2433 2434 8768 0x1701f3d9+2434 2435 8767 0x1701f2a5+2435 2436 8766 0x1701f171+2436 2437 8765 0x1701f03e+2437 2438 8764 0x1701ef0c+2438 2439 8763 0x1701eddc+2439 2440 8762 0x1701ecaa+2440 2441 8761 0x1701eb7b+2441 2442 8760 0x1701ea4d+2442 2443 8759 0x1701e91f+2443 2444 8758 0x1701e7f1+2444 2445 8757 0x1701e6c3+2445 2446 8756 0x1701e598+2446 2447 8755 0x1701e46c+2447 2448 8754 0x1701e342+2448 2449 8753 0x1701e218+2449 2450 8752 0x1701e0ee+2450 2451 8751 0x1701dfc6+2451 2452 8750 0x1701de9d+2452 2453 8749 0x1701dd76+2453 2454 8748 0x1701dc50+2454 2455 8747 0x1701db2a+2455 2456 8746 0x1701da06+2456 2457 8745 0x1701d8e3+2457 2458 8744 0x1701d7be+2458 2459 8743 0x1701d69c+2459 2460 8742 0x1701d579+2460 2461 8741 0x1701d459+2461 2462 8740 0x1701d337+2462 2463 8739 0x1701d218+2463 2464 8738 0x1701d0f9+2464 2465 8737 0x1701cfda+2465 2466 8736 0x1701cebc+2466 2467 8735 0x1701cd9e+2467 2468 8734 0x1701cc82+2468 2469 8733 0x1701cb65+2469 2470 8732 0x1701ca4a+2470 2471 8731 0x1701c931+2471 2472 8730 0x1701c818+2472 2473 8729 0x1701c6fe+2473 2474 8728 0x1701c5e6+2474 2475 8727 0x1701c4cf+2475 2476 8726 0x1701c3b8+2476 2477 8725 0x1701c2a1+2477 2478 8724 0x1701c18b+2478 2479 8723 0x1701c077+2479 2480 8722 0x1701bf62+2480 2481 8721 0x1701be4e+2481 2482 8720 0x1701bd3b+2482 2483 8719 0x1701bc29+2483 2484 8718 0x1701bb18+2484 2485 8717 0x1701ba06+2485 2486 8716 0x1701b8f6+2486 2487 8715 0x1701b7e7+2487 2488 8714 0x1701b6d9+2488 2489 8713 0x1701b5ca+2489 2490 8712 0x1701b4bc+2490 2491 8711 0x1701b3b0+2491 2492 8710 0x1701b2a4+2492 2493 8709 0x1701b197+2493 2494 8708 0x1701b08c+2494 2495 8707 0x1701af81+2495 2496 8706 0x1701ae78+2496 2497 8705 0x1701ad6f+2497 2498 8704 0x1701ac67+2498 2499 8703 0x1701ab5f+2499 2500 8702 0x1701aa58+2500 2501 8701 0x1701a951+2501 2502 8700 0x1701a84b+2502 2503 8699 0x1701a747+2503 2504 8698 0x1701a641+2504 2505 8697 0x1701a53e+2505 2506 8696 0x1701a43a+2506 2507 8695 0x1701a337+2507 2508 8694 0x1701a236+2508 2509 8693 0x1701a134+2509 2510 8692 0x1701a032+2510 2511 8691 0x17019f32+2511 2512 8690 0x17019e33+2512 2513 8689 0x17019d34+2513 2514 8688 0x17019c35+2514 2515 8687 0x17019b38+2515 2516 8686 0x17019a3a+2516 2517 8685 0x1701993e+2517 2518 8684 0x17019843+2518 2519 8683 0x17019747+2519 2520 8682 0x1701964c+2520 2521 8681 0x17019553+2521 2522 8680 0x17019459+2522 2523 8679 0x17019360+2523 2524 8678 0x17019267+2524 2525 8677 0x1701916f+2525 2526 8676 0x17019078+2526 2527 8675 0x17018f81+2527 2528 8674 0x17018e8c+2528 2529 8673 0x17018d96+2529 2530 8672 0x17018ca1+2530 2531 8671 0x17018bad+2531 2532 8670 0x17018aba+2532 2533 8669 0x170189c6+2533 2534 8668 0x170188d4+2534 2535 8667 0x170187e3+2535 2536 8666 0x170186f1+2536 2537 8665 0x17018601+2537 2538 8664 0x17018510+2538 2539 8663 0x17018421+2539 2540 8662 0x17018332+2540 2541 8661 0x17018243+2541 2542 8660 0x17018154+2542 2543 8659 0x17018068+2543 2544 8658 0x17017f7b+2544 2545 8657 0x17017e8f+2545 2546 8656 0x17017da2+2546 2547 8655 0x17017cb8+2547 2548 8654 0x17017bcd+2548 2549 8653 0x17017ae3+2549 2550 8652 0x170179fa+2550 2551 8651 0x17017912+2551 2552 8650 0x17017829+2552 2553 8649 0x17017742+2553 2554 8648 0x1701765a+2554 2555 8647 0x17017573+2555 2556 8646 0x1701748d+2556 2557 8645 0x170173a7+2557 2558 8644 0x170172c3+2558 2559 8643 0x170171dd+2559 2560 8642 0x170170f9+2560 2561 8641 0x17017016+2561 2562 8640 0x17016f32+2562 2563 8639 0x17016e51+2563 2564 8638 0x17016d70+2564 2565 8637 0x17016c8e+2565 2566 8636 0x17016bad+2566 2567 8635 0x17016acd+2567 2568 8634 0x170169ed+2568 2569 8633 0x1701690e+2569 2570 8632 0x1701682f+2570 2571 8631 0x17016751+2571 2572 8630 0x17016673+2572 2573 8629 0x17016596+2573 2574 8628 0x170164b8+2574 2575 8627 0x170163dc+2575 2576 8626 0x17016300+2576 2577 8625 0x17016226+2577 2578 8624 0x1701614b+2578 2579 8623 0x17016070+2579 2580 8622 0x17015f97+2580 2581 8621 0x17015ebe+2581 2582 8620 0x17015de6+2582 2583 8619 0x17015d0e+2583 2584 8618 0x17015c36+2584 2585 8617 0x17015b5e+2585 2586 8616 0x17015a87+2586 2587 8615 0x170159b2+2587 2588 8614 0x170158db+2588 2589 8613 0x17015806+2589 2590 8612 0x17015732+2590 2591 8611 0x1701565e+2591 2592 8610 0x1701558c+2592 2593 8609 0x170154ba+2593 2594 8608 0x170153e9+2594 2595 8607 0x17015318+2595 2596 8606 0x17015247+2596 2597 8605 0x17015177+2597 2598 8604 0x170150a7+2598 2599 8603 0x17014fd9+2599 2600 8602 0x17014f0a+2600 2601 8601 0x17014e3c+2601 2602 8600 0x17014d6e+2602 2603 8599 0x17014ca0+2603 2604 8598 0x17014bd4+2604 2605 8597 0x17014b07+2605 2606 8596 0x17014a3c+2606 2607 8595 0x17014970+2607 2608 8594 0x170148a5+2608 2609 8593 0x170147db+2609 2610 8592 0x17014712+2610 2611 8591 0x17014648+2611 2612 8590 0x17014580+2612 2613 8589 0x170144b7+2613 2614 8588 0x170143ef+2614 2615 8587 0x17014327+2615 2616 8586 0x17014260+2616 2617 8585 0x1701419a+2617 2618 8584 0x170140d3+2618 2619 8583 0x1701400e+2619 2620 8582 0x17013f49+2620 2621 8581 0x17013e83+2621 2622 8580 0x17013dc0+2622 2623 8579 0x17013cfc+2623 2624 8578 0x17013c38+2624 2625 8577 0x17013b75+2625 2626 8576 0x17013ab3+2626 2627 8575 0x170139f2+2627 2628 8574 0x17013930+2628 2629 8573 0x1701386e+2629 2630 8572 0x170137ae+2630 2631 8571 0x170136ee+2631 2632 8570 0x1701362e+2632 2633 8569 0x1701356f+2633 2634 8568 0x170134b0+2634 2635 8567 0x170133f2+2635 2636 8566 0x17013334+2636 2637 8565 0x17013276+2637 2638 8564 0x170131b9+2638 2639 8563 0x170130fd+2639 2640 8562 0x17013041+2640 2641 8561 0x17012f86+2641 2642 8560 0x17012ecb+2642 2643 8559 0x17012e0f+2643 2644 8558 0x17012d56+2644 2645 8557 0x17012c9b+2645 2646 8556 0x17012be2+2646 2647 8555 0x17012b29+2647 2648 8554 0x17012a70+2648 2649 8553 0x170129b9+2649 2650 8552 0x17012901+2650 2651 8551 0x17012849+2651 2652 8550 0x17012793+2652 2653 8549 0x170126dc+2653 2654 8548 0x17012626+2654 2655 8547 0x17012570+2655 2656 8546 0x170124bc+2656 2657 8545 0x17012408+2657 2658 8544 0x17012353+2658 2659 8543 0x1701229f+2659 2660 8542 0x170121ec+2660 2661 8541 0x17012139+2661 2662 8540 0x17012087+2662 2663 8539 0x17011fd5+2663 2664 8538 0x17011f23+2664 2665 8537 0x17011e72+2665 2666 8536 0x17011dc1+2666 2667 8535 0x17011d11+2667 2668 8534 0x17011c61+2668 2669 8533 0x17011bb1+2669 2670 8532 0x17011b02+2670 2671 8531 0x17011a54+2671 2672 8530 0x170119a6+2672 2673 8529 0x170118f8+2673 2674 8528 0x1701184a+2674 2675 8527 0x1701179e+2675 2676 8526 0x170116f1+2676 2677 8525 0x17011645+2677 2678 8524 0x17011599+2678 2679 8523 0x170114ee+2679 2680 8522 0x17011443+2680 2681 8521 0x17011398+2681 2682 8520 0x170112ee+2682 2683 8519 0x17011244+2683 2684 8518 0x1701119b+2684 2685 8517 0x170110f2+2685 2686 8516 0x1701104a+2686 2687 8515 0x17010fa3+2687 2688 8514 0x17010efb+2688 2689 8513 0x17010e53+2689 2690 8512 0x17010dad+2690 2691 8511 0x17010d06+2691 2692 8510 0x17010c60+2692 2693 8509 0x17010bbb+2693 2694 8508 0x17010b16+2694 2695 8507 0x17010a70+2695 2696 8506 0x170109cc+2696 2697 8505 0x17010928+2697 2698 8504 0x17010884+2698 2699 8503 0x170107e1+2699 2700 8502 0x1701073e+2700 2701 8501 0x1701069c+2701 2702 8500 0x170105f9+2702 2703 8499 0x17010558+2703 2704 8498 0x170104b7+2704 2705 8497 0x17010416+2705 2706 8496 0x17010376+2706 2707 8495 0x170102d6+2707 2708 8494 0x17010236+2708 2709 8493 0x17010197+2709 2710 8492 0x170100f8+2710 2711 8491 0x17010059+2711 2712 8490 0x1700ffbb+2712 2713 8489 0x1700ff1d+2713 2714 8488 0x1700fe80+2714 2715 8487 0x1700fde3+2715 2716 8486 0x1700fd46+2716 2717 8485 0x1700fcaa+2717 2718 8484 0x1700fc0e+2718 2719 8483 0x1700fb73+2719 2720 8482 0x1700fad8+2720 2721 8481 0x1700fa3d+2721 2722 8480 0x1700f9a3+2722 2723 8479 0x1700f909+2723 2724 8478 0x1700f86f+2724 2725 8477 0x1700f7d6+2725 2726 8476 0x1700f73d+2726 2727 8475 0x1700f6a5+2727 2728 8474 0x1700f60c+2728 2729 8473 0x1700f575+2729 2730 8472 0x1700f4dd+2730 2731 8471 0x1700f446+2731 2732 8470 0x1700f3af+2732 2733 8469 0x1700f31a+2733 2734 8468 0x1700f284+2734 2735 8467 0x1700f1ee+2735 2736 8466 0x1700f159+2736 2737 8465 0x1700f0c5+2737 2738 8464 0x1700f030+2738 2739 8463 0x1700ef9b+2739 2740 8462 0x1700ef08+2740 2741 8461 0x1700ee74+2741 2742 8460 0x1700ede1+2742 2743 8459 0x1700ed4f+2743 2744 8458 0x1700ecbc+2744 2745 8457 0x1700ec2b+2745 2746 8456 0x1700eb99+2746 2747 8455 0x1700eb08+2747 2748 8454 0x1700ea77+2748 2749 8453 0x1700e9e7+2749 2750 8452 0x1700e957+2750 2751 8451 0x1700e8c7+2751 2752 8450 0x1700e837+2752 2753 8449 0x1700e7a8+2753 2754 8448 0x1700e719+2754 2755 8447 0x1700e68b+2755 2756 8446 0x1700e5fd+2756 2757 8445 0x1700e56f+2757 2758 8444 0x1700e4e2+2758 2759 8443 0x1700e454+2759 2760 8442 0x1700e3c8+2760 2761 8441 0x1700e33b+2761 2762 8440 0x1700e2b0+2762 2763 8439 0x1700e223+2763 2764 8438 0x1700e199+2764 2765 8437 0x1700e10e+2765 2766 8436 0x1700e084+2766 2767 8435 0x1700dff9+2767 2768 8434 0x1700df6f+2768 2769 8433 0x1700dee5+2769 2770 8432 0x1700de5c+2770 2771 8431 0x1700ddd3+2771 2772 8430 0x1700dd4a+2772 2773 8429 0x1700dcc2+2773 2774 8428 0x1700dc3a+2774 2775 8427 0x1700dbb2+2775 2776 8426 0x1700db2b+2776 2777 8425 0x1700daa4+2777 2778 8424 0x1700da1d+2778 2779 8423 0x1700d997+2779 2780 8422 0x1700d911+2780 2781 8421 0x1700d88c+2781 2782 8420 0x1700d806+2782 2783 8419 0x1700d781+2783 2784 8418 0x1700d6fd+2784 2785 8417 0x1700d678+2785 2786 8416 0x1700d5f4+2786 2787 8415 0x1700d570+2787 2788 8414 0x1700d4ed+2788 2789 8413 0x1700d46a+2789 2790 8412 0x1700d3e7+2790 2791 8411 0x1700d364+2791 2792 8410 0x1700d2e2+2792 2793 8409 0x1700d260+2793 2794 8408 0x1700d1de+2794 2795 8407 0x1700d15e+2795 2796 8406 0x1700d0dd+2796 2797 8405 0x1700d05c+2797 2798 8404 0x1700cfdc+2798 2799 8403 0x1700cf5c+2799 2800 8402 0x1700cedc+2800 2801 8401 0x1700ce5d+2801 2802 8400 0x1700cddd+2802 2803 8399 0x1700cd5f+2803 2804 8398 0x1700cce1+2804 2805 8397 0x1700cc62+2805 2806 8396 0x1700cbe4+2806 2807 8395 0x1700cb67+2807 2808 8394 0x1700cae9+2808 2809 8393 0x1700ca6d+2809 2810 8392 0x1700c9f1+2810 2811 8391 0x1700c974+2811 2812 8390 0x1700c8f8+2812 2813 8389 0x1700c87c+2813 2814 8388 0x1700c801+2814 2815 8387 0x1700c785+2815 2816 8386 0x1700c70b+2816 2817 8385 0x1700c690+2817 2818 8384 0x1700c616+2818 2819 8383 0x1700c59c+2819 2820 8382 0x1700c522+2820 2821 8381 0x1700c4a9+2821 2822 8380 0x1700c42f+2822 2823 8379 0x1700c3b7+2823 2824 8378 0x1700c33e+2824 2825 8377 0x1700c2c7+2825 2826 8376 0x1700c24f+2826 2827 8375 0x1700c1d7+2827 2828 8374 0x1700c160+2828 2829 8373 0x1700c0e8+2829 2830 8372 0x1700c072+2830 2831 8371 0x1700bffb+2831 2832 8370 0x1700bf85+2832 2833 8369 0x1700bf0f+2833 2834 8368 0x1700be99+2834 2835 8367 0x1700be23+2835 2836 8366 0x1700bdaf+2836 2837 8365 0x1700bd39+2837 2838 8364 0x1700bcc4+2838 2839 8363 0x1700bc50+2839 2840 8362 0x1700bbdc+2840 2841 8361 0x1700bb69+2841 2842 8360 0x1700baf6+2842 2843 8359 0x1700ba82+2843 2844 8358 0x1700ba0f+2844 2845 8357 0x1700b99d+2845 2846 8356 0x1700b92a+2846 2847 8355 0x1700b8b8+2847 2848 8354 0x1700b846+2848 2849 8353 0x1700b7d5+2849 2850 8352 0x1700b763+2850 2851 8351 0x1700b6f2+2851 2852 8350 0x1700b681+2852 2853 8349 0x1700b611+2853 2854 8348 0x1700b5a0+2854 2855 8347 0x1700b530+2855 2856 8346 0x1700b4c1+2856 2857 8345 0x1700b451+2857 2858 8344 0x1700b3e2+2858 2859 8343 0x1700b373+2859 2860 8342 0x1700b304+2860 2861 8341 0x1700b296+2861 2862 8340 0x1700b228+2862 2863 8339 0x1700b1ba+2863 2864 8338 0x1700b14c+2864 2865 8337 0x1700b0de+2865 2866 8336 0x1700b071+2866 2867 8335 0x1700b004+2867 2868 8334 0x1700af97+2868 2869 8333 0x1700af2a+2869 2870 8332 0x1700aebe+2870 2871 8331 0x1700ae53+2871 2872 8330 0x1700ade7+2872 2873 8329 0x1700ad7c+2873 2874 8328 0x1700ad10+2874 2875 8327 0x1700aca5+2875 2876 8326 0x1700ac3a+2876 2877 8325 0x1700abd0+2877 2878 8324 0x1700ab66+2878 2879 8323 0x1700aafd+2879 2880 8322 0x1700aa94+2880 2881 8321 0x1700aa2b+2881 2882 8320 0x1700a9c2+2882 2883 8319 0x1700a959+2883 2884 8318 0x1700a8f1+2884 2885 8317 0x1700a889+2885 2886 8316 0x1700a821+2886 2887 8315 0x1700a7bb+2887 2888 8314 0x1700a753+2888 2889 8313 0x1700a6ec+2889 2890 8312 0x1700a686+2890 2891 8311 0x1700a61f+2891 2892 8310 0x1700a5b9+2892 2893 8309 0x1700a553+2893 2894 8308 0x1700a4ed+2894 2895 8307 0x1700a487+2895 2896 8306 0x1700a422+2896 2897 8305 0x1700a3bd+2897 2898 8304 0x1700a358+2898 2899 8303 0x1700a2f3+2899 2900 8302 0x1700a28f+2900 2901 8301 0x1700a22b+2901 2902 8300 0x1700a1c7+2902 2903 8299 0x1700a164+2903 2904 8298 0x1700a100+2904 2905 8297 0x1700a09d+2905 2906 8296 0x1700a03a+2906 2907 8295 0x17009fd7+2907 2908 8294 0x17009f75+2908 2909 8293 0x17009f13+2909 2910 8292 0x17009eb1+2910 2911 8291 0x17009e4f+2911 2912 8290 0x17009ded+2912 2913 8289 0x17009d8c+2913 2914 8288 0x17009d2b+2914 2915 8287 0x17009cca+2915 2916 8286 0x17009c69+2916 2917 8285 0x17009c09+2917 2918 8284 0x17009ba9+2918 2919 8283 0x17009b49+2919 2920 8282 0x17009ae9+2920 2921 8281 0x17009a8a+2921 2922 8280 0x17009a2a+2922 2923 8279 0x170099cb+2923 2924 8278 0x1700996c+2924 2925 8277 0x1700990e+2925 2926 8276 0x170098af+2926 2927 8275 0x17009851+2927 2928 8274 0x170097f3+2928 2929 8273 0x17009795+2929 2930 8272 0x17009738+2930 2931 8271 0x170096db+2931 2932 8270 0x1700967e+2932 2933 8269 0x17009621+2933 2934 8268 0x170095c4+2934 2935 8267 0x17009568+2935 2936 8266 0x1700950c+2936 2937 8265 0x170094b0+2937 2938 8264 0x17009454+2938 2939 8263 0x170093f9+2939 2940 8262 0x1700939d+2940 2941 8261 0x17009342+2941 2942 8260 0x170092e7+2942 2943 8259 0x1700928d+2943 2944 8258 0x17009232+2944 2945 8257 0x170091d8+2945 2946 8256 0x1700917e+2946 2947 8255 0x17009124+2947 2948 8254 0x170090cb+2948 2949 8253 0x17009072+2949 2950 8252 0x17009019+2950 2951 8251 0x17008fc0+2951 2952 8250 0x17008f67+2952 2953 8249 0x17008f0f+2953 2954 8248 0x17008eb6+2954 2955 8247 0x17008e5e+2955 2956 8246 0x17008e06+2956 2957 8245 0x17008dae+2957 2958 8244 0x17008d57+2958 2959 8243 0x17008d00+2959 2960 8242 0x17008ca9+2960 2961 8241 0x17008c52+2961 2962 8240 0x17008bfb+2962 2963 8239 0x17008ba5+2963 2964 8238 0x17008b4f+2964 2965 8237 0x17008af9+2965 2966 8236 0x17008aa3+2966 2967 8235 0x17008a4e+2967 2968 8234 0x170089f9+2968 2969 8233 0x170089a3+2969 2970 8232 0x1700894e+2970 2971 8231 0x170088fa+2971 2972 8230 0x170088a5+2972 2973 8229 0x17008851+2973 2974 8228 0x170087fc+2974 2975 8227 0x170087a9+2975 2976 8226 0x17008755+2976 2977 8225 0x17008701+2977 2978 8224 0x170086ae+2978 2979 8223 0x1700865b+2979 2980 8222 0x17008608+2980 2981 8221 0x170085b5+2981 2982 8220 0x17008563+2982 2983 8219 0x17008511+2983 2984 8218 0x170084bf+2984 2985 8217 0x1700846c+2985 2986 8216 0x1700841b+2986 2987 8215 0x170083c9+2987 2988 8214 0x17008378+2988 2989 8213 0x17008327+2989 2990 8212 0x170082d6+2990 2991 8211 0x17008285+2991 2992 8210 0x17008235+2992 2993 8209 0x170081e4+2993 2994 8208 0x17008194+2994 2995 8207 0x17008144+2995 2996 8206 0x170080f5+2996 2997 8205 0x170080a5+2997 2998 8204 0x17008056+2998 2999 8203 0x17008006+2999 3000 8202 0x167fb822+3000 3001 8201 0x167f6950+3001 3002 8200 0x167f1a7e+3002 3003 8199 0x167ecc02+3003 3004 8198 0x167e7ddc+3004 3005 8197 0x167e2fb6+3005 3006 8196 0x167de1e5+3006 3007 8195 0x167d946b+3007 3008 8194 0x167d46f1+3008 3009 8193 0x167cf9cc+3009 3010 8192 0x167caca7+3010 3011 8191 0x167c5fd9+3011 3012 8190 0x167c130a+3012 3013 8189 0x167bc691+3013 3014 8188 0x167b7a6e+3014 3015 8187 0x167b2df5+3015 3016 8186 0x167ae228+3016 3017 8185 0x167a965b+3017 3018 8184 0x167a4a8e+3018 3019 8183 0x1679ff6d+3019 3020 8182 0x1679b3f6+3020 3021 8181 0x167968d4+3021 3022 8180 0x16791e09+3022 3023 8179 0x1678d33d+3023 3024 8178 0x167888c8+3024 3025 8177 0x16783efe+3025 3026 8176 0x1677f4de+3026 3027 8175 0x1677aabe+3027 3028 8174 0x167760f4+3028 3029 8173 0x16771780+3029 3030 8172 0x1676ce0c+3030 3031 8171 0x16768498+3031 3032 8170 0x16763b7a+3032 3033 8169 0x1675f2b1+3033 3034 8168 0x1675a9e9+3034 3035 8167 0x16756177+3035 3036 8166 0x16751904+3036 3037 8165 0x1674d0e8+3037 3038 8164 0x167488cb+3038 3039 8163 0x16744104+3039 3040 8162 0x1673f993+3040 3041 8161 0x1673b222+3041 3042 8160 0x16736ab2+3042 3043 8159 0x16732397+3043 3044 8158 0x1672dcd1+3044 3045 8157 0x1672960c+3045 3046 8156 0x16724f47+3046 3047 8155 0x167208d8+3047 3048 8154 0x1671c2be+3048 3049 8153 0x16717c4f+3049 3050 8152 0x1671368b+3050 3051 8151 0x1670f0c8+3051 3052 8150 0x1670ab04+3052 3053 8149 0x16706597+3053 3054 8148 0x1670207f+3054 3055 8147 0x166fdbbd+3055 3056 8146 0x166f96a5+3056 3057 8145 0x166f51e3+3057 3058 8144 0x166f0d77+3058 3059 8143 0x166ec90b+3059 3060 8142 0x166e849f+3060 3061 8141 0x166e4088+3061 3062 8140 0x166dfcc8+3062 3063 8139 0x166db908+3063 3064 8138 0x166d7547+3064 3065 8137 0x166d31dd+3065 3066 8136 0x166ceec8+3066 3067 8135 0x166cabb4+3067 3068 8134 0x166c689f+3068 3069 8133 0x166c25e0+3069 3070 8132 0x166be321+3070 3071 8131 0x166ba10e+3071 3072 8130 0x166b5ea5+3072 3073 8129 0x166b1c92+3073 3074 8128 0x166ada7f+3074 3075 8127 0x166a98c2+3075 3076 8126 0x166a575b+3076 3077 8125 0x166a159d+3077 3078 8124 0x1669d436+3078 3079 8123 0x16699325+3079 3080 8122 0x16695213+3080 3081 8121 0x16691157+3081 3082 8120 0x1668d09c+3082 3083 8119 0x16688fe0+3083 3084 8118 0x16684f7b+3084 3085 8117 0x16680f6b+3085 3086 8116 0x1667cfb1+3086 3087 8115 0x16678fa1+3087 3088 8114 0x16674fe7+3088 3089 8113 0x1667102d+3089 3090 8112 0x1666d0c8+3090 3091 8111 0x16669164+3091 3092 8110 0x16665256+3092 3093 8109 0x16661348+3093 3094 8108 0x1665d48f+3094 3095 8107 0x166595d7+3095 3096 8106 0x1665571e+3096 3097 8105 0x166518bc+3097 3098 8104 0x1664da59+3098 3099 8103 0x16649c4d+3099 3100 8102 0x16645e96+3100 3101 8101 0x166420df+3101 3102 8100 0x1663e328+3102 3103 8099 0x1663a5c7+3103 3104 8098 0x16636866+3104 3105 8097 0x16632b05+3105 3106 8096 0x1662edfa+3106 3107 8095 0x1662b0ef+3107 3108 8094 0x1662743a+3108 3109 8093 0x16623784+3109 3110 8092 0x1661fb25+3110 3111 8091 0x1661bec6+3111 3112 8090 0x166182bc+3112 3113 8089 0x166146b3+3113 3114 8088 0x16610aa9+3114 3115 8087 0x1660cef5+3115 3116 8086 0x16609342+3116 3117 8085 0x166057e4+3117 3118 8084 0x16601c86+3118 3119 8083 0x165fe17e+3119 3120 8082 0x165fa676+3120 3121 8081 0x165f6b6e+3121 3122 8080 0x165f30bc+3122 3123 8079 0x165ef60a+3123 3124 8078 0x165ebb58+3124 3125 8077 0x165e80fc+3125 3126 8076 0x165e46a0+3126 3127 8075 0x165e0c99+3127 3128 8074 0x165dd293+3128 3129 8073 0x165d98e2+3129 3130 8072 0x165d5f32+3130 3131 8071 0x165d2581+3131 3132 8070 0x165cec7d+3132 3133 8069 0x165cb322+3133 3134 8068 0x165c7a1d+3134 3135 8067 0x165c4119+3135 3136 8066 0x165c0814+3136 3137 8065 0x165bcf65+3137 3138 8064 0x165b96b6+3138 3139 8063 0x165b5e5d+3139 3140 8062 0x165b2604+3140 3141 8061 0x165aedab+3141 3142 8060 0x165ab5a8+3142 3143 8059 0x165a7da5+3143 3144 8058 0x165a45a1+3144 3145 8057 0x165a0df4+3145 3146 8056 0x1659d647+3146 3147 8055 0x16599f45+3147 3148 8054 0x165967ee+3148 3149 8053 0x16593096+3149 3150 8052 0x1658f995+3150 3151 8051 0x1658c293+3151 3152 8050 0x16588be8+3152 3153 8049 0x1658553c+3153 3154 8048 0x16581e90+3154 3155 8047 0x1657e83a+3155 3156 8046 0x1657b1e4+3156 3157 8045 0x16577b8f+3157 3158 8044 0x1657458f+3158 3159 8043 0x16570f8f+3159 3160 8042 0x1656d9e4+3160 3161 8041 0x1656a43a+3161 3162 8040 0x16566e90+3162 3163 8039 0x1656393c+3163 3164 8038 0x165603e8+3164 3165 8037 0x1655cee9+3165 3166 8036 0x16559a16+3166 3167 8035 0x1655656d+3167 3168 8034 0x165530f0+3168 3169 8033 0x1654fc72+3169 3170 8032 0x1654c84a+3170 3171 8031 0x16549423+3171 3172 8030 0x16545ffb+3172 3173 8029 0x16542c29+3173 3174 8028 0x1653f857+3174 3175 8027 0x1653c4b0+3175 3176 8026 0x16539109+3176 3177 8025 0x16535d8d+3177 3178 8024 0x16532a92+3178 3179 8023 0x1652f741+3179 3180 8022 0x1652c446+3180 3181 8021 0x1652914a+3181 3182 8020 0x16525e7a+3182 3183 8019 0x16522baa+3183 3184 8018 0x1651f904+3184 3185 8017 0x1651c68a+3185 3186 8016 0x1651943b+3186 3187 8015 0x165161eb+3187 3188 8014 0x16512fc7+3188 3189 8013 0x1650fdcd+3189 3190 8012 0x1650cbfe+3190 3191 8011 0x16509a2f+3191 3192 8010 0x1650688c+3192 3193 8009 0x16503713+3193 3194 8008 0x165005c5+3194 3195 8007 0x164fd477+3195 3196 8006 0x164fa329+3196 3197 8005 0x164f7206+3197 3198 8004 0x164f410e+3198 3199 8003 0x164f1041+3199 3200 8002 0x164edf9f+3200 3201 8001 0x164eaefd+3201 3202 8000 0x164e7e5b+3202 3203 7999 0x164e4e0e+3203 3204 7998 0x164e1dc2+3204 3205 7997 0x164deda1+3205 3206 7996 0x164dbd7f+3206 3207 7995 0x164d8d89+3207 3208 7994 0x164d5dbd+3208 3209 7993 0x164d2e48+3209 3210 7992 0x164cfea7+3210 3211 7991 0x164ccf31+3211 3212 7990 0x164c9fbc+3212 3213 7989 0x164c709c+3213 3214 7988 0x164c4151+3214 3215 7987 0x164c125c+3215 3216 7986 0x164be367+3216 3217 7985 0x164bb49d+3217 3218 7984 0x164b85d4+3218 3219 7983 0x164b575f+3219 3220 7982 0x164b28c0+3220 3221 7981 0x164afa77+3221 3222 7980 0x164acc2e+3222 3223 7979 0x164a9e10+3223 3224 7978 0x164a7048+3224 3225 7977 0x164a4254+3225 3226 7976 0x164a1461+3226 3227 7975 0x1649e6c4+3227 3228 7974 0x1649b926+3228 3229 7973 0x16498bb4+3229 3230 7972 0x16495e41+3230 3231 7971 0x164930fa+3231 3232 7970 0x164903dd+3232 3233 7969 0x1648d6c1+3233 3234 7968 0x1648a9cf+3234 3235 7967 0x16487d08+3235 3236 7966 0x16485041+3236 3237 7965 0x164823a6+3237 3238 7964 0x1647f70a+3238 3239 7963 0x1647cac4+3239 3240 7962 0x16479e7e+3240 3241 7961 0x16477263+3241 3242 7960 0x16474648+3242 3243 7959 0x16471a2d+3243 3244 7958 0x1646ee68+3244 3245 7957 0x1646c2a3+3245 3246 7956 0x164696dd+3246 3247 7955 0x16466b43+3247 3248 7954 0x16463fd4+3248 3249 7953 0x1646148f+3249 3250 7952 0x1645e94b+3250 3251 7951 0x1645be07+3251 3252 7950 0x16459318+3252 3253 7949 0x1645682a+3253 3254 7948 0x16453d3b+3254 3255 7947 0x164512a3+3255 3256 7946 0x1644e80a+3256 3257 7945 0x1644bd9c+3257 3258 7944 0x1644932f+3258 3259 7943 0x164468c1+3259 3260 7942 0x16443e7e+3260 3261 7941 0x16441466+3261 3262 7940 0x1643ea79+3262 3263 7939 0x1643c08c+3263 3264 7938 0x164396a0+3264 3265 7937 0x16436cde+3265 3266 7936 0x16434346+3266 3267 7935 0x164319da+3267 3268 7934 0x1642f06e+3268 3269 7933 0x1642c702+3269 3270 7932 0x16429e17+3270 3271 7931 0x164274d6+3271 3272 7930 0x16424bea+3272 3273 7929 0x164222ff+3273 3274 7928 0x1641fa14+3274 3275 7927 0x1641d17e+3275 3276 7926 0x1641a8be+3276 3277 7925 0x16418053+3277 3278 7924 0x164157e9+3278 3279 7923 0x16412f7e+3279 3280 7922 0x1641073f+3280 3281 7921 0x1640df2a+3281 3282 7920 0x1640b715+3282 3283 7919 0x16408f2c+3283 3284 7918 0x1640676d+3284 3285 7917 0x16403fd9+3285 3286 7916 0x1640181a+3286 3287 7915 0x163ff086+3287 3288 7914 0x163fc91d+3288 3289 7913 0x163fa1df+3289 3290 7912 0x163f7a76+3290 3291 7911 0x163f5363+3291 3292 7910 0x163f2c50+3292 3293 7909 0x163f053d+3293 3294 7908 0x163ede80+3294 3295 7907 0x163eb798+3295 3296 7906 0x163e9106+3296 3297 7905 0x163e6a48+3297 3298 7904 0x163e43e1+3298 3299 7903 0x163e1d7a+3299 3300 7902 0x163df712+3300 3301 7901 0x163dd101+3301 3302 7900 0x163daaef+3302 3303 7899 0x163d84de+3303 3304 7898 0x163d5ef7+3304 3305 7897 0x163d3911+3305 3306 7896 0x163d1355+3306 3307 7895 0x163ced9a+3307 3308 7894 0x163cc809+3308 3309 7893 0x163ca2a3+3309 3310 7892 0x163c7d3d+3310 3311 7891 0x163c57d8+3311 3312 7890 0x163c329d+3312 3313 7889 0x163c0d8d+3313 3314 7888 0x163be87d+3314 3315 7887 0x163bc398+3315 3316 7886 0x163b9ede+3316 3317 7885 0x163b7a24+3317 3318 7884 0x163b556a+3318 3319 7883 0x163b30db+3319 3320 7882 0x163b0c4c+3320 3321 7881 0x163ae7e8+3321 3322 7880 0x163ac3ae+3322 3323 7879 0x163a9f75+3323 3324 7878 0x163a7b3c+3324 3325 7877 0x163a572e+3325 3326 7876 0x163a334a+3326 3327 7875 0x163a0f67+3327 3328 7874 0x1639eb83+3328 3329 7873 0x1639c7cb+3329 3330 7872 0x1639a43d+3330 3331 7871 0x163980db+3331 3332 7870 0x16395d78+3332 3333 7869 0x16393a16+3333 3334 7868 0x163916de+3334 3335 7867 0x1638f3a6+3335 3336 7866 0x1638d09a+3336 3337 7865 0x1638ad8d+3337 3338 7864 0x16388a80+3338 3339 7863 0x163867c9+3339 3340 7862 0x163844e8+3340 3341 7861 0x1638225c+3341 3342 7860 0x1637ffa5+3342 3343 7859 0x1637dd44+3343 3344 7858 0x1637bab8+3344 3345 7857 0x16379882+3345 3346 7856 0x16377621+3346 3347 7855 0x16375441+3347 3348 7854 0x1637320b+3348 3349 7853 0x16371000+3349 3350 7852 0x1636ee1f+3350 3351 7851 0x1636cc3f+3351 3352 7850 0x1636aa8a+3352 3353 7849 0x163688d5+3353 3354 7848 0x1636674a+3354 3355 7847 0x163645c0+3355 3356 7846 0x16362461+3356 3357 7845 0x16360301+3357 3358 7844 0x1635e1cd+3358 3359 7843 0x1635c098+3359 3360 7842 0x16359f64+3360 3361 7841 0x16357e5a+3361 3362 7840 0x16355da7+3362 3363 7839 0x16353cc8+3363 3364 7838 0x16351be9+3364 3365 7837 0x1634fb36+3365 3366 7836 0x1634da82+3366 3367 7835 0x1634b9f9+3367 3368 7834 0x1634999c+3368 3369 7833 0x1634793e+3369 3370 7832 0x163458e0+3370 3371 7831 0x163438ad+3371 3372 7830 0x1634187a+3372 3373 7829 0x1633f872+3373 3374 7828 0x1633d86a+3374 3375 7827 0x1633b862+3375 3376 7826 0x16339885+3376 3377 7825 0x163378d3+3377 3378 7824 0x1633594c+3378 3379 7823 0x1633399a+3379 3380 7822 0x16331a13+3380 3381 7821 0x1632fa8c+3381 3382 7820 0x1632db30+3382 3383 7819 0x1632bbd3+3383 3384 7818 0x16329ca2+3384 3385 7817 0x16327d71+3385 3386 7816 0x16325e6a+3386 3387 7815 0x16323f64+3387 3388 7814 0x1632205e+3388 3389 7813 0x16320182+3389 3390 7812 0x1631e2d2+3390 3391 7811 0x1631c3f6+3391 3392 7810 0x1631a571+3392 3393 7809 0x163186eb+3393 3394 7808 0x16316866+3394 3395 7807 0x16314a0b+3395 3396 7806 0x16312bb0+3396 3397 7805 0x16310d56+3397 3398 7804 0x1630ef26+3398 3399 7803 0x1630d0f6+3399 3400 7802 0x1630b2f2+3400 3401 7801 0x163094ed+3401 3402 7800 0x16307713+3402 3403 7799 0x16305939+3403 3404 7798 0x16303b5f+3404 3405 7797 0x16301db1+3405 3406 7796 0x16300002+3406 3407 7795 0x162fe27e+3407 3408 7794 0x162fc525+3408 3409 7793 0x162fa7a1+3409 3410 7792 0x162f8a48+3410 3411 7791 0x162f6cef+3411 3412 7790 0x162f4fc0+3412 3413 7789 0x162f3292+3413 3414 7788 0x162f158f+3414 3415 7787 0x162ef88c+3415 3416 7786 0x162edb89+3416 3417 7785 0x162ebeb1+3417 3418 7784 0x162ea1d8+3418 3419 7783 0x162e8500+3419 3420 7782 0x162e6853+3420 3421 7781 0x162e4bd0+3421 3422 7780 0x162e2f4e+3422 3423 7779 0x162e12cc+3423 3424 7778 0x162df674+3424 3425 7777 0x162dda1d+3425 3426 7776 0x162dbdc5+3426 3427 7775 0x162da199+3427 3428 7774 0x162d856c+3428 3429 7773 0x162d696b+3429 3430 7772 0x162d4d69+3430 3431 7771 0x162d3168+3431 3432 7770 0x162d1591+3432 3433 7769 0x162cf9ba+3433 3434 7768 0x162cdde4+3434 3435 7767 0x162cc238+3435 3436 7766 0x162ca68c+3436 3437 7765 0x162c8b0b+3437 3438 7764 0x162c6f8b+3438 3439 7763 0x162c5435+3439 3440 7762 0x162c38df+3440 3441 7761 0x162c1d89+3441 3442 7760 0x162c0233+3442 3443 7759 0x162be708+3443 3444 7758 0x162bcc08+3444 3445 7757 0x162bb0dd+3445 3446 7756 0x162b95dd+3446 3447 7755 0x162b7add+3447 3448 7754 0x162b6008+3448 3449 7753 0x162b4533+3449 3450 7752 0x162b2a89+3450 3451 7751 0x162b0fb4+3451 3452 7750 0x162af535+3452 3453 7749 0x162adab6+3453 3454 7748 0x162ac077+3454 3455 7747 0x162aa622+3455 3456 7746 0x162a8bf9+3456 3457 7745 0x162a71ba+3457 3458 7744 0x162a57a6+3458 3459 7743 0x162a3d93+3459 3460 7742 0x162a2394+3460 3461 7741 0x162a09ab+3461 3462 7740 0x1629efc2+3462 3463 7739 0x1629d5ef+3463 3464 7738 0x1629bc31+3464 3465 7737 0x1629a288+3465 3466 7736 0x162988e0+3466 3467 7735 0x16296f4d+3467 3468 7734 0x162955cf+3468 3469 7733 0x16293c51+3469 3470 7732 0x16292314+3470 3471 7731 0x162909c2+3471 3472 7730 0x1628f06f+3472 3473 7729 0x1628d747+3473 3474 7728 0x1628be1f+3474 3475 7727 0x1628a4f8+3475 3476 7726 0x16288bfb+3476 3477 7725 0x162872fe+3477 3478 7724 0x16285a17+3478 3479 7723 0x1628412f+3479 3480 7722 0x1628285d+3480 3481 7721 0x16280fa1+3481 3482 7720 0x1627f6fa+3482 3483 7719 0x1627de53+3483 3484 7718 0x1627c5c2+3484 3485 7717 0x1627ad5b+3485 3486 7716 0x162794df+3486 3487 7715 0x16277c79+3487 3488 7714 0x16276427+3488 3489 7713 0x16274bd6+3489 3490 7712 0x1627339b+3490 3491 7711 0x16271b75+3491 3492 7710 0x1627034e+3492 3493 7709 0x1626eb53+3493 3494 7708 0x1626d343+3494 3495 7707 0x1626bb5d+3495 3496 7706 0x1626a377+3496 3497 7705 0x16268ba7+3497 3498 7704 0x162673d6+3498 3499 7703 0x16265c1c+3499 3500 7702 0x162644a1+3500 3501 7701 0x16262cfc+3501 3502 7700 0x1626156c+3502 3503 7699 0x1625fdf1+3503 3504 7698 0x1625e68d+3504 3505 7697 0x1625cf28+3505 3506 7696 0x1625b7d8+3506 3507 7695 0x1625a089+3507 3508 7694 0x1625894f+3508 3509 7693 0x1625722a+3509 3510 7692 0x16255b05+3510 3511 7691 0x1625440c+3511 3512 7690 0x16252cfd+3512 3513 7689 0x16251618+3513 3514 7688 0x1624ff34+3514 3515 7687 0x1624e866+3515 3516 7686 0x1624d1ac+3516 3517 7685 0x1624baf3+3517 3518 7684 0x1624a44f+3518 3519 7683 0x16248dac+3519 3520 7682 0x1624771d+3520 3521 7681 0x162460a4+3521 3522 7680 0x16244a2c+3522 3523 7679 0x162433c8+3523 3524 7678 0x16241d65+3524 3525 7677 0x1624072c+3525 3526 7676 0x1623f0de+3526 3527 7675 0x1623dabc+3527 3528 7674 0x1623c499+3528 3529 7673 0x1623ae8b+3529 3530 7672 0x1623987e+3530 3531 7671 0x1623829b+3531 3532 7670 0x16236cb8+3532 3533 7669 0x162356d6+3533 3534 7668 0x16234109+3534 3535 7667 0x16232b3c+3535 3536 7666 0x16231599+3536 3537 7665 0x1622ffe2+3537 3538 7664 0x1622ea55+3538 3539 7663 0x1622d4c8+3539 3540 7662 0x1622bf3c+3540 3541 7661 0x1622a9c4+3541 3542 7660 0x16229463+3542 3543 7659 0x16227f16+3543 3544 7658 0x162269ca+3544 3545 7657 0x1622547e+3545 3546 7656 0x16223f72+3546 3547 7655 0x16222a50+3547 3548 7654 0x1622152f+3548 3549 7653 0x16220023+3549 3550 7652 0x1621eb2d+3550 3551 7651 0x1621d636+3551 3552 7650 0x1621c155+3552 3553 7649 0x1621ac8a+3553 3554 7648 0x162197be+3554 3555 7647 0x16218308+3555 3556 7646 0x16216e52+3556 3557 7645 0x162159b1+3557 3558 7644 0x16214511+3558 3559 7643 0x16213086+3559 3560 7642 0x16211c10+3560 3561 7641 0x1621079a+3561 3562 7640 0x1620f350+3562 3563 7639 0x1620df05+3563 3564 7638 0x1620caba+3564 3565 7637 0x1620b685+3565 3566 7636 0x1620a250+3566 3567 7635 0x16208e30+3567 3568 7634 0x16207a10+3568 3569 7633 0x16206606+3569 3570 7632 0x162051fb+3570 3571 7631 0x16203e1c+3571 3572 7630 0x16202a27+3572 3573 7629 0x1620165d+3573 3574 7628 0x1620027e+3574 3575 7627 0x161feec9+3575 3576 7626 0x161fdb15+3576 3577 7625 0x161fc78b+3577 3578 7624 0x161fb3ec+3578 3579 7623 0x161fa04d+3579 3580 7622 0x161f8cd9+3580 3581 7621 0x161f7965+3581 3582 7620 0x161f65f1+3582 3583 7619 0x161f5292+3583 3584 7618 0x161f3f34+3584 3585 7617 0x161f2c00+3585 3586 7616 0x161f18b7+3586 3587 7615 0x161f0583+3587 3588 7614 0x161ef265+3588 3589 7613 0x161edf5c+3589 3590 7612 0x161ecc3e+3590 3591 7611 0x161eb94b+3591 3592 7610 0x161ea66d+3592 3593 7609 0x161e937a+3593 3594 7608 0x161e809c+3594 3595 7607 0x161e6dd4+3595 3596 7606 0x161e5b0b+3596 3597 7605 0x161e4858+3597 3598 7604 0x161e35a6+3598 3599 7603 0x161e2308+3599 3600 7602 0x161e106b+3600 3601 7601 0x161dfde3+3601 3602 7600 0x161deb70+3602 3603 7599 0x161dd8fe+3603 3604 7598 0x161dc68b+3604 3605 7597 0x161db42e+3605 3606 7596 0x161da1e7+3606 3607 7595 0x161d8f9f+3607 3608 7594 0x161d7d6d+3608 3609 7593 0x161d6b51+3609 3610 7592 0x161d591e+3610 3611 7591 0x161d4717+3611 3612 7590 0x161d34fb+3612 3613 7589 0x161d2309+3613 3614 7588 0x161d1102+3614 3615 7587 0x161cff26+3615 3616 7586 0x161ced34+3616 3617 7585 0x161cdb6d+3617 3618 7584 0x161cc9a6+3618 3619 7583 0x161cb7e0+3619 3620 7582 0x161ca62e+3620 3621 7581 0x161c947d+3621 3622 7580 0x161c82e1+3622 3623 7579 0x161c7170+3623 3624 7578 0x161c5fea+3624 3625 7577 0x161c4e64+3625 3626 7576 0x161c3cf3+3626 3627 7575 0x161c2b97+3627 3628 7574 0x161c1a3c+3628 3629 7573 0x161c08e1+3629 3630 7572 0x161bf79b+3630 3631 7571 0x161be655+3631 3632 7570 0x161bd524+3632 3633 7569 0x161bc409+3633 3634 7568 0x161bb2ee+3634 3635 7567 0x161ba1d3+3635 3636 7566 0x161b90cd+3636 3637 7565 0x161b7fdd+3637 3638 7564 0x161b6f03+3638 3639 7563 0x161b5e13+3639 3640 7562 0x161b4d38+3640 3641 7561 0x161b3c5d+3641 3642 7560 0x161b2b98+3642 3643 7559 0x161b1ae9+3643 3644 7558 0x161b0a39+3644 3645 7557 0x161af989+3645 3646 7556 0x161ae8ef+3646 3647 7555 0x161ad855+3647 3648 7554 0x161ac7d0+3648 3649 7553 0x161ab74b+3649 3650 7552 0x161aa6dc+3650 3651 7551 0x161a966d+3651 3652 7550 0x161a8613+3652 3653 7549 0x161a75b9+3653 3654 7548 0x161a658a+3654 3655 7547 0x161a5546+3655 3656 7546 0x161a4501+3656 3657 7545 0x161a34d2+3657 3658 7544 0x161a24b9+3658 3659 7543 0x161a149f+3659 3660 7542 0x161a0486+3660 3661 7541 0x1619f482+3661 3662 7540 0x1619e494+3662 3663 7539 0x1619d490+3663 3664 7538 0x1619c4b7+3664 3665 7537 0x1619b4c8+3665 3666 7536 0x1619a504+3666 3667 7535 0x1619952b+3667 3668 7534 0x16198568+3668 3669 7533 0x161975cf+3669 3670 7532 0x16196621+3670 3671 7531 0x16195673+3671 3672 7530 0x161946da+3672 3673 7529 0x16193757+3673 3674 7528 0x161927bf+3674 3675 7527 0x16191851+3675 3676 7526 0x161908ce+3676 3677 7525 0x1618f960+3677 3678 7524 0x1618ea08+3678 3679 7523 0x1618daaf+3679 3680 7522 0x1618cb57+3680 3681 7521 0x1618bc14+3681 3682 7520 0x1618ace7+3682 3683 7519 0x16189da4+3683 3684 7518 0x16188e8c+3684 3685 7517 0x16187f75+3685 3686 7516 0x1618705d+3686 3687 7515 0x16186145+3687 3688 7514 0x16185243+3688 3689 7513 0x16184356+3689 3690 7512 0x16183453+3690 3691 7511 0x1618257c+3691 3692 7510 0x1618168f+3692 3693 7509 0x161807b7+3693 3694 7508 0x1617f8f5+3694 3695 7507 0x1617ea33+3695 3696 7506 0x1617db71+3696 3697 7505 0x1617ccaf+3697 3698 7504 0x1617be18+3698 3699 7503 0x1617af6c+3699 3700 7502 0x1617a0ea+3700 3701 7501 0x16179253+3701 3702 7500 0x161783d2+3702 3703 7499 0x16177550+3703 3704 7498 0x161766e4+3704 3705 7497 0x16175878+3705 3706 7496 0x16174a0c+3706 3707 7495 0x16173bb5+3707 3708 7494 0x16172d5e+3708 3709 7493 0x16171f08+3709 3710 7492 0x161710c7+3710 3711 7491 0x1617029b+3711 3712 7490 0x1616f45a+3712 3713 7489 0x1616e643+3713 3714 7488 0x1616d818+3714 3715 7487 0x1616ca17+3715 3716 7486 0x1616bc01+3716 3717 7485 0x1616ae00+3717 3718 7484 0x16169fff+3718 3719 7483 0x16169214+3719 3720 7482 0x16168413+3720 3721 7481 0x1616763d+3721 3722 7480 0x16166852+3722 3723 7479 0x16165a7c+3723 3724 7478 0x16164cbb+3724 3725 7477 0x16163efb+3725 3726 7476 0x1616313b+3726 3727 7475 0x1616237a+3727 3728 7474 0x161615cf+3728 3729 7473 0x1616083a+3729 3730 7472 0x1615faa4+3730 3731 7471 0x1615ed0f+3731 3732 7470 0x1615df7a+3732 3733 7469 0x1615d1fa+3733 3734 7468 0x1615c47a+3734 3735 7467 0x1615b70f+3735 3736 7466 0x1615a9a4+3736 3737 7465 0x16159c3a+3737 3738 7464 0x16158ee5+3738 3739 7463 0x16158190+3739 3740 7462 0x1615743b+3740 3741 7461 0x16156711+3741 3742 7460 0x161559e6+3742 3743 7459 0x16154cbc+3743 3744 7458 0x16153fa8+3744 3745 7457 0x16153293+3745 3746 7456 0x16152594+3746 3747 7455 0x16151895+3747 3748 7454 0x16150b95+3748 3749 7453 0x1614fea1+3749 3750 7452 0x1614f1b7+3750 3751 7451 0x1614e4d8+3751 3752 7450 0x1614d7f9+3752 3753 7449 0x1614cb25+3753 3754 7448 0x1614be51+3754 3755 7447 0x1614b192+3755 3756 7446 0x1614a4d3+3756 3757 7445 0x1614981f+3757 3758 7444 0x16148b6b+3758 3759 7443 0x16147ec2+3759 3760 7442 0x16147223+3760 3761 7441 0x1614659a+3761 3762 7440 0x16145906+3762 3763 7439 0x16144c7d+3763 3764 7438 0x16143ffe+3764 3765 7437 0x1614338b+3765 3766 7436 0x16142717+3766 3767 7435 0x16141aae+3767 3768 7434 0x16140e45+3768 3769 7433 0x161401e7+3769 3770 7432 0x1613f593+3770 3771 7431 0x1613e94b+3771 3772 7430 0x1613dd02+3772 3773 7429 0x1613d0c4+3773 3774 7428 0x1613c491+3774 3775 7427 0x1613b85d+3775 3776 7426 0x1613ac40+3776 3777 7425 0x1613a022+3777 3778 7424 0x16139404+3778 3779 7423 0x161387f1+3779 3780 7422 0x16137be9+3780 3781 7421 0x16136feb+3781 3782 7420 0x161363ed+3782 3783 7419 0x161357fa+3783 3784 7418 0x16134c08+3784 3785 7417 0x1613401f+3785 3786 7416 0x16133442+3786 3787 7415 0x16132865+3787 3788 7414 0x16131c9d+3788 3789 7413 0x161310ca+3789 3790 7412 0x1613050d+3790 3791 7411 0x1612f950+3791 3792 7410 0x1612eda8+3792 3793 7409 0x1612e200+3793 3794 7408 0x1612d658+3794 3795 7407 0x1612cabb+3795 3796 7406 0x1612bf1e+3796 3797 7405 0x1612b38c+3797 3798 7404 0x1612a804+3798 3799 7403 0x16129c7d+3799 3800 7402 0x16129100+3800 3801 7401 0x1612858e+3801 3802 7400 0x16127a27+3802 3803 7399 0x16126ebf+3803 3804 7398 0x16126358+3804 3805 7397 0x16125806+3805 3806 7396 0x16124cb4+3806 3807 7395 0x16124178+3807 3808 7394 0x16123631+3808 3809 7393 0x16122af4+3809 3810 7392 0x16121fb8+3810 3811 7391 0x16121486+3811 3812 7390 0x1612095f+3812 3813 7389 0x1611fe43+3813 3814 7388 0x1611f327+3814 3815 7387 0x1611e815+3815 3816 7386 0x1611dd04+3816 3817 7385 0x1611d1fd+3817 3818 7384 0x1611c701+3818 3819 7383 0x1611bc05+3819 3820 7382 0x1611b114+3820 3821 7381 0x1611a622+3821 3822 7380 0x16119b51+3822 3823 7379 0x16119075+3823 3824 7378 0x1611859a+3824 3825 7377 0x16117ac8+3825 3826 7376 0x16117002+3826 3827 7375 0x1611653c+3827 3828 7374 0x16115a80+3828 3829 7373 0x16114fc5+3829 3830 7372 0x16114514+3830 3831 7371 0x16113a6d+3831 3832 7370 0x16112fd2+3832 3833 7369 0x1611252c+3833 3834 7368 0x16111a9b+3834 3835 7367 0x1611100a+3835 3836 7366 0x16110585+3836 3837 7365 0x1610faff+3837 3838 7364 0x1610f099+3838 3839 7363 0x1610e61e+3839 3840 7362 0x1610dbad+3840 3841 7361 0x1610d147+3841 3842 7360 0x1610c6ec+3842 3843 7359 0x1610bc91+3843 3844 7358 0x1610b236+3844 3845 7357 0x1610a7e6+3845 3846 7356 0x16109da0+3846 3847 7355 0x16109366+3847 3848 7354 0x1610892b+3848 3849 7353 0x16107ef0+3849 3850 7352 0x161074c0+3850 3851 7351 0x16106a9a+3851 3852 7350 0x16106075+3852 3853 7349 0x16105665+3853 3854 7348 0x16104c55+3854 3855 7347 0x16104245+3855 3856 7346 0x16103840+3856 3857 7345 0x16102e3b+3857 3858 7344 0x16102441+3858 3859 7343 0x16101a46+3859 3860 7342 0x16101056+3860 3861 7341 0x16100671+3861 3862 7340 0x160ffc8d+3862 3863 7339 0x160ff2a8+3863 3864 7338 0x160fe8d8+3864 3865 7337 0x160fdf09+3865 3866 7336 0x160fd539+3866 3867 7335 0x160fcb74+3867 3868 7334 0x160fc1c5+3868 3869 7333 0x160fb80b+3869 3870 7332 0x160fae51+3870 3871 7331 0x160fa4ac+3871 3872 7330 0x160f9afd+3872 3873 7329 0x160f9163+3873 3874 7328 0x160f87c9+3874 3875 7327 0x160f7e30+3875 3876 7326 0x160f74a0+3876 3877 7325 0x160f6b1c+3877 3878 7324 0x160f6198+3878 3879 7323 0x160f581e+3879 3880 7322 0x160f4ea4+3880 3881 7321 0x160f4536+3881 3882 7320 0x160f3bc7+3882 3883 7319 0x160f3262+3883 3884 7318 0x160f2909+3884 3885 7317 0x160f1fb0+3885 3886 7316 0x160f1661+3886 3887 7315 0x160f0d12+3887 3888 7314 0x160f03ce+3888 3889 7313 0x160efa8a+3889 3890 7312 0x160ef146+3890 3891 7311 0x160ee818+3891 3892 7310 0x160edede+3892 3893 7309 0x160ed5bb+3893 3894 7308 0x160ecc97+3894 3895 7307 0x160ec373+3895 3896 7306 0x160eba5a+3896 3897 7305 0x160eb141+3897 3898 7304 0x160ea833+3898 3899 7303 0x160e9f3a+3899 3900 7302 0x160e9636+3900 3901 7301 0x160e8d33+3901 3902 7300 0x160e843a+3902 3903 7299 0x160e7b4c+3903 3904 7298 0x160e725e+3904 3905 7297 0x160e6970+3905 3906 7296 0x160e608c+3906 3907 7295 0x160e57b4+3907 3908 7294 0x160e4edb+3908 3909 7293 0x160e460d+3909 3910 7292 0x160e3d3f+3910 3911 7291 0x160e347c+3911 3912 7290 0x160e2bb9+3912 3913 7289 0x160e2300+3913 3914 7288 0x160e1a53+3914 3915 7287 0x160e11a5+3915 3916 7286 0x160e08f7+3916 3917 7285 0x160e0049+3917 3918 7284 0x160df7b1+3918 3919 7283 0x160def0e+3919 3920 7282 0x160de681+3920 3921 7281 0x160ddde8+3921 3922 7280 0x160dd566+3922 3923 7279 0x160dccd8+3923 3924 7278 0x160dc455+3924 3925 7277 0x160dbbdd+3925 3926 7276 0x160db365+3926 3927 7275 0x160daaf8+3927 3928 7274 0x160da28b+3928 3929 7273 0x160d9a28+3929 3930 7272 0x160d91d0+3930 3931 7271 0x160d8978+3931 3932 7270 0x160d8121+3932 3933 7269 0x160d78c9+3933 3934 7268 0x160d707c+3934 3935 7267 0x160d6839+3935 3936 7266 0x160d5ff7+3936 3937 7265 0x160d57b4+3937 3938 7264 0x160d4f7d+3938 3939 7263 0x160d4750+3939 3940 7262 0x160d3f23+3940 3941 7261 0x160d36f6+3941 3942 7260 0x160d2ed4+3942 3943 7259 0x160d26b2+3943 3944 7258 0x160d1e9a+3944 3945 7257 0x160d168d+3945 3946 7256 0x160d0e81+3946 3947 7255 0x160d0674+3947 3948 7254 0x160cfe72+3948 3949 7253 0x160cf670+3949 3950 7252 0x160cee79+3950 3951 7251 0x160ce682+3951 3952 7250 0x160cde8a+3952 3953 7249 0x160cd69e+3953 3954 7248 0x160ccebc+3954 3955 7247 0x160cc6cf+3955 3956 7246 0x160cbef8+3956 3957 7245 0x160cb721+3957 3958 7244 0x160caf4a+3958 3959 7243 0x160ca773+3959 3960 7242 0x160c9fb2+3960 3961 7241 0x160c97f0+3961 3962 7240 0x160c902f+3962 3963 7239 0x160c886d+3963 3964 7238 0x160c80b6+3964 3965 7237 0x160c790a+3965 3966 7236 0x160c7153+3966 3967 7235 0x160c69b2+3967 3968 7234 0x160c6206+3968 3969 7233 0x160c5a64+3969 3970 7232 0x160c52ce+3970 3971 7231 0x160c4b37+3971 3972 7230 0x160c43a0+3972 3973 7229 0x160c3c14+3973 3974 7228 0x160c3488+3974 3975 7227 0x160c2d07+3975 3976 7226 0x160c2591+3976 3977 7225 0x160c1e10+3977 3978 7224 0x160c1699+3978 3979 7223 0x160c0f2d+3979 3980 7222 0x160c07c2+3980 3981 7221 0x160c0056+3981 3982 7220 0x160bf8ea+3982 3983 7219 0x160bf189+3983 3984 7218 0x160bea33+3984 3985 7217 0x160be2dd+3985 3986 7216 0x160bdb86+3986 3987 7215 0x160bd43b+3987 3988 7214 0x160bccef+3988 3989 7213 0x160bc5a4+3989 3990 7212 0x160bbe63+3990 3991 7211 0x160bb738+3991 3992 7210 0x160baff7+3992 3993 7209 0x160ba8c1+3993 3994 7208 0x160ba196+3994 3995 7207 0x160b9a6a+3995 3996 7206 0x160b933f+3996 3997 7205 0x160b8c1e+3997 3998 7204 0x160b84fe+3998 3999 7203 0x160b7de8+3999 4000 7202 0x160b76d2+4000 4001 7201 0x160b6fbc+4001 4002 7200 0x160b68b1+4002 4003 7199 0x160b61a6+4003 4004 7198 0x160b5a9b+4004 4005 7197 0x160b539a+4005 4006 7196 0x160b4ca5+4006 4007 7195 0x160b45af+4007 4008 7194 0x160b3eb9+4008 4009 7193 0x160b37ce+4009 4010 7192 0x160b30d9+4010 4011 7191 0x160b29f9+4011 4012 7190 0x160b230e+4012 4013 7189 0x160b1c2d+4013 4014 7188 0x160b1558+4014 4015 7187 0x160b0e78+4015 4016 7186 0x160b07a2+4016 4017 7185 0x160b00d8+4017 4018 7184 0x160afa0d+4018 4019 7183 0x160af342+4019 4020 7182 0x160aec82+4020 4021 7181 0x160ae5c2+4021 4022 7180 0x160adf0d+4022 4023 7179 0x160ad84d+4023 4024 7178 0x160ad198+4024 4025 7177 0x160acaed+4025 4026 7176 0x160ac438+4026 4027 7175 0x160abd98+4027 4028 7174 0x160ab6f8+4028 4029 7173 0x160ab05e+4029 4030 7172 0x160aa9ce+4030 4031 7171 0x160aa33e+4031 4032 7170 0x160a9cb4+4032 4033 7169 0x160a962a+4033 4034 7168 0x160a8fa5+4034 4035 7167 0x160a8925+4035 4036 7166 0x160a82ab+4036 4037 7165 0x160a7c3b+4037 4038 7164 0x160a75c6+4038 4039 7163 0x160a6f57+4039 4040 7162 0x160a68e7+4040 4041 7161 0x160a6283+4041 4042 7160 0x160a5c1e+4042 4043 7159 0x160a55b9+4043 4044 7158 0x160a4f5f+4044 4045 7157 0x160a4905+4045 4046 7156 0x160a42b0+4046 4047 7155 0x160a3c5c+4047 4048 7154 0x160a3612+4048 4049 7153 0x160a2fc3+4049 4050 7152 0x160a297e+4050 4051 7151 0x160a2339+4051 4052 7150 0x160a1d00+4052 4053 7149 0x160a16cb+4053 4054 7148 0x160a1091+4054 4055 7147 0x160a0a5d+4055 4056 7146 0x160a042e+4056 4057 7145 0x1609fe04+4057 4058 7144 0x1609f7da+4058 4059 7143 0x1609f1b6+4059 4060 7142 0x1609eb91+4060 4061 7141 0x1609e578+4061 4062 7140 0x1609df5e+4062 4063 7139 0x1609d945+4063 4064 7138 0x1609d336+4064 4065 7137 0x1609cd27+4065 4066 7136 0x1609c718+4066 4067 7135 0x1609c114+4067 4068 7134 0x1609bb15+4068 4069 7133 0x1609b516+4069 4070 7132 0x1609af17+4070 4071 7131 0x1609a91e+4071 4072 7130 0x1609a32a+4072 4073 7129 0x16099d3b+4073 4074 7128 0x1609974c+4074 4075 7127 0x16099163+4075 4076 7126 0x16098b7a+4076 4077 7125 0x16098596+4077 4078 7124 0x16097fb7+4078 4079 7123 0x160979de+4079 4080 7122 0x16097405+4080 4081 7121 0x16096e2b+4081 4082 7120 0x1609685d+4082 4083 7119 0x16096294+4083 4084 7118 0x16095ccb+4084 4085 7117 0x16095707+4085 4086 7116 0x16095143+4086 4087 7115 0x16094b7f+4087 4088 7114 0x160945c6+4088 4089 7113 0x1609400d+4089 4090 7112 0x16093a59+4090 4091 7111 0x160934a6+4091 4092 7110 0x16092ef7+4092 4093 7109 0x1609294e+4093 4094 7108 0x160923a6+4094 4095 7107 0x16091e02+4095 4096 7106 0x1609185e+4096 4097 7105 0x160912c6+4097 4098 7104 0x16090d27+4098 4099 7103 0x16090799+4099 4100 7102 0x16090206+4100 4101 7101 0x1608fc78+4101 4102 7100 0x1608f6ef+4102 4103 7099 0x1608f166+4103 4104 7098 0x1608ebdd+4104 4105 7097 0x1608e65f+4105 4106 7096 0x1608e0e1+4106 4107 7095 0x1608db63+4107 4108 7094 0x1608d5eb+4108 4109 7093 0x1608d077+4109 4110 7092 0x1608cb0a+4110 4111 7091 0x1608c59c+4111 4112 7090 0x1608c02e+4112 4113 7089 0x1608bacb+4113 4114 7088 0x1608b56d+4114 4115 7087 0x1608b00a+4115 4116 7086 0x1608aab1+4116 4117 7085 0x1608a553+4117 4118 7084 0x1608a000+4118 4119 7083 0x16089aad+4119 4120 7082 0x1608955a+4120 4121 7081 0x16089012+4121 4122 7080 0x16088ac9+4122 4123 7079 0x16088581+4123 4124 7078 0x1608803e+4124 4125 7077 0x16087b00+4125 4126 7076 0x160875c3+4126 4127 7075 0x1608708b+4127 4128 7074 0x16086b58+4128 4129 7073 0x1608662a+4129 4130 7072 0x160860f7+4130 4131 7071 0x16085bcf+4131 4132 7070 0x160856a7+4132 4133 7069 0x1608517f+4133 4134 7068 0x16084c5c+4134 4135 7067 0x1608473f+4135 4136 7066 0x16084221+4136 4137 7065 0x16083d09+4137 4138 7064 0x160837f1+4138 4139 7063 0x160832de+4139 4140 7062 0x16082dd1+4140 4141 7061 0x160828c4+4141 4142 7060 0x160823bc+4142 4143 7059 0x16081eb4+4143 4144 7058 0x160819b1+4144 4145 7057 0x160814b9+4145 4146 7056 0x16080fbc+4146 4147 7055 0x16080ac4+4147 4148 7054 0x160805cd+4148 4149 7053 0x160800da+4149 4150 7052 0x1607fbe8+4150 4151 7051 0x1607f6fa+4151 4152 7050 0x1607f213+4152 4153 7049 0x1607ed2b+4153 4154 7048 0x1607e849+4154 4155 7047 0x1607e366+4155 4156 7046 0x1607de89+4156 4157 7045 0x1607d9ac+4157 4158 7044 0x1607d4d5+4158 4159 7043 0x1607d002+4159 4160 7042 0x1607cb35+4160 4161 7041 0x1607c663+4161 4162 7040 0x1607c196+4162 4163 7039 0x1607bccf+4163 4164 7038 0x1607b807+4164 4165 7037 0x1607b345+4165 4166 7036 0x1607ae88+4166 4167 7035 0x1607a9cb+4167 4168 7034 0x1607a50e+4168 4169 7033 0x1607a057+4169 4170 7032 0x16079ba5+4170 4171 7031 0x160796f3+4171 4172 7030 0x16079246+4172 4173 7029 0x16078d99+4173 4174 7028 0x160788f2+4174 4175 7027 0x16078450+4175 4176 7026 0x16077fae+4176 4177 7025 0x16077b0c+4177 4178 7024 0x1607766f+4178 4179 7023 0x160771d8+4179 4180 7022 0x16076d41+4180 4181 7021 0x160768aa+4181 4182 7020 0x16076418+4182 4183 7019 0x16075f8b+4183 4184 7018 0x16075aff+4184 4185 7017 0x16075678+4185 4186 7016 0x160751f0+4186 4187 7015 0x16074d6f+4187 4188 7014 0x160748ed+4188 4189 7013 0x16074470+4189 4190 7012 0x16073ff4+4190 4191 7011 0x16073b82+4191 4192 7010 0x1607370b+4192 4193 7009 0x1607329a+4193 4194 7008 0x16072e28+4194 4195 7007 0x160729bc+4195 4196 7006 0x16072555+4196 4197 7005 0x160720ee+4197 4198 7004 0x16071c87+4198 4199 7003 0x16071825+4199 4200 7002 0x160713c3+4200 4201 7001 0x16070f67+4201 4202 7000 0x16070b10+4202 4203 6999 0x160706ba+4203 4204 6998 0x16070263+4204 4205 6997 0x1606fe11+4205 4206 6996 0x1606f9c5+4206 4207 6995 0x1606f579+4207 4208 6994 0x1606f132+4208 4209 6993 0x1606eceb+4209 4210 6992 0x1606e8a5+4210 4211 6991 0x1606e463+4211 4212 6990 0x1606e027+4212 4213 6989 0x1606dbeb+4213 4214 6988 0x1606d7af+4214 4215 6987 0x1606d379+4215 4216 6986 0x1606cf42+4216 4217 6985 0x1606cb11+4217 4218 6984 0x1606c6e5+4218 4219 6983 0x1606c2b3+4219 4220 6982 0x1606be8d+4220 4221 6981 0x1606ba6c+4221 4222 6980 0x1606b645+4222 4223 6979 0x1606b224+4223 4224 6978 0x1606ae03+4224 4225 6977 0x1606a9e7+4225 4226 6976 0x1606a5cb+4226 4227 6975 0x1606a1af+4227 4228 6974 0x16069d9e+4228 4229 6973 0x16069988+4229 4230 6972 0x16069577+4230 4231 6971 0x1606916b+4231 4232 6970 0x16068d5f+4232 4233 6969 0x16068953+4233 4234 6968 0x1606854d+4234 4235 6967 0x1606814c+4235 4236 6966 0x16067d4b+4236 4237 6965 0x1606794f+4237 4238 6964 0x16067554+4238 4239 6963 0x16067158+4239 4240 6962 0x16066d62+4240 4241 6961 0x1606696c+4241 4242 6960 0x1606657b+4242 4243 6959 0x1606618a+4243 4244 6958 0x16065d99+4244 4245 6957 0x160659ad+4245 4246 6956 0x160655c7+4246 4247 6955 0x160651e1+4247 4248 6954 0x16064dfb+4248 4249 6953 0x16064a1a+4249 4250 6952 0x16064639+4250 4251 6951 0x1606425e+4251 4252 6950 0x16063e83+4252 4253 6949 0x16063aac+4253 4254 6948 0x160636d6+4254 4255 6947 0x16063300+4255 4256 6946 0x16062f30+4256 4257 6945 0x16062b64+4257 4258 6944 0x16062794+4258 4259 6943 0x160623c8+4259 4260 6942 0x16062002+4260 4261 6941 0x16061c3c+4261 4262 6940 0x1606187c+4262 4263 6939 0x160614b6+4263 4264 6938 0x160610fb+4264 4265 6937 0x16060d3a+4265 4266 6936 0x16060984+4266 4267 6935 0x160605ce+4267 4268 6934 0x16060218+4268 4269 6933 0x1605fe63+4269 4270 6932 0x1605fab2+4270 4271 6931 0x1605f702+4271 4272 6930 0x1605f356+4272 4273 6929 0x1605efab+4273 4274 6928 0x1605ec06+4274 4275 6927 0x1605e860+4275 4276 6926 0x1605e4ba+4276 4277 6925 0x1605e11a+4277 4278 6924 0x1605dd79+4278 4279 6923 0x1605d9d9+4279 4280 6922 0x1605d63e+4280 4281 6921 0x1605d2a8+4281 4282 6920 0x1605cf0d+4282 4283 6919 0x1605cb7d+4283 4284 6918 0x1605c7ec+4284 4285 6917 0x1605c45c+4285 4286 6916 0x1605c0cc+4286 4287 6915 0x1605bd41+4287 4288 6914 0x1605b9b6+4288 4289 6913 0x1605b630+4289 4290 6912 0x1605b2ab+4290 4291 6911 0x1605af25+4291 4292 6910 0x1605aba5+4292 4293 6909 0x1605a825+4293 4294 6908 0x1605a4a5+4294 4295 6907 0x1605a12a+4295 4296 6906 0x16059daf+4296 4297 6905 0x16059a3a+4297 4298 6904 0x160596ca+4298 4299 6903 0x16059354+4299 4300 6902 0x16058fe4+4300 4301 6901 0x16058c74+4301 4302 6900 0x16058904+4302 4303 6899 0x16058599+4303 4304 6898 0x16058234+4304 4305 6897 0x16057ec9+4305 4306 6896 0x16057b64+4306 4307 6895 0x16057804+4307 4308 6894 0x160574a4+4308 4309 6893 0x16057144+4309 4310 6892 0x16056de4+4310 4311 6891 0x16056a89+4311 4312 6890 0x1605672e+4312 4313 6889 0x160563de+4313 4314 6888 0x16056084+4314 4315 6887 0x16055d34+4315 4316 6886 0x160559e7+4316 4317 6885 0x1605569c+4317 4318 6884 0x16055354+4318 4319 6883 0x1605500c+4319 4320 6882 0x16054cca+4320 4321 6881 0x16054987+4321 4322 6880 0x16054645+4322 4323 6879 0x16054305+4323 4324 6878 0x16053fc8+4324 4325 6877 0x16053c8e+4325 4326 6876 0x16053956+4326 4327 6875 0x1605361e+4327 4328 6874 0x160532e9+4328 4329 6873 0x16052fb9+4329 4330 6872 0x16052c87+4330 4331 6871 0x16052957+4331 4332 6870 0x1605262a+4332 4333 6869 0x160522fd+4333 4334 6868 0x16051fd6+4334 4335 6867 0x16051cab+4335 4336 6866 0x16051986+4336 4337 6865 0x16051661+4337 4338 6864 0x1605133f+4338 4339 6863 0x1605101f+4339 4340 6862 0x16050d00+4340 4341 6861 0x160509e3+4341 4342 6860 0x160506c9+4342 4343 6859 0x160503b1+4343 4344 6858 0x1605009c+4344 4345 6857 0x1604fd87+4345 4346 6856 0x1604fa75+4346 4347 6855 0x1604f763+4347 4348 6854 0x1604f453+4348 4349 6853 0x1604f144+4349 4350 6852 0x1604ee3a+4350 4351 6851 0x1604eb30+4351 4352 6850 0x1604e826+4352 4353 6849 0x1604e521+4353 4354 6848 0x1604e21c+4354 4355 6847 0x1604df1a+4355 4356 6846 0x1604dc18+4356 4357 6845 0x1604d918+4357 4358 6844 0x1604d61c+4358 4359 6843 0x1604d322+4359 4360 6842 0x1604d02a+4360 4361 6841 0x1604cd30+4361 4362 6840 0x1604ca3c+4362 4363 6839 0x1604c747+4363 4364 6838 0x1604c455+4364 4365 6837 0x1604c163+4365 4366 6836 0x1604be74+4366 4367 6835 0x1604bb87+4367 4368 6834 0x1604b89d+4368 4369 6833 0x1604b5b3+4369 4370 6832 0x1604b2c9+4370 4371 6831 0x1604afe5+4371 4372 6830 0x1604ad00+4372 4373 6829 0x1604aa1e+4373 4374 6828 0x1604a73c+4374 4375 6827 0x1604a460+4375 4376 6826 0x1604a183+4376 4377 6825 0x16049ea9+4377 4378 6824 0x16049bd0+4378 4379 6823 0x160498f6+4379 4380 6822 0x16049621+4380 4381 6821 0x1604934d+4381 4382 6820 0x16049078+4382 4383 6819 0x16048da7+4383 4384 6818 0x16048ad7+4384 4385 6817 0x1604880b+4385 4386 6816 0x1604853f+4386 4387 6815 0x16048275+4387 4388 6814 0x16047fab+4388 4389 6813 0x16047ce4+4389 4390 6812 0x16047a22+4390 4391 6811 0x16047761+4391 4392 6810 0x1604749f+4392 4393 6809 0x160471dd+4393 4394 6808 0x16046f1e+4394 4395 6807 0x16046c62+4395 4396 6806 0x160469a8+4396 4397 6805 0x160466ef+4397 4398 6804 0x16046438+4398 4399 6803 0x16046181+4399 4400 6802 0x16045ecd+4400 4401 6801 0x16045c1b+4401 4402 6800 0x16045969+4402 4403 6799 0x160456ba+4403 4404 6798 0x1604540c+4404 4405 6797 0x16045165+4405 4406 6796 0x16044eb9+4406 4407 6795 0x16044c12+4407 4408 6794 0x1604496b+4408 4409 6793 0x160446c4+4409 4410 6792 0x16044420+4410 4411 6791 0x1604417e+4411 4412 6790 0x16043edd+4412 4413 6789 0x16043c3e+4413 4414 6788 0x160439a2+4414 4415 6787 0x16043706+4415 4416 6786 0x1604346c+4416 4417 6785 0x160431d3+4417 4418 6784 0x16042f3c+4418 4419 6783 0x16042ca8+4419 4420 6782 0x16042a14+4420 4421 6781 0x16042785+4421 4422 6780 0x160424f4+4422 4423 6779 0x16042265+4423 4424 6778 0x16041fd9+4424 4425 6777 0x16041d4d+4425 4426 6776 0x16041ac4+4426 4427 6775 0x1604183a+4427 4428 6774 0x160415b4+4428 4429 6773 0x1604132d+4429 4430 6772 0x160410a9+4430 4431 6771 0x16040e28+4431 4432 6770 0x16040ba7+4432 4433 6769 0x16040928+4433 4434 6768 0x160406a9+4434 4435 6767 0x1604042d+4435 4436 6766 0x160401b7+4436 4437 6765 0x1603ff3e+4437 4438 6764 0x1603fcc7+4438 4439 6763 0x1603fa50+4439 4440 6762 0x1603f7dd+4440 4441 6761 0x1603f569+4441 4442 6760 0x1603f2f8+4442 4443 6759 0x1603f089+4443 4444 6758 0x1603ee1b+4444 4445 6757 0x1603ebaf+4445 4446 6756 0x1603e943+4446 4447 6755 0x1603e6da+4447 4448 6754 0x1603e471+4448 4449 6753 0x1603e20a+4449 4450 6752 0x1603dfa4+4450 4451 6751 0x1603dd43+4451 4452 6750 0x1603dae1+4452 4453 6749 0x1603d880+4453 4454 6748 0x1603d622+4454 4455 6747 0x1603d3c4+4455 4456 6746 0x1603d168+4456 4457 6745 0x1603cf0c+4457 4458 6744 0x1603ccb3+4458 4459 6743 0x1603ca5a+4459 4460 6742 0x1603c804+4460 4461 6741 0x1603c5b0+4461 4462 6740 0x1603c35c+4462 4463 6739 0x1603c109+4463 4464 6738 0x1603beb8+4464 4465 6737 0x1603bc69+4465 4466 6736 0x1603ba1b+4466 4467 6735 0x1603b7d2+4467 4468 6734 0x1603b586+4468 4469 6733 0x1603b33e+4469 4470 6732 0x1603b0f5+4470 4471 6731 0x1603aeae+4471 4472 6730 0x1603ac6b+4472 4473 6729 0x1603aa27+4473 4474 6728 0x1603a7e4+4474 4475 6727 0x1603a5a3+4475 4476 6726 0x1603a364+4476 4477 6725 0x1603a126+4477 4478 6724 0x16039ee8+4478 4479 6723 0x16039cad+4479 4480 6722 0x16039a74+4480 4481 6721 0x1603983b+4481 4482 6720 0x16039605+4482 4483 6719 0x160393d1+4483 4484 6718 0x1603919b+4484 4485 6717 0x16038f68+4485 4486 6716 0x16038d37+4486 4487 6715 0x16038b06+4487 4488 6714 0x160388d8+4488 4489 6713 0x160386aa+4489 4490 6712 0x1603847e+4490 4491 6711 0x16038253+4491 4492 6710 0x1603802a+4492 4493 6709 0x16037e01+4493 4494 6708 0x16037bd9+4494 4495 6707 0x160379b5+4495 4496 6706 0x1603778f+4496 4497 6705 0x1603756f+4497 4498 6704 0x1603734e+4498 4499 6703 0x1603712d+4499 4500 6702 0x16036f0f+4500 4501 6701 0x16036cf1+4501 4502 6700 0x16036ad6+4502 4503 6699 0x160368ba+4503 4504 6698 0x160366a2+4504 4505 6697 0x16036489+4505 4506 6696 0x16036271+4506 4507 6695 0x1603605b+4507 4508 6694 0x16035e47+4508 4509 6693 0x16035c34+4509 4510 6692 0x16035a23+4510 4511 6691 0x16035813+4511 4512 6690 0x16035602+4512 4513 6689 0x160353f7+4513 4514 6688 0x160351e9+4514 4515 6687 0x16034fde+4515 4516 6686 0x16034dd5+4516 4517 6685 0x16034bcd+4517 4518 6684 0x160349c4+4518 4519 6683 0x160347be+4519 4520 6682 0x160345b8+4520 4521 6681 0x160343b5+4521 4522 6680 0x160341b2+4522 4523 6679 0x16033faf+4523 4524 6678 0x16033db1+4524 4525 6677 0x16033bb1+4525 4526 6676 0x160339b3+4526 4527 6675 0x160337b8+4527 4528 6674 0x160335bd+4528 4529 6673 0x160333c4+4529 4530 6672 0x160331cc+4530 4531 6671 0x16032fd3+4531 4532 6670 0x16032ddd+4532 4533 6669 0x16032be8+4533 4534 6668 0x160329f5+4534 4535 6667 0x16032802+4535 4536 6666 0x1603260e+4536 4537 6665 0x1603241e+4537 4538 6664 0x16032230+4538 4539 6663 0x16032043+4539 4540 6662 0x16031e55+4540 4541 6661 0x16031c6a+4541 4542 6660 0x16031a7f+4542 4543 6659 0x16031896+4543 4544 6658 0x160316ae+4544 4545 6657 0x160314c8+4545 4546 6656 0x160312e3+4546 4547 6655 0x160310fd+4547 4548 6654 0x16030f1a+4548 4549 6653 0x16030d37+4549 4550 6652 0x16030b57+4550 4551 6651 0x16030977+4551 4552 6650 0x16030796+4552 4553 6649 0x160305b9+4553 4554 6648 0x160303de+4554 4555 6647 0x16030203+4555 4556 6646 0x16030028+4556 4557 6645 0x1602fe4d+4557 4558 6644 0x1602fc75+4558 4559 6643 0x1602faa2+4559 4560 6642 0x1602f8ca+4560 4561 6641 0x1602f6f7+4561 4562 6640 0x1602f521+4562 4563 6639 0x1602f351+4563 4564 6638 0x1602f17e+4564 4565 6637 0x1602efae+4565 4566 6636 0x1602edde+4566 4567 6635 0x1602ec10+4567 4568 6634 0x1602ea43+4568 4569 6633 0x1602e878+4569 4570 6632 0x1602e6ad+4570 4571 6631 0x1602e4e2+4571 4572 6630 0x1602e31a+4572 4573 6629 0x1602e152+4573 4574 6628 0x1602df8c+4574 4575 6627 0x1602ddc7+4575 4576 6626 0x1602dc01+4576 4577 6625 0x1602da3f+4577 4578 6624 0x1602d87c+4578 4579 6623 0x1602d6bc+4579 4580 6622 0x1602d4fc+4580 4581 6621 0x1602d33c+4581 4582 6620 0x1602d17e+4582 4583 6619 0x1602cfc1+4583 4584 6618 0x1602ce03+4584 4585 6617 0x1602cc49+4585 4586 6616 0x1602ca8e+4586 4587 6615 0x1602c8d6+4587 4588 6614 0x1602c71e+4588 4589 6613 0x1602c568+4589 4590 6612 0x1602c3b0+4590 4591 6611 0x1602c1fe+4591 4592 6610 0x1602c048+4592 4593 6609 0x1602be96+4593 4594 6608 0x1602bce3+4594 4595 6607 0x1602bb33+4595 4596 6606 0x1602b983+4596 4597 6605 0x1602b7d3+4597 4598 6604 0x1602b626+4598 4599 6603 0x1602b478+4599 4600 6602 0x1602b2cb+4600 4601 6601 0x1602b120+4601 4602 6600 0x1602af76+4602 4603 6599 0x1602adce+4603 4604 6598 0x1602ac28+4604 4605 6597 0x1602aa86+4605 4606 6596 0x1602a8e2+4606 4607 6595 0x1602a73e+4607 4608 6594 0x1602a59d+4608 4609 6593 0x1602a3fc+4609 4610 6592 0x1602a25c+4610 4611 6591 0x1602a0bd+4611 4612 6590 0x16029f1f+4612 4613 6589 0x16029d81+4613 4614 6588 0x16029be5+4614 4615 6587 0x16029a4a+4615 4616 6586 0x160298b0+4616 4617 6585 0x16029717+4617 4618 6584 0x1602957e+4618 4619 6583 0x160293e8+4619 4620 6582 0x16029252+4620 4621 6581 0x160290bd+4621 4622 6580 0x16028f28+4622 4623 6579 0x16028d96+4623 4624 6578 0x16028c03+4624 4625 6577 0x16028a71+4625 4626 6576 0x160288e1+4626 4627 6575 0x16028751+4627 4628 6574 0x160285c1+4628 4629 6573 0x16028434+4629 4630 6572 0x160282a7+4630 4631 6571 0x1602811b+4631 4632 6570 0x16027f8f+4632 4633 6569 0x16027e05+4633 4634 6568 0x16027c7c+4634 4635 6567 0x16027af5+4635 4636 6566 0x1602796e+4636 4637 6565 0x160277e7+4637 4638 6564 0x16027662+4638 4639 6563 0x160274dd+4639 4640 6562 0x16027359+4640 4641 6561 0x160271d7+4641 4642 6560 0x16027055+4642 4643 6559 0x16026ed4+4643 4644 6558 0x16026d54+4644 4645 6557 0x16026bd6+4645 4646 6556 0x16026a57+4646 4647 6555 0x160268d9+4647 4648 6554 0x1602675d+4648 4649 6553 0x160265e1+4649 4650 6552 0x16026467+4650 4651 6551 0x160262ee+4651 4652 6550 0x16026176+4652 4653 6549 0x16025ffd+4653 4654 6548 0x16025e87+4654 4655 6547 0x16025d11+4655 4656 6546 0x16025b9c+4656 4657 6545 0x16025a27+4657 4658 6544 0x160258b3+4658 4659 6543 0x16025741+4659 4660 6542 0x160255cf+4660 4661 6541 0x1602545e+4661 4662 6540 0x160252ee+4662 4663 6539 0x16025180+4663 4664 6538 0x16025012+4664 4665 6537 0x16024ea3+4665 4666 6536 0x16024d39+4666 4667 6535 0x16024bce+4667 4668 6534 0x16024a62+4668 4669 6533 0x160248f9+4669 4670 6532 0x16024790+4670 4671 6531 0x16024627+4671 4672 6530 0x160244c0+4672 4673 6529 0x1602435a+4673 4674 6528 0x160241f5+4674 4675 6527 0x16024090+4675 4676 6526 0x16023f2c+4676 4677 6525 0x16023dc9+4677 4678 6524 0x16023c67+4678 4679 6523 0x16023b06+4679 4680 6522 0x160239a5+4680 4681 6521 0x16023847+4681 4682 6520 0x160236e9+4682 4683 6519 0x1602358a+4683 4684 6518 0x1602342e+4684 4685 6517 0x160232d1+4685 4686 6516 0x16023175+4686 4687 6515 0x1602301b+4687 4688 6514 0x16022ec1+4688 4689 6513 0x16022d68+4689 4690 6512 0x16022c11+4690 4691 6511 0x16022ab9+4691 4692 6510 0x16022962+4692 4693 6509 0x1602280d+4693 4694 6508 0x160226b8+4694 4695 6507 0x16022564+4695 4696 6506 0x16022411+4696 4697 6505 0x160222c1+4697 4698 6504 0x1602216f+4698 4699 6503 0x1602201e+4699 4700 6502 0x16021ece+4700 4701 6501 0x16021d7f+4701 4702 6500 0x16021c31+4702 4703 6499 0x16021ae3+4703 4704 6498 0x16021996+4704 4705 6497 0x1602184b+4705 4706 6496 0x160216ff+4706 4707 6495 0x160215b5+4707 4708 6494 0x1602146d+4708 4709 6493 0x16021324+4709 4710 6492 0x160211db+4710 4711 6491 0x16021095+4711 4712 6490 0x16020f51+4712 4713 6489 0x16020e0b+4713 4714 6488 0x16020cc6+4714 4715 6487 0x16020b83+4715 4716 6486 0x16020a3f+4716 4717 6485 0x160208fd+4717 4718 6484 0x160207bb+4718 4719 6483 0x1602067b+4719 4720 6482 0x1602053a+4720 4721 6481 0x160203fb+4721 4722 6480 0x160202bd+4722 4723 6479 0x1602017f+4723 4724 6478 0x16020042+4724 4725 6477 0x1601ff07+4725 4726 6476 0x1601fdca+4726 4727 6475 0x1601fc90+4727 4728 6474 0x1601fb58+4728 4729 6473 0x1601fa1f+4729 4730 6472 0x1601f8e7+4730 4731 6471 0x1601f7af+4731 4732 6470 0x1601f678+4732 4733 6469 0x1601f542+4733 4734 6468 0x1601f40e+4734 4735 6467 0x1601f2d9+4735 4736 6466 0x1601f1a4+4736 4737 6465 0x1601f071+4737 4738 6464 0x1601ef3f+4738 4739 6463 0x1601ee0d+4739 4740 6462 0x1601ecdd+4740 4741 6461 0x1601ebae+4741 4742 6460 0x1601ea7e+4742 4743 6459 0x1601e951+4743 4744 6458 0x1601e823+4744 4745 6457 0x1601e6f6+4745 4746 6456 0x1601e5ca+4746 4747 6455 0x1601e49f+4747 4748 6454 0x1601e373+4748 4749 6453 0x1601e24a+4749 4750 6452 0x1601e120+4750 4751 6451 0x1601dff7+4751 4752 6450 0x1601decf+4752 4753 6449 0x1601dda8+4753 4754 6448 0x1601dc80+4754 4755 6447 0x1601db5b+4755 4756 6446 0x1601da36+4756 4757 6445 0x1601d912+4757 4758 6444 0x1601d7f0+4758 4759 6443 0x1601d6cd+4759 4760 6442 0x1601d5aa+4760 4761 6441 0x1601d489+4761 4762 6440 0x1601d367+4762 4763 6439 0x1601d247+4763 4764 6438 0x1601d128+4764 4765 6437 0x1601d009+4765 4766 6436 0x1601ceeb+4766 4767 6435 0x1601cdcf+4767 4768 6434 0x1601ccb1+4768 4769 6433 0x1601cb96+4769 4770 6432 0x1601ca7b+4770 4771 6431 0x1601c960+4771 4772 6430 0x1601c846+4772 4773 6429 0x1601c72c+4773 4774 6428 0x1601c615+4774 4775 6427 0x1601c4fd+4775 4776 6426 0x1601c3e6+4776 4777 6425 0x1601c2d0+4777 4778 6424 0x1601c1ba+4778 4779 6423 0x1601c0a4+4779 4780 6422 0x1601bf90+4780 4781 6421 0x1601be7d+4781 4782 6420 0x1601bd6a+4782 4783 6419 0x1601bc57+4783 4784 6418 0x1601bb45+4784 4785 6417 0x1601ba34+4785 4786 6416 0x1601b923+4786 4787 6415 0x1601b814+4787 4788 6414 0x1601b705+4788 4789 6413 0x1601b5f8+4789 4790 6412 0x1601b4ea+4790 4791 6411 0x1601b3dc+4791 4792 6410 0x1601b2d0+4792 4793 6409 0x1601b1c4+4793 4794 6408 0x1601b0b9+4794 4795 6407 0x1601afaf+4795 4796 6406 0x1601aea4+4796 4797 6405 0x1601ad9c+4797 4798 6404 0x1601ac92+4798 4799 6403 0x1601ab8b+4799 4800 6402 0x1601aa83+4800 4801 6401 0x1601a97d+4801 4802 6400 0x1601a876+4802 4803 6399 0x1601a771+4803 4804 6398 0x1601a66e+4804 4805 6397 0x1601a569+4805 4806 6396 0x1601a465+4806 4807 6395 0x1601a362+4807 4808 6394 0x1601a261+4808 4809 6393 0x1601a15f+4809 4810 6392 0x1601a05d+4810 4811 6391 0x16019f5d+4811 4812 6390 0x16019e5d+4812 4813 6389 0x16019d5e+4813 4814 6388 0x16019c60+4814 4815 6387 0x16019b61+4815 4816 6386 0x16019a64+4816 4817 6385 0x16019968+4817 4818 6384 0x1601986b+4818 4819 6383 0x16019771+4819 4820 6382 0x16019676+4820 4821 6381 0x1601957c+4821 4822 6380 0x16019483+4822 4823 6379 0x16019389+4823 4824 6378 0x16019291+4824 4825 6377 0x16019199+4825 4826 6376 0x160190a2+4826 4827 6375 0x16018fab+4827 4828 6374 0x16018eb4+4828 4829 6373 0x16018dbf+4829 4830 6372 0x16018cca+4830 4831 6371 0x16018bd5+4831 4832 6370 0x16018ae2+4832 4833 6369 0x160189ee+4833 4834 6368 0x160188fc+4834 4835 6367 0x1601880b+4835 4836 6366 0x16018719+4836 4837 6365 0x16018628+4837 4838 6364 0x16018538+4838 4839 6363 0x16018449+4839 4840 6362 0x16018359+4840 4841 6361 0x1601826a+4841 4842 6360 0x1601817d+4842 4843 6359 0x1601808f+4843 4844 6358 0x16017fa2+4844 4845 6357 0x16017eb6+4845 4846 6356 0x16017dc9+4846 4847 6355 0x16017cdf+4847 4848 6354 0x16017bf4+4848 4849 6353 0x16017b0a+4849 4850 6352 0x16017a21+4850 4851 6351 0x16017939+4851 4852 6350 0x1601784f+4852 4853 6349 0x16017767+4853 4854 6348 0x16017681+4854 4855 6347 0x1601759a+4855 4856 6346 0x160174b3+4856 4857 6345 0x160173ce+4857 4858 6344 0x160172e8+4858 4859 6343 0x16017204+4859 4860 6342 0x16017120+4860 4861 6341 0x1601703c+4861 4862 6340 0x16016f59+4862 4863 6339 0x16016e77+4863 4864 6338 0x16016d94+4864 4865 6337 0x16016cb2+4865 4866 6336 0x16016bd2+4866 4867 6335 0x16016af2+4867 4868 6334 0x16016a12+4868 4869 6333 0x16016932+4869 4870 6332 0x16016853+4870 4871 6331 0x16016775+4871 4872 6330 0x16016697+4872 4873 6329 0x160165ba+4873 4874 6328 0x160164dd+4874 4875 6327 0x16016401+4875 4876 6326 0x16016325+4876 4877 6325 0x1601624a+4877 4878 6324 0x1601616f+4878 4879 6323 0x16016095+4879 4880 6322 0x16015fbb+4880 4881 6321 0x16015ee2+4881 4882 6320 0x16015e0a+4882 4883 6319 0x16015d32+4883 4884 6318 0x16015c5a+4884 4885 6317 0x16015b82+4885 4886 6316 0x16015aab+4886 4887 6315 0x160159d5+4887 4888 6314 0x160158ff+4888 4889 6313 0x1601582a+4889 4890 6312 0x16015755+4890 4891 6311 0x16015682+4891 4892 6310 0x160155af+4892 4893 6309 0x160154dd+4893 4894 6308 0x1601540b+4894 4895 6307 0x1601533a+4895 4896 6306 0x1601526a+4896 4897 6305 0x1601519a+4897 4898 6304 0x160150ca+4898 4899 6303 0x16014ffb+4899 4900 6302 0x16014f2c+4900 4901 6301 0x16014e5e+4901 4902 6300 0x16014d90+4902 4903 6299 0x16014cc3+4903 4904 6298 0x16014bf6+4904 4905 6297 0x16014b29+4905 4906 6296 0x16014a5d+4906 4907 6295 0x16014992+4907 4908 6294 0x160148c7+4908 4909 6293 0x160147fc+4909 4910 6292 0x16014732+4910 4911 6291 0x16014669+4911 4912 6290 0x160145a1+4912 4913 6289 0x160144d8+4913 4914 6288 0x16014410+4914 4915 6287 0x16014348+4915 4916 6286 0x16014281+4916 4917 6285 0x160141bb+4917 4918 6284 0x160140f5+4918 4919 6283 0x1601402f+4919 4920 6282 0x16013f6a+4920 4921 6281 0x16013ea4+4921 4922 6280 0x16013de0+4922 4923 6279 0x16013d1c+4923 4924 6278 0x16013c59+4924 4925 6277 0x16013b95+4925 4926 6276 0x16013ad3+4926 4927 6275 0x16013a12+4927 4928 6274 0x16013950+4928 4929 6273 0x1601388f+4929 4930 6272 0x160137ce+4930 4931 6271 0x1601370e+4931 4932 6270 0x1601364e+4932 4933 6269 0x1601358f+4933 4934 6268 0x160134d0+4934 4935 6267 0x16013412+4935 4936 6266 0x16013354+4936 4937 6265 0x16013296+4937 4938 6264 0x160131d9+4938 4939 6263 0x1601311c+4939 4940 6262 0x16013060+4940 4941 6261 0x16012fa4+4941 4942 6260 0x16012ee9+4942 4943 6259 0x16012e2f+4943 4944 6258 0x16012d74+4944 4945 6257 0x16012cbb+4945 4946 6256 0x16012c01+4946 4947 6255 0x16012b48+4947 4948 6254 0x16012a8f+4948 4949 6253 0x160129d7+4949 4950 6252 0x1601291f+4950 4951 6251 0x16012868+4951 4952 6250 0x160127b1+4952 4953 6249 0x160126fa+4953 4954 6248 0x16012644+4954 4955 6247 0x1601258f+4955 4956 6246 0x160124da+4956 4957 6245 0x16012425+4957 4958 6244 0x16012371+4958 4959 6243 0x160122be+4959 4960 6242 0x1601220a+4960 4961 6241 0x16012157+4961 4962 6240 0x160120a4+4962 4963 6239 0x16011ff2+4963 4964 6238 0x16011f41+4964 4965 6237 0x16011e90+4965 4966 6236 0x16011ddf+4966 4967 6235 0x16011d2e+4967 4968 6234 0x16011c7e+4968 4969 6233 0x16011bcf+4969 4970 6232 0x16011b20+4970 4971 6231 0x16011a70+4971 4972 6230 0x160119c2+4972 4973 6229 0x16011915+4973 4974 6228 0x16011867+4974 4975 6227 0x160117ba+4975 4976 6226 0x1601170e+4976 4977 6225 0x16011661+4977 4978 6224 0x160115b6+4978 4979 6223 0x1601150b+4979 4980 6222 0x16011460+4980 4981 6221 0x160113b4+4981 4982 6220 0x1601130b+4982 4983 6219 0x16011261+4983 4984 6218 0x160111b7+4984 4985 6217 0x1601110e+4985 4986 6216 0x16011066+4986 4987 6215 0x16010fbe+4987 4988 6214 0x16010f16+4988 4989 6213 0x16010e6f+4989 4990 6212 0x16010dc8+4990 4991 6211 0x16010d22+4991 4992 6210 0x16010c7c+4992 4993 6209 0x16010bd6+4993 4994 6208 0x16010b31+4994 4995 6207 0x16010a8c+4995 4996 6206 0x160109e8+4996 4997 6205 0x16010943+4997 4998 6204 0x160108a0+4998 4999 6203 0x160107fc+4999 5000 6202 0x16010759+5000 5001 6201 0x160106b7+5001 5002 6200 0x16010614+5002 5003 6199 0x16010573+5003 5004 6198 0x160104d2+5004 5005 6197 0x16010431+5005 5006 6196 0x16010391+5006 5007 6195 0x160102f1+5007 5008 6194 0x16010250+5008 5009 6193 0x160101b1+5009 5010 6192 0x16010112+5010 5011 6191 0x16010073+5011 5012 6190 0x1600ffd5+5012 5013 6189 0x1600ff37+5013 5014 6188 0x1600fe9a+5014 5015 6187 0x1600fdfd+5015 5016 6186 0x1600fd60+5016 5017 6185 0x1600fcc4+5017 5018 6184 0x1600fc28+5018 5019 6183 0x1600fb8d+5019 5020 6182 0x1600faf2+5020 5021 6181 0x1600fa57+5021 5022 6180 0x1600f9bd+5022 5023 6179 0x1600f922+5023 5024 6178 0x1600f889+5024 5025 6177 0x1600f7ef+5025 5026 6176 0x1600f756+5026 5027 6175 0x1600f6be+5027 5028 6174 0x1600f626+5028 5029 6173 0x1600f58e+5029 5030 6172 0x1600f4f7+5030 5031 6171 0x1600f460+5031 5032 6170 0x1600f3c9+5032 5033 6169 0x1600f332+5033 5034 6168 0x1600f29d+5034 5035 6167 0x1600f207+5035 5036 6166 0x1600f172+5036 5037 6165 0x1600f0dd+5037 5038 6164 0x1600f048+5038 5039 6163 0x1600efb4+5039 5040 6162 0x1600ef21+5040 5041 6161 0x1600ee8d+5041 5042 6160 0x1600edfa+5042 5043 6159 0x1600ed67+5043 5044 6158 0x1600ecd5+5044 5045 6157 0x1600ec43+5045 5046 6156 0x1600ebb1+5046 5047 6155 0x1600eb20+5047 5048 6154 0x1600ea8f+5048 5049 6153 0x1600e9ff+5049 5050 6152 0x1600e96e+5050 5051 6151 0x1600e8df+5051 5052 6150 0x1600e84f+5052 5053 6149 0x1600e7c0+5053 5054 6148 0x1600e731+5054 5055 6147 0x1600e6a3+5055 5056 6146 0x1600e614+5056 5057 6145 0x1600e587+5057 5058 6144 0x1600e4f9+5058 5059 6143 0x1600e46c+5059 5060 6142 0x1600e3e0+5060 5061 6141 0x1600e353+5061 5062 6140 0x1600e2c6+5062 5063 6139 0x1600e23b+5063 5064 6138 0x1600e1af+5064 5065 6137 0x1600e125+5065 5066 6136 0x1600e09a+5066 5067 6135 0x1600e010+5067 5068 6134 0x1600df86+5068 5069 6133 0x1600defc+5069 5070 6132 0x1600de73+5070 5071 6131 0x1600dde9+5071 5072 6130 0x1600dd61+5072 5073 6129 0x1600dcd8+5073 5074 6128 0x1600dc50+5074 5075 6127 0x1600dbc9+5075 5076 6126 0x1600db41+5076 5077 6125 0x1600daba+5077 5078 6124 0x1600da34+5078 5079 6123 0x1600d9ad+5079 5080 6122 0x1600d928+5080 5081 6121 0x1600d8a2+5081 5082 6120 0x1600d81c+5082 5083 6119 0x1600d797+5083 5084 6118 0x1600d713+5084 5085 6117 0x1600d68e+5085 5086 6116 0x1600d60a+5086 5087 6115 0x1600d586+5087 5088 6114 0x1600d503+5088 5089 6113 0x1600d47f+5089 5090 6112 0x1600d3fc+5090 5091 6111 0x1600d37a+5091 5092 6110 0x1600d2f8+5092 5093 6109 0x1600d275+5093 5094 6108 0x1600d1f4+5094 5095 6107 0x1600d172+5095 5096 6106 0x1600d0f2+5096 5097 6105 0x1600d072+5097 5098 6104 0x1600cff1+5098 5099 6103 0x1600cf71+5099 5100 6102 0x1600cef1+5100 5101 6101 0x1600ce72+5101 5102 6100 0x1600cdf3+5102 5103 6099 0x1600cd74+5103 5104 6098 0x1600ccf5+5104 5105 6097 0x1600cc77+5105 5106 6096 0x1600cbf9+5106 5107 6095 0x1600cb7c+5107 5108 6094 0x1600cafe+5108 5109 6093 0x1600ca81+5109 5110 6092 0x1600ca05+5110 5111 6091 0x1600c989+5111 5112 6090 0x1600c90c+5112 5113 6089 0x1600c891+5113 5114 6088 0x1600c816+5114 5115 6087 0x1600c79a+5115 5116 6086 0x1600c71f+5116 5117 6085 0x1600c6a5+5117 5118 6084 0x1600c62a+5118 5119 6083 0x1600c5b1+5119 5120 6082 0x1600c536+5120 5121 6081 0x1600c4bd+5121 5122 6080 0x1600c444+5122 5123 6079 0x1600c3cb+5123 5124 6078 0x1600c352+5124 5125 6077 0x1600c2da+5125 5126 6076 0x1600c263+5126 5127 6075 0x1600c1eb+5127 5128 6074 0x1600c173+5128 5129 6073 0x1600c0fc+5129 5130 6072 0x1600c085+5130 5131 6071 0x1600c00e+5131 5132 6070 0x1600bf98+5132 5133 6069 0x1600bf22+5133 5134 6068 0x1600bead+5134 5135 6067 0x1600be37+5135 5136 6066 0x1600bdc2+5136 5137 6065 0x1600bd4d+5137 5138 6064 0x1600bcd8+5138 5139 6063 0x1600bc64+5139 5140 6062 0x1600bbf0+5140 5141 6061 0x1600bb7c+5141 5142 6060 0x1600bb09+5142 5143 6059 0x1600ba96+5143 5144 6058 0x1600ba22+5144 5145 6057 0x1600b9b0+5145 5146 6056 0x1600b93d+5146 5147 6055 0x1600b8cb+5147 5148 6054 0x1600b859+5148 5149 6053 0x1600b7e7+5149 5150 6052 0x1600b776+5150 5151 6051 0x1600b705+5151 5152 6050 0x1600b694+5152 5153 6049 0x1600b623+5153 5154 6048 0x1600b5b3+5154 5155 6047 0x1600b543+5155 5156 6046 0x1600b4d3+5156 5157 6045 0x1600b464+5157 5158 6044 0x1600b3f5+5158 5159 6043 0x1600b385+5159 5160 6042 0x1600b316+5160 5161 6041 0x1600b2a8+5161 5162 6040 0x1600b23a+5162 5163 6039 0x1600b1cc+5163 5164 6038 0x1600b15e+5164 5165 6037 0x1600b0f0+5165 5166 6036 0x1600b083+5166 5167 6035 0x1600b016+5167 5168 6034 0x1600afa9+5168 5169 6033 0x1600af3c+5169 5170 6032 0x1600aed0+5170 5171 6031 0x1600ae64+5171 5172 6030 0x1600adf9+5172 5173 6029 0x1600ad8e+5173 5174 6028 0x1600ad22+5174 5175 6027 0x1600acb7+5175 5176 6026 0x1600ac4c+5176 5177 6025 0x1600abe2+5177 5178 6024 0x1600ab78+5178 5179 6023 0x1600ab0e+5179 5180 6022 0x1600aaa5+5180 5181 6021 0x1600aa3c+5181 5182 6020 0x1600a9d4+5182 5183 6019 0x1600a96b+5183 5184 6018 0x1600a903+5184 5185 6017 0x1600a89b+5185 5186 6016 0x1600a833+5186 5187 6015 0x1600a7cb+5187 5188 6014 0x1600a765+5188 5189 6013 0x1600a6fd+5189 5190 6012 0x1600a697+5190 5191 6011 0x1600a630+5191 5192 6010 0x1600a5ca+5192 5193 6009 0x1600a564+5193 5194 6008 0x1600a4fe+5194 5195 6007 0x1600a498+5195 5196 6006 0x1600a433+5196 5197 6005 0x1600a3ce+5197 5198 6004 0x1600a369+5198 5199 6003 0x1600a304+5199 5200 6002 0x1600a2a0+5200 5201 6001 0x1600a23c+5201 5202 6000 0x1600a1d8+5202 5203 5999 0x1600a174+5203 5204 5998 0x1600a111+5204 5205 5997 0x1600a0ae+5205 5206 5996 0x1600a04b+5206 5207 5995 0x16009fe8+5207 5208 5994 0x16009f85+5208 5209 5993 0x16009f23+5209 5210 5992 0x16009ec1+5210 5211 5991 0x16009e5f+5211 5212 5990 0x16009dfd+5212 5213 5989 0x16009d9c+5213 5214 5988 0x16009d3b+5214 5215 5987 0x16009cda+5215 5216 5986 0x16009c79+5216 5217 5985 0x16009c19+5217 5218 5984 0x16009bb9+5218 5219 5983 0x16009b59+5219 5220 5982 0x16009af9+5220 5221 5981 0x16009a99+5221 5222 5980 0x16009a3a+5222 5223 5979 0x160099db+5223 5224 5978 0x1600997c+5224 5225 5977 0x1600991d+5225 5226 5976 0x160098bf+5226 5227 5975 0x16009861+5227 5228 5974 0x16009803+5228 5229 5973 0x160097a5+5229 5230 5972 0x16009748+5230 5231 5971 0x160096ea+5231 5232 5970 0x1600968d+5232 5233 5969 0x16009630+5233 5234 5968 0x160095d4+5234 5235 5967 0x16009577+5235 5236 5966 0x1600951b+5236 5237 5965 0x160094bf+5237 5238 5964 0x16009464+5238 5239 5963 0x16009408+5239 5240 5962 0x160093ac+5240 5241 5961 0x16009351+5241 5242 5960 0x160092f6+5242 5243 5959 0x1600929c+5243 5244 5958 0x16009241+5244 5245 5957 0x160091e7+5245 5246 5956 0x1600918d+5246 5247 5955 0x16009133+5247 5248 5954 0x160090da+5248 5249 5953 0x16009080+5249 5250 5952 0x16009027+5250 5251 5951 0x16008fce+5251 5252 5950 0x16008f76+5252 5253 5949 0x16008f1d+5253 5254 5948 0x16008ec5+5254 5255 5947 0x16008e6d+5255 5256 5946 0x16008e15+5256 5257 5945 0x16008dbd+5257 5258 5944 0x16008d66+5258 5259 5943 0x16008d0e+5259 5260 5942 0x16008cb7+5260 5261 5941 0x16008c60+5261 5262 5940 0x16008c0a+5262 5263 5939 0x16008bb3+5263 5264 5938 0x16008b5d+5264 5265 5937 0x16008b08+5265 5266 5936 0x16008ab2+5266 5267 5935 0x16008a5c+5267 5268 5934 0x16008a07+5268 5269 5933 0x160089b1+5269 5270 5932 0x1600895d+5270 5271 5931 0x16008908+5271 5272 5930 0x160088b3+5272 5273 5929 0x1600885f+5273 5274 5928 0x1600880a+5274 5275 5927 0x160087b7+5275 5276 5926 0x16008763+5276 5277 5925 0x1600870f+5277 5278 5924 0x160086bc+5278 5279 5923 0x16008669+5279 5280 5922 0x16008616+5280 5281 5921 0x160085c3+5281 5282 5920 0x16008571+5282 5283 5919 0x1600851e+5283 5284 5918 0x160084cc+5284 5285 5917 0x1600847a+5285 5286 5916 0x16008428+5286 5287 5915 0x160083d7+5287 5288 5914 0x16008386+5288 5289 5913 0x16008334+5289 5290 5912 0x160082e3+5290 5291 5911 0x16008292+5291 5292 5910 0x16008242+5292 5293 5909 0x160081f1+5293 5294 5908 0x160081a1+5294 5295 5907 0x16008152+5295 5296 5906 0x16008102+5296 5297 5905 0x160080b2+5297 5298 5904 0x16008063+5298 5299 5903 0x16008014+5299 5300 5902 0x157fc536+5300 5301 5901 0x157f7665+5301 5302 5900 0x157f2793+5302 5303 5899 0x157ed917+5303 5304 5898 0x157e8af1+5304 5305 5897 0x157e3cca+5305 5306 5896 0x157deefa+5306 5307 5895 0x157da12a+5307 5308 5894 0x157d53af+5308 5309 5893 0x157d068b+5309 5310 5892 0x157cb9bc+5310 5311 5891 0x157c6c98+5311 5312 5890 0x157c1fc9+5312 5313 5889 0x157bd350+5313 5314 5888 0x157b86d7+5314 5315 5887 0x157b3ab4+5315 5316 5886 0x157aeee7+5316 5317 5885 0x157aa31a+5317 5318 5884 0x157a574d+5318 5319 5883 0x157a0bd6+5319 5320 5882 0x1579c0b4+5320 5321 5881 0x15797593+5321 5322 5880 0x15792a72+5322 5323 5879 0x1578dffc+5323 5324 5878 0x15789530+5324 5325 5877 0x15784b11+5325 5326 5876 0x157800f1+5326 5327 5875 0x1577b727+5327 5328 5874 0x15776d5d+5328 5329 5873 0x15772393+5329 5330 5872 0x1576da1f+5330 5331 5871 0x15769101+5331 5332 5870 0x157647e3+5332 5333 5869 0x1575fec4+5333 5334 5868 0x1575b5fc+5334 5335 5867 0x15756d8a+5335 5336 5866 0x15752517+5336 5337 5865 0x1574dcfb+5337 5338 5864 0x157494de+5338 5339 5863 0x15744d17+5339 5340 5862 0x15740551+5340 5341 5861 0x1573be35+5341 5342 5860 0x157376c5+5342 5343 5859 0x15732faa+5343 5344 5858 0x1572e88f+5344 5345 5857 0x1572a1c9+5345 5346 5856 0x15725b04+5346 5347 5855 0x15721495+5347 5348 5854 0x1571ce26+5348 5349 5853 0x1571880c+5349 5350 5852 0x15714249+5350 5351 5851 0x1570fc85+5351 5352 5850 0x1570b6c2+5352 5353 5849 0x15707154+5353 5354 5848 0x15702be6+5354 5355 5847 0x156fe6ce+5355 5356 5846 0x156fa262+5356 5357 5845 0x156f5d4a+5357 5358 5844 0x156f18de+5358 5359 5843 0x156ed472+5359 5360 5842 0x156e9006+5360 5361 5841 0x156e4bf0+5361 5362 5840 0x156e082f+5362 5363 5839 0x156dc419+5363 5364 5838 0x156d80af+5364 5365 5837 0x156d3d44+5365 5366 5836 0x156cf9da+5366 5367 5835 0x156cb6c5+5367 5368 5834 0x156c73b0+5368 5369 5833 0x156c30f2+5369 5370 5832 0x156bee33+5370 5371 5831 0x156babca+5371 5372 5830 0x156b69b7+5372 5373 5829 0x156b27a4+5373 5374 5828 0x156ae591+5374 5375 5827 0x156aa3d3+5375 5376 5826 0x156a6216+5376 5377 5825 0x156a20af+5377 5378 5824 0x1569df48+5378 5379 5823 0x15699de0+5379 5380 5822 0x15695d25+5380 5381 5821 0x15691c13+5381 5382 5820 0x1568db57+5382 5383 5819 0x15689af2+5383 5384 5818 0x15685a36+5384 5385 5817 0x15681a26+5385 5386 5816 0x1567da16+5386 5387 5815 0x15679a5c+5387 5388 5814 0x15675aa2+5388 5389 5813 0x15671ae8+5389 5390 5812 0x1566db84+5390 5391 5811 0x15669c20+5391 5392 5810 0x15665cbc+5392 5393 5809 0x15661dae+5393 5394 5808 0x1565def5+5394 5395 5807 0x1565a03d+5395 5396 5806 0x15656184+5396 5397 5805 0x15652322+5397 5398 5804 0x1564e4bf+5398 5399 5803 0x1564a6b2+5399 5400 5802 0x156468a6+5400 5401 5801 0x15642aef+5401 5402 5800 0x1563ed38+5402 5403 5799 0x1563afd7+5403 5404 5798 0x15637276+5404 5405 5797 0x1563356b+5405 5406 5796 0x1562f80a+5406 5407 5795 0x1562bb55+5407 5408 5794 0x15627e4a+5408 5409 5793 0x156241ea+5409 5410 5792 0x15620535+5410 5411 5791 0x1561c8d5+5411 5412 5790 0x15618ccc+5412 5413 5789 0x1561506d+5413 5414 5788 0x156114b9+5414 5415 5787 0x1560d8af+5415 5416 5786 0x15609cfc+5416 5417 5785 0x1560619e+5417 5418 5784 0x15602696+5418 5419 5783 0x155feb38+5419 5420 5782 0x155fb030+5420 5421 5781 0x155f7528+5421 5422 5780 0x155f3a76+5422 5423 5779 0x155effc4+5423 5424 5778 0x155ec512+5424 5425 5777 0x155e8ab6+5425 5426 5776 0x155e505a+5426 5427 5775 0x155e1653+5427 5428 5774 0x155ddc4d+5428 5429 5773 0x155da29c+5429 5430 5772 0x155d68ec+5430 5431 5771 0x155d2f3c+5431 5432 5770 0x155cf5e1+5432 5433 5769 0x155cbcdc+5433 5434 5768 0x155c8382+5434 5435 5767 0x155c4a7d+5435 5436 5766 0x155c1178+5436 5437 5765 0x155bd8c9+5437 5438 5764 0x155ba01a+5438 5439 5763 0x155b67c1+5439 5440 5762 0x155b2f12+5440 5441 5761 0x155af70f+5441 5442 5760 0x155abeb6+5442 5443 5759 0x155a8709+5443 5444 5758 0x155a4f06+5444 5445 5757 0x155a1758+5445 5446 5756 0x1559dfab+5446 5447 5755 0x1559a854+5447 5448 5754 0x155970fc+5448 5449 5753 0x155939fb+5449 5450 5752 0x155902a3+5450 5451 5751 0x1558cbf7+5451 5452 5750 0x155894f6+5452 5453 5749 0x15585e4a+5453 5454 5748 0x1558279e+5454 5455 5747 0x1557f149+5455 5456 5746 0x1557baf3+5456 5457 5745 0x1557849d+5457 5458 5744 0x15574e9d+5458 5459 5743 0x1557189d+5459 5460 5742 0x1556e2f3+5460 5461 5741 0x1556acf3+5461 5462 5740 0x1556779e+5462 5463 5739 0x155641f4+5463 5464 5738 0x15560cf6+5464 5465 5737 0x1555d7a2+5465 5466 5736 0x1555a2f9+5466 5467 5735 0x15556e51+5467 5468 5734 0x155539a8+5468 5469 5733 0x15550555+5469 5470 5732 0x1554d103+5470 5471 5731 0x15549cb0+5471 5472 5730 0x155468b3+5472 5473 5729 0x155434b6+5473 5474 5728 0x155400e5+5474 5475 5727 0x1553cd3e+5475 5476 5726 0x15539997+5476 5477 5725 0x1553661b+5477 5478 5724 0x155332ca+5478 5479 5723 0x1552ffce+5479 5480 5722 0x1552cca8+5480 5481 5721 0x155299ad+5481 5482 5720 0x155266dd+5482 5483 5719 0x15523437+5483 5484 5718 0x15520192+5484 5485 5717 0x1551ceed+5485 5486 5716 0x15519c9d+5486 5487 5715 0x15516a4e+5487 5488 5714 0x15513829+5488 5489 5713 0x1551062f+5489 5490 5712 0x1550d436+5490 5491 5711 0x1550a267+5491 5492 5710 0x155070c3+5492 5493 5709 0x15503f20+5493 5494 5708 0x15500da7+5494 5495 5707 0x154fdcaf+5495 5496 5706 0x154fab61+5496 5497 5705 0x154f7a3e+5497 5498 5704 0x154f4946+5498 5499 5703 0x154f1879+5499 5500 5702 0x154ee7ac+5500 5501 5701 0x154eb709+5501 5502 5700 0x154e8667+5502 5503 5699 0x154e561b+5503 5504 5698 0x154e25cf+5504 5505 5697 0x154df582+5505 5506 5696 0x154dc58c+5506 5507 5695 0x154d9595+5507 5508 5694 0x154d65ca+5508 5509 5693 0x154d35fe+5509 5510 5692 0x154d0689+5510 5511 5691 0x154cd713+5511 5512 5690 0x154ca79d+5512 5513 5689 0x154c7853+5513 5514 5688 0x154c4933+5514 5515 5687 0x154c1a3e+5515 5516 5686 0x154beb49+5516 5517 5685 0x154bbc54+5517 5518 5684 0x154b8db5+5518 5519 5683 0x154b5f16+5519 5520 5682 0x154b30a2+5520 5521 5681 0x154b022e+5521 5522 5680 0x154ad3e5+5522 5523 5679 0x154aa5c7+5523 5524 5678 0x154a77a9+5524 5525 5677 0x154a49e0+5525 5526 5676 0x154a1c18+5526 5527 5675 0x1549ee50+5527 5528 5674 0x1549c0b2+5528 5529 5673 0x15499340+5529 5530 5672 0x154965cd+5530 5531 5671 0x15493886+5531 5532 5670 0x15490b69+5532 5533 5669 0x1548de4d+5533 5534 5668 0x1548b15b+5534 5535 5667 0x15488469+5535 5536 5666 0x154857a2+5536 5537 5665 0x15482b07+5537 5538 5664 0x1547fe6b+5538 5539 5663 0x1547d1fa+5539 5540 5662 0x1547a5b4+5540 5541 5661 0x154779c4+5541 5542 5660 0x15474da9+5542 5543 5659 0x1547218e+5543 5544 5658 0x1546f59e+5544 5545 5657 0x1546c9d9+5545 5546 5656 0x15469e3e+5546 5547 5655 0x154672a4+5547 5548 5654 0x1546470a+5548 5549 5653 0x15461bc6+5549 5550 5652 0x1545f081+5550 5551 5651 0x1545c53d+5551 5552 5650 0x15459a23+5552 5553 5649 0x15456f35+5553 5554 5648 0x15454471+5554 5555 5647 0x154519ae+5555 5556 5646 0x1544ef15+5556 5557 5645 0x1544c4a7+5557 5558 5644 0x15449a3a+5558 5559 5643 0x15446fcc+5559 5560 5642 0x15444589+5560 5561 5641 0x15441b71+5561 5562 5640 0x1543f15a+5562 5563 5639 0x1543c76d+5563 5564 5638 0x15439dab+5564 5565 5637 0x154373e9+5565 5566 5636 0x15434a52+5566 5567 5635 0x154320bb+5567 5568 5634 0x1542f74e+5568 5569 5633 0x1542cde2+5569 5570 5632 0x1542a4cc+5570 5571 5631 0x15427bb6+5571 5572 5630 0x154252cb+5572 5573 5629 0x154229df+5573 5574 5628 0x154200f4+5574 5575 5627 0x1541d833+5575 5576 5626 0x1541af9e+5576 5577 5625 0x15418709+5577 5578 5624 0x15415e9e+5578 5579 5623 0x15413634+5579 5580 5622 0x15410df4+5580 5581 5621 0x1540e5df+5581 5582 5620 0x1540bdcb+5582 5583 5619 0x154095e1+5583 5584 5618 0x15406df7+5584 5585 5617 0x15404638+5585 5586 5616 0x15401ea4+5586 5587 5615 0x153ff73c+5587 5588 5614 0x153fcfa8+5588 5589 5613 0x153fa86a+5589 5590 5612 0x153f8101+5590 5591 5611 0x153f59ee+5591 5592 5610 0x153f32db+5592 5593 5609 0x153f0bc8+5593 5594 5608 0x153ee4df+5594 5595 5607 0x153ebe22+5595 5596 5606 0x153e9765+5596 5597 5605 0x153e70d3+5597 5598 5604 0x153e4a40+5598 5599 5603 0x153e23d9+5599 5600 5602 0x153dfd72+5600 5601 5601 0x153dd735+5601 5602 5600 0x153db14f+5602 5603 5599 0x153d8b3d+5603 5604 5598 0x153d652c+5604 5605 5597 0x153d3f70+5605 5606 5596 0x153d198a+5606 5607 5595 0x153cf3f9+5607 5608 5594 0x153cce3d+5608 5609 5593 0x153ca8d8+5609 5610 5592 0x153c8372+5610 5611 5591 0x153c5e0c+5611 5612 5590 0x153c38d1+5612 5613 5589 0x153c13c1+5613 5614 5588 0x153beeb1+5614 5615 5587 0x153bc9a2+5615 5616 5586 0x153ba4e8+5616 5617 5585 0x153b802d+5617 5618 5584 0x153b5b9e+5618 5619 5583 0x153b36e4+5619 5620 5582 0x153b1280+5620 5621 5581 0x153aedf1+5621 5622 5580 0x153ac9b8+5622 5623 5579 0x153aa57f+5623 5624 5578 0x153a8145+5624 5625 5577 0x153a5d37+5625 5626 5576 0x153a3929+5626 5627 5575 0x153a1545+5627 5628 5574 0x1539f18d+5628 5629 5573 0x1539cdd5+5629 5630 5572 0x1539aa1c+5630 5631 5571 0x1539868f+5631 5632 5570 0x1539632c+5632 5633 5569 0x15393ff4+5633 5634 5568 0x15391cbd+5634 5635 5567 0x1538f985+5635 5636 5566 0x1538d64d+5636 5637 5565 0x1538b36c+5637 5638 5564 0x1538905f+5638 5639 5563 0x15386d7d+5639 5640 5562 0x15384ac6+5640 5641 5561 0x1538280f+5641 5642 5560 0x15380584+5642 5643 5559 0x1537e2f8+5643 5644 5558 0x1537c097+5644 5645 5557 0x15379e36+5645 5646 5556 0x15377bd5+5646 5647 5555 0x153759ca+5647 5648 5554 0x153737be+5648 5649 5553 0x153715b3+5649 5650 5552 0x1536f3d3+5650 5651 5551 0x1536d1f3+5651 5652 5550 0x1536b03e+5652 5653 5549 0x15368e88+5653 5654 5548 0x15366cd3+5654 5655 5547 0x15364b49+5655 5656 5546 0x153629e9+5656 5657 5545 0x1536088a+5657 5658 5544 0x1535e756+5658 5659 5543 0x1535c621+5659 5660 5542 0x1535a4ed+5660 5661 5541 0x153583e3+5661 5662 5540 0x153562da+5662 5663 5539 0x15354226+5663 5664 5538 0x15352172+5664 5665 5537 0x153500bf+5665 5666 5536 0x1534e00b+5666 5667 5535 0x1534bf82+5667 5668 5534 0x15349ef9+5668 5669 5533 0x15347e9c+5669 5670 5532 0x15345e3e+5670 5671 5531 0x15343e0b+5671 5672 5530 0x15341dd8+5672 5673 5529 0x1533fda5+5673 5674 5528 0x1533dd9d+5674 5675 5527 0x1533bdc0+5675 5676 5526 0x15339de3+5676 5677 5525 0x15337e06+5677 5678 5524 0x15335e54+5678 5679 5523 0x15333ef8+5679 5680 5522 0x15331f46+5680 5681 5521 0x1532ffea+5681 5682 5520 0x1532e062+5682 5683 5519 0x1532c131+5683 5684 5518 0x1532a1d5+5684 5685 5517 0x153282a4+5685 5686 5516 0x1532639d+5686 5687 5515 0x15324497+5687 5688 5514 0x15322591+5688 5689 5513 0x153206b5+5689 5690 5512 0x1531e7da+5690 5691 5511 0x1531c929+5691 5692 5510 0x1531aa79+5692 5693 5509 0x15318bf3+5693 5694 5508 0x15316d99+5694 5695 5507 0x15314f13+5695 5696 5506 0x153130b8+5696 5697 5505 0x1531125e+5697 5698 5504 0x1530f42e+5698 5699 5503 0x1530d5fe+5699 5700 5502 0x1530b7fa+5700 5701 5501 0x153099f5+5701 5702 5500 0x15307bf0+5702 5703 5499 0x15305e16+5703 5704 5498 0x15304067+5704 5705 5497 0x1530228e+5705 5706 5496 0x153004df+5706 5707 5495 0x152fe75b+5707 5708 5494 0x152fc9d7+5708 5709 5493 0x152fac7e+5709 5710 5492 0x152f8f25+5710 5711 5491 0x152f71cc+5711 5712 5490 0x152f549d+5712 5713 5489 0x152f376f+5713 5714 5488 0x152f1a6c+5714 5715 5487 0x152efd3e+5715 5716 5486 0x152ee066+5716 5717 5485 0x152ec363+5717 5718 5484 0x152ea68a+5718 5719 5483 0x152e89dd+5719 5720 5482 0x152e6d30+5720 5721 5481 0x152e5082+5721 5722 5480 0x152e3400+5722 5723 5479 0x152e177e+5723 5724 5478 0x152dfb26+5724 5725 5477 0x152ddecf+5725 5726 5476 0x152dc277+5726 5727 5475 0x152da64b+5727 5728 5474 0x152d8a1e+5728 5729 5473 0x152d6e1d+5729 5730 5472 0x152d521b+5730 5731 5471 0x152d361a+5731 5732 5470 0x152d1a18+5732 5733 5469 0x152cfe6c+5733 5734 5468 0x152ce296+5734 5735 5467 0x152cc6ea+5735 5736 5466 0x152cab3e+5736 5737 5465 0x152c8f92+5737 5738 5464 0x152c7412+5738 5739 5463 0x152c58bc+5739 5740 5462 0x152c3d66+5740 5741 5461 0x152c2210+5741 5742 5460 0x152c06e5+5742 5743 5459 0x152beb8f+5743 5744 5458 0x152bd08f+5744 5745 5457 0x152bb564+5745 5746 5456 0x152b9a64+5746 5747 5455 0x152b7f64+5747 5748 5454 0x152b648f+5748 5749 5453 0x152b49ba+5749 5750 5452 0x152b2ee5+5750 5751 5451 0x152b143b+5751 5752 5450 0x152af991+5752 5753 5449 0x152adf12+5753 5754 5448 0x152ac4be+5754 5755 5447 0x152aaa94+5755 5756 5446 0x152a9055+5756 5757 5445 0x152a7617+5757 5758 5444 0x152a5c03+5758 5759 5443 0x152a41ef+5759 5760 5442 0x152a27f0+5760 5761 5441 0x152a0df2+5761 5762 5440 0x1529f41f+5762 5763 5439 0x1529da4b+5763 5764 5438 0x1529c08d+5764 5765 5437 0x1529a6cf+5765 5766 5436 0x15298d27+5766 5767 5435 0x15297393+5767 5768 5434 0x15295a16+5768 5769 5433 0x15294098+5769 5770 5432 0x15292730+5770 5771 5431 0x15290df3+5771 5772 5430 0x1528f4b6+5772 5773 5429 0x1528db78+5773 5774 5428 0x1528c251+5774 5775 5427 0x1528a929+5775 5776 5426 0x15289017+5776 5777 5425 0x1528771a+5777 5778 5424 0x15285e33+5778 5779 5423 0x1528454b+5779 5780 5422 0x15282c79+5780 5781 5421 0x152813bd+5781 5782 5420 0x1527fb16+5782 5783 5419 0x1527e26f+5783 5784 5418 0x1527c9dd+5784 5785 5417 0x1527b14c+5785 5786 5416 0x152798fb+5786 5787 5415 0x15278094+5787 5788 5414 0x1527682e+5788 5789 5413 0x15274fdd+5789 5790 5412 0x152737a1+5790 5791 5411 0x15271f7b+5791 5792 5410 0x15270755+5792 5793 5409 0x1526ef44+5793 5794 5408 0x1526d749+5794 5795 5407 0x1526bf4e+5795 5796 5406 0x1526a768+5796 5797 5405 0x15268f98+5797 5798 5404 0x152677c7+5798 5799 5403 0x15266022+5799 5800 5402 0x15264867+5800 5801 5401 0x152630ed+5801 5802 5400 0x1526195d+5802 5803 5399 0x152601e2+5803 5804 5398 0x1525ea68+5804 5805 5397 0x1525d303+5805 5806 5396 0x1525bbb3+5806 5807 5395 0x1525a464+5807 5808 5394 0x15258d2a+5808 5809 5393 0x15257605+5809 5810 5392 0x15255ee1+5810 5811 5391 0x152547d2+5811 5812 5390 0x152530d8+5812 5813 5389 0x152519de+5813 5814 5388 0x152502fa+5814 5815 5387 0x1524ec2c+5815 5816 5386 0x1524d55d+5816 5817 5385 0x1524beb9+5817 5818 5384 0x1524a815+5818 5819 5383 0x15249172+5819 5820 5382 0x15247ae3+5820 5821 5381 0x15246455+5821 5822 5380 0x15244df2+5822 5823 5379 0x15243779+5823 5824 5378 0x1524212b+5824 5825 5377 0x15240add+5825 5826 5376 0x1523f4a4+5826 5827 5375 0x1523de6c+5827 5828 5374 0x1523c849+5828 5829 5373 0x1523b23c+5829 5830 5372 0x15239c2e+5830 5831 5371 0x15238636+5831 5832 5370 0x15237053+5832 5833 5369 0x15235a71+5833 5834 5368 0x152344a4+5834 5835 5367 0x15232eec+5835 5836 5366 0x15231934+5836 5837 5365 0x1523037d+5837 5838 5364 0x1522eddb+5838 5839 5363 0x1522d84e+5839 5840 5362 0x1522c2d7+5840 5841 5361 0x1522ad5f+5841 5842 5360 0x152297fe+5842 5843 5359 0x1522829c+5843 5844 5358 0x15226d4f+5844 5845 5357 0x15225819+5845 5846 5356 0x152242e2+5846 5847 5355 0x15222dd6+5847 5848 5354 0x152218b4+5848 5849 5353 0x152203a9+5849 5850 5352 0x1521eeb2+5850 5851 5351 0x1521d9bc+5851 5852 5350 0x1521c4db+5852 5853 5349 0x1521affa+5853 5854 5348 0x15219b2e+5854 5855 5347 0x15218678+5855 5856 5346 0x152171c2+5856 5857 5345 0x15215d21+5857 5858 5344 0x15214881+5858 5859 5343 0x152133f6+5859 5860 5342 0x15211f80+5860 5861 5341 0x15210b0a+5861 5862 5340 0x1520f6aa+5862 5863 5339 0x1520e25f+5863 5864 5338 0x1520ce15+5864 5865 5337 0x1520b9df+5865 5866 5336 0x1520a5aa+5866 5867 5335 0x1520918a+5867 5868 5334 0x15207d6b+5868 5869 5333 0x15206960+5869 5870 5332 0x15205556+5870 5871 5331 0x15204161+5871 5872 5330 0x15202d82+5872 5873 5329 0x152019a2+5873 5874 5328 0x152005d8+5874 5875 5327 0x151ff20e+5875 5876 5326 0x151fde5a+5876 5877 5325 0x151fcabb+5877 5878 5324 0x151fb731+5878 5879 5323 0x151fa392+5879 5880 5322 0x151f9009+5880 5881 5321 0x151f7c95+5881 5882 5320 0x151f6921+5882 5883 5319 0x151f55c2+5883 5884 5318 0x151f4279+5884 5885 5317 0x151f2f30+5885 5886 5316 0x151f1be7+5886 5887 5315 0x151f08b3+5887 5888 5314 0x151ef595+5888 5889 5313 0x151ee277+5889 5890 5312 0x151ecf6e+5890 5891 5311 0x151ebc7b+5891 5892 5310 0x151ea972+5892 5893 5309 0x151e96aa+5893 5894 5308 0x151e83cc+5894 5895 5307 0x151e70ee+5895 5896 5306 0x151e5e26+5896 5897 5305 0x151e4b73+5897 5898 5304 0x151e38c0+5898 5899 5303 0x151e2622+5899 5900 5302 0x151e1385+5900 5901 5301 0x151e00fd+5901 5902 5300 0x151dee75+5902 5903 5299 0x151ddc03+5903 5904 5298 0x151dc9a6+5904 5905 5297 0x151db749+5905 5906 5296 0x151da4ec+5906 5907 5295 0x151d92a4+5907 5908 5294 0x151d8072+5908 5909 5293 0x151d6e55+5909 5910 5292 0x151d5c23+5910 5911 5291 0x151d4a07+5911 5912 5290 0x151d37ff+5912 5913 5289 0x151d25f8+5913 5914 5288 0x151d1407+5914 5915 5287 0x151d0215+5915 5916 5286 0x151cf039+5916 5917 5285 0x151cde5d+5917 5918 5284 0x151ccc96+5918 5919 5283 0x151cbacf+5919 5920 5282 0x151ca91e+5920 5921 5281 0x151c976c+5921 5922 5280 0x151c85d1+5922 5923 5279 0x151c744a+5923 5924 5278 0x151c62c4+5924 5925 5277 0x151c5153+5925 5926 5276 0x151c3fe2+5926 5927 5275 0x151c2e71+5927 5928 5274 0x151c1d16+5928 5929 5273 0x151c0bba+5929 5930 5272 0x151bfa74+5930 5931 5271 0x151be944+5931 5932 5270 0x151bd813+5932 5933 5269 0x151bc6e3+5933 5934 5268 0x151bb5c8+5934 5935 5267 0x151ba4ad+5935 5936 5266 0x151b93a7+5936 5937 5265 0x151b82b7+5937 5938 5264 0x151b71b2+5938 5939 5263 0x151b60ec+5939 5940 5262 0x151b5012+5940 5941 5261 0x151b3f37+5941 5942 5260 0x151b2e72+5942 5943 5259 0x151b1dad+5943 5944 5258 0x151b0cfd+5944 5945 5257 0x151afc4e+5945 5946 5256 0x151aebb3+5946 5947 5255 0x151adb19+5947 5948 5254 0x151aca94+5948 5949 5253 0x151aba10+5949 5950 5252 0x151aa9a0+5950 5951 5251 0x151a9931+5951 5952 5250 0x151a88c2+5952 5953 5249 0x151a787d+5953 5954 5248 0x151a6823+5954 5955 5247 0x151a57f4+5955 5956 5246 0x151a47c6+5956 5957 5245 0x151a3781+5957 5958 5244 0x151a2768+5958 5959 5243 0x151a174e+5959 5960 5242 0x151a0735+5960 5961 5241 0x1519f731+5961 5962 5240 0x1519e72d+5962 5963 5239 0x1519d73e+5963 5964 5238 0x1519c750+5964 5965 5237 0x1519b777+5965 5966 5236 0x1519a79e+5966 5967 5235 0x151997da+5967 5968 5234 0x15198817+5968 5969 5233 0x15197853+5969 5970 5232 0x151968bb+5970 5971 5231 0x1519590c+5971 5972 5230 0x15194974+5972 5973 5229 0x151939db+5973 5974 5228 0x15192a58+5974 5975 5227 0x15191ad5+5975 5976 5226 0x15190b67+5976 5977 5225 0x1518fbf9+5977 5978 5224 0x1518eca1+5978 5979 5223 0x1518dd33+5979 5980 5222 0x1518cdf1+5980 5981 5221 0x1518beae+5981 5982 5220 0x1518af6b+5982 5983 5219 0x1518a03e+5983 5984 5218 0x15189110+5984 5985 5217 0x151881f9+5985 5986 5216 0x151872e1+5986 5987 5215 0x151863c9+5987 5988 5214 0x151854c6+5988 5989 5213 0x151845c4+5989 5990 5212 0x151836d7+5990 5991 5211 0x151827ea+5991 5992 5210 0x15181913+5992 5993 5209 0x15180a3b+5993 5994 5208 0x1517fb64+5994 5995 5207 0x1517eca2+5995 5996 5206 0x1517dde0+5996 5997 5205 0x1517cf33+5997 5998 5204 0x1517c087+5998 5999 5203 0x1517b1da+5999 6000 5202 0x1517a343+6000 6001 5201 0x151794c2+6001 6002 5200 0x15178640+6002 6003 5199 0x151777bf+6003 6004 5198 0x1517693d+6004 6005 5197 0x15175ad1+6005 6006 5196 0x15174c65+6006 6007 5195 0x15173e0e+6007 6008 5194 0x15172fb7+6008 6009 5193 0x15172176+6009 6010 5192 0x15171335+6010 6011 5191 0x151704f4+6011 6012 5190 0x1516f6c8+6012 6013 5189 0x1516e89c+6013 6014 5188 0x1516da71+6014 6015 5187 0x1516cc5a+6015 6016 5186 0x1516be5a+6016 6017 5185 0x1516b059+6017 6018 5184 0x1516a258+6018 6019 5183 0x15169457+6019 6020 5182 0x1516866c+6020 6021 5181 0x15167881+6021 6022 5180 0x15166aab+6022 6023 5179 0x15165cd5+6023 6024 5178 0x15164eff+6024 6025 5177 0x1516413f+6025 6026 5176 0x1516337e+6026 6027 5175 0x151625d3+6027 6028 5174 0x15161813+6028 6029 5173 0x15160a7d+6029 6030 5172 0x1515fcd3+6030 6031 5171 0x1515ef53+6031 6032 5170 0x1515e1bd+6032 6033 5169 0x1515d43d+6033 6034 5168 0x1515c6bd+6034 6035 5167 0x1515b953+6035 6036 5166 0x1515abd3+6036 6037 5165 0x15159e7e+6037 6038 5164 0x15159113+6038 6039 5163 0x151583be+6039 6040 5162 0x15157674+6040 6041 5161 0x1515693f+6041 6042 5160 0x15155c15+6042 6043 5159 0x15154ef5+6043 6044 5158 0x151541d6+6044 6045 5157 0x151534c1+6045 6046 5156 0x151527b7+6046 6047 5155 0x15151ac3+6047 6048 5154 0x15150dc3+6048 6049 5153 0x151500cf+6049 6050 5152 0x1514f3db+6050 6051 5151 0x1514e6fc+6051 6052 5150 0x1514da1d+6052 6053 5149 0x1514cd48+6053 6054 5148 0x1514c074+6054 6055 5147 0x1514b3aa+6055 6056 5146 0x1514a6ec+6056 6057 5145 0x15149a38+6057 6058 5144 0x15148d84+6058 6059 5143 0x151480e5+6059 6060 5142 0x1514743c+6060 6061 5141 0x151467a8+6061 6062 5140 0x15145b1f+6062 6063 5139 0x15144e95+6063 6064 5138 0x15144217+6064 6065 5137 0x15143599+6065 6066 5136 0x15142925+6066 6067 5135 0x15141cbc+6067 6068 5134 0x15141053+6068 6069 5133 0x151403f5+6069 6070 5132 0x1513f7a1+6070 6071 5131 0x1513eb59+6071 6072 5130 0x1513df10+6072 6073 5129 0x1513d2d2+6073 6074 5128 0x1513c694+6074 6075 5127 0x1513ba61+6075 6076 5126 0x1513ae38+6076 6077 5125 0x1513a21a+6077 6078 5124 0x15139607+6078 6079 5123 0x151389f4+6079 6080 5122 0x15137dec+6080 6081 5121 0x151371e3+6081 6082 5120 0x151365e6+6082 6083 5119 0x151359f3+6083 6084 5118 0x15134e00+6084 6085 5117 0x15134218+6085 6086 5116 0x1513363a+6086 6087 5115 0x15132a68+6087 6088 5114 0x15131e95+6088 6089 5113 0x151312c2+6089 6090 5112 0x15130705+6090 6091 5111 0x1512fb48+6091 6092 5110 0x1512ef8b+6092 6093 5109 0x1512e3ee+6093 6094 5108 0x1512d846+6094 6095 5107 0x1512cca9+6095 6096 5106 0x1512c10c+6096 6097 5105 0x1512b57a+6097 6098 5104 0x1512a9f2+6098 6099 5103 0x15129e6b+6099 6100 5102 0x151292ee+6100 6101 5101 0x1512877c+6101 6102 5100 0x15127c0a+6102 6103 5099 0x151270a2+6103 6104 5098 0x15126546+6104 6105 5097 0x151259e9+6105 6106 5096 0x15124e97+6106 6107 5095 0x15124345+6107 6108 5094 0x15123814+6108 6109 5093 0x15122ccc+6109 6110 5092 0x1512219b+6110 6111 5091 0x15121669+6111 6112 5090 0x15120b42+6112 6113 5089 0x1512001b+6113 6114 5088 0x1511f4ff+6114 6115 5087 0x1511e9ed+6115 6116 5086 0x1511dedc+6116 6117 5085 0x1511d3d5+6117 6118 5084 0x1511c8ce+6118 6119 5083 0x1511bddd+6119 6120 5082 0x1511b2e1+6120 6121 5081 0x1511a7fb+6121 6122 5080 0x15119d14+6122 6123 5079 0x15119238+6123 6124 5078 0x15118767+6124 6125 5077 0x15117c96+6125 6126 5076 0x151171c5+6126 6127 5075 0x15116709+6127 6128 5074 0x15115c43+6128 6129 5073 0x15115192+6129 6130 5072 0x151146e1+6130 6131 5071 0x15113c3b+6131 6132 5070 0x15113195+6132 6133 5069 0x151126f9+6133 6134 5068 0x15111c5e+6134 6135 5067 0x151111cd+6135 6136 5066 0x15110747+6136 6137 5065 0x1510fcc1+6137 6138 5064 0x1510f246+6138 6139 5063 0x1510e7e0+6139 6140 5062 0x1510dd70+6140 6141 5061 0x1510d2ff+6141 6142 5060 0x1510c8a4+6142 6143 5059 0x1510be49+6143 6144 5058 0x1510b3ee+6144 6145 5057 0x1510a99e+6145 6146 5056 0x15109f58+6146 6147 5055 0x15109513+6147 6148 5054 0x15108ad8+6148 6149 5053 0x151080a8+6149 6150 5052 0x15107678+6150 6151 5051 0x15106c48+6151 6152 5050 0x1510622d+6152 6153 5049 0x15105808+6153 6154 5048 0x15104e03+6154 6155 5047 0x151043f3+6155 6156 5046 0x151039e3+6156 6157 5045 0x15102fe8+6157 6158 5044 0x151025e3+6158 6159 5043 0x15101bf3+6159 6160 5042 0x15101204+6160 6161 5041 0x15100814+6161 6162 5040 0x150ffe2f+6162 6163 5039 0x150ff455+6163 6164 5038 0x150fea7b+6164 6165 5037 0x150fe0ab+6165 6166 5036 0x150fd6dc+6166 6167 5035 0x150fcd17+6167 6168 5034 0x150fc352+6168 6169 5033 0x150fb998+6169 6170 5032 0x150faff3+6170 6171 5031 0x150fa644+6171 6172 5030 0x150f9ca0+6172 6173 5029 0x150f92fb+6173 6174 5028 0x150f8961+6174 6175 5027 0x150f7fc7+6175 6176 5026 0x150f7638+6176 6177 5025 0x150f6cb4+6177 6178 5024 0x150f6330+6178 6179 5023 0x150f59ab+6179 6180 5022 0x150f503c+6180 6181 5021 0x150f46c3+6181 6182 5020 0x150f3d5e+6182 6183 5019 0x150f33f0+6183 6184 5018 0x150f2a96+6184 6185 5017 0x150f2147+6185 6186 5016 0x150f17ee+6186 6187 5015 0x150f0e9f+6187 6188 5014 0x150f0551+6188 6189 5013 0x150efc17+6189 6190 5012 0x150ef2d3+6190 6191 5011 0x150ee99a+6191 6192 5010 0x150ee06c+6192 6193 5009 0x150ed73d+6193 6194 5008 0x150ece19+6194 6195 5007 0x150ec4f6+6195 6196 5006 0x150ebbdc+6196 6197 5005 0x150eb2c3+6197 6198 5004 0x150ea9b5+6198 6199 5003 0x150ea0b2+6199 6200 5002 0x150e97ae+6200 6201 5001 0x150e8eb5+6201 6202 5000 0x150e85bc+6202 6203 4999 0x150e7cc3+6203 6204 4998 0x150e73d5+6204 6205 4997 0x150e6af2+6205 6206 4996 0x150e620f+6206 6207 4995 0x150e592b+6207 6208 4994 0x150e5053+6208 6209 4993 0x150e4785+6209 6210 4992 0x150e3eb7+6210 6211 4991 0x150e35f4+6211 6212 4990 0x150e2d30+6212 6213 4989 0x150e246d+6213 6214 4988 0x150e1bb5+6214 6215 4987 0x150e1307+6215 6216 4986 0x150e0a64+6216 6217 4985 0x150e01c1+6217 6218 4984 0x150df91e+6218 6219 4983 0x150df086+6219 6220 4982 0x150de7ee+6220 6221 4981 0x150ddf55+6221 6222 4980 0x150dd6c8+6222 6223 4979 0x150dce45+6223 6224 4978 0x150dc5c2+6224 6225 4977 0x150dbd4a+6225 6226 4976 0x150db4d2+6226 6227 4975 0x150dac65+6227 6228 4974 0x150da3f8+6228 6229 4973 0x150d9b8a+6229 6230 4972 0x150d9328+6230 6231 4971 0x150d8adb+6231 6232 4970 0x150d8283+6232 6233 4969 0x150d7a2b+6233 6234 4968 0x150d71de+6234 6235 4967 0x150d699b+6235 6236 4966 0x150d6159+6236 6237 4965 0x150d5917+6237 6238 4964 0x150d50df+6238 6239 4963 0x150d48a7+6239 6240 4962 0x150d407a+6240 6241 4961 0x150d3858+6241 6242 4960 0x150d302b+6242 6243 4959 0x150d2814+6243 6244 4958 0x150d1ff2+6244 6245 4957 0x150d17e5+6245 6246 4956 0x150d0fd8+6246 6247 4955 0x150d07cb+6247 6248 4954 0x150cffca+6248 6249 4953 0x150cf7c8+6249 6250 4952 0x150cefc6+6250 6251 4951 0x150ce7ce+6251 6252 4950 0x150cdfe2+6252 6253 4949 0x150cd7f5+6253 6254 4948 0x150cd009+6254 6255 4947 0x150cc827+6255 6256 4946 0x150cc045+6256 6257 4945 0x150cb86e+6257 6258 4944 0x150cb097+6258 6259 4943 0x150ca8c0+6259 6260 4942 0x150ca0fe+6260 6261 4941 0x150c9932+6261 6262 4940 0x150c917b+6262 6263 4939 0x150c89ba+6263 6264 4938 0x150c8203+6264 6265 4937 0x150c7a4c+6265 6266 4936 0x150c72a0+6266 6267 4935 0x150c6af4+6267 6268 4934 0x150c6352+6268 6269 4933 0x150c5bb1+6269 6270 4932 0x150c5410+6270 6271 4931 0x150c4c79+6271 6272 4930 0x150c44e2+6272 6273 4929 0x150c3d56+6273 6274 4928 0x150c35ca+6274 6275 4927 0x150c2e49+6275 6276 4926 0x150c26c8+6276 6277 4925 0x150c1f52+6277 6278 4924 0x150c17db+6278 6279 4923 0x150c1065+6279 6280 4922 0x150c08f9+6280 6281 4921 0x150c018d+6281 6282 4920 0x150bfa2c+6282 6283 4919 0x150bf2cb+6283 6284 4918 0x150beb6a+6284 6285 4917 0x150be414+6285 6286 4916 0x150bdcbe+6286 6287 4915 0x150bd572+6287 6288 4914 0x150bce27+6288 6289 4913 0x150bc6db+6289 6290 4912 0x150bbf9a+6290 6291 4911 0x150bb859+6291 6292 4910 0x150bb12e+6292 6293 4909 0x150ba9f8+6293 6294 4908 0x150ba2cd+6294 6295 4907 0x150b9b97+6295 6296 4906 0x150b9476+6296 6297 4905 0x150b8d4b+6297 6298 4904 0x150b862a+6298 6299 4903 0x150b7f14+6299 6300 4902 0x150b77fe+6300 6301 4901 0x150b70e9+6301 6302 4900 0x150b69dd+6302 6303 4899 0x150b62d2+6303 6304 4898 0x150b5bc7+6304 6305 4897 0x150b54c7+6305 6306 4896 0x150b4dc6+6306 6307 4895 0x150b46d1+6307 6308 4894 0x150b3fe6+6308 6309 4893 0x150b38f0+6309 6310 4892 0x150b3205+6310 6311 4891 0x150b2b1a+6311 6312 4890 0x150b243a+6312 6313 4889 0x150b1d5a+6313 6314 4888 0x150b167a+6314 6315 4887 0x150b0f9a+6315 6316 4886 0x150b08cf+6316 6317 4885 0x150b01f9+6317 6318 4884 0x150afb2f+6318 6319 4883 0x150af464+6319 6320 4882 0x150aed99+6320 6321 4881 0x150ae6d9+6321 6322 4880 0x150ae024+6322 6323 4879 0x150ad96f+6323 6324 4878 0x150ad2b9+6324 6325 4877 0x150acc04+6325 6326 4876 0x150ac55a+6326 6327 4875 0x150abeaf+6327 6328 4874 0x150ab80f+6328 6329 4873 0x150ab17a+6329 6330 4872 0x150aaae5+6330 6331 4871 0x150aa455+6331 6332 4870 0x150a9dcb+6332 6333 4869 0x150a9741+6333 6334 4868 0x150a90bc+6334 6335 4867 0x150a8a3c+6335 6336 4866 0x150a83c2+6336 6337 4865 0x150a7d48+6337 6338 4864 0x150a76d8+6338 6339 4863 0x150a7069+6339 6340 4862 0x150a69f9+6340 6341 4861 0x150a6394+6341 6342 4860 0x150a5d2a+6342 6343 4859 0x150a56cb+6343 6344 4858 0x150a506b+6344 6345 4857 0x150a4a11+6345 6346 4856 0x150a43bd+6346 6347 4855 0x150a3d68+6347 6348 4854 0x150a371e+6348 6349 4853 0x150a30cf+6349 6350 4852 0x150a2a8a+6350 6351 4851 0x150a2446+6351 6352 4850 0x150a1e06+6352 6353 4849 0x150a17cd+6353 6354 4848 0x150a1198+6354 6355 4847 0x150a0b64+6355 6356 4846 0x150a0535+6356 6357 4845 0x1509ff0b+6357 6358 4844 0x1509f8e1+6358 6359 4843 0x1509f2b7+6359 6360 4842 0x1509ec98+6360 6361 4841 0x1509e679+6361 6362 4840 0x1509e060+6362 6363 4839 0x1509da4b+6363 6364 4838 0x1509d437+6364 6365 4837 0x1509ce28+6365 6366 4836 0x1509c819+6366 6367 4835 0x1509c215+6367 6368 4834 0x1509bc11+6368 6369 4833 0x1509b618+6369 6370 4832 0x1509b019+6370 6371 4831 0x1509aa1f+6371 6372 4830 0x1509a42b+6372 6373 4829 0x15099e37+6373 6374 4828 0x15099849+6374 6375 4827 0x1509925f+6375 6376 4826 0x15098c76+6376 6377 4825 0x15098692+6377 6378 4824 0x150980b3+6378 6379 4823 0x15097ad5+6379 6380 4822 0x150974fb+6380 6381 4821 0x15096f28+6381 6382 4820 0x15096954+6382 6383 4819 0x15096385+6383 6384 4818 0x15095dc1+6384 6385 4817 0x150957f8+6385 6386 4816 0x15095234+6386 6387 4815 0x15094c76+6387 6388 4814 0x150946b8+6388 6389 4813 0x150940ff+6389 6390 4812 0x15093b4b+6390 6391 4811 0x15093597+6391 6392 4810 0x15092fe9+6392 6393 4809 0x15092a40+6393 6394 4808 0x15092497+6394 6395 4807 0x15091ef3+6395 6396 4806 0x15091950+6396 6397 4805 0x150913b2+6397 6398 4804 0x15090e19+6398 6399 4803 0x15090880+6399 6400 4802 0x150902f7+6400 6401 4801 0x1508fd64+6401 6402 4800 0x1508f7db+6402 6403 4799 0x1508f252+6403 6404 4798 0x1508ecca+6404 6405 4797 0x1508e746+6405 6406 4796 0x1508e1c8+6406 6407 4795 0x1508dc50+6407 6408 4794 0x1508d6d7+6408 6409 4793 0x1508d15e+6409 6410 4792 0x1508cbf0+6410 6411 4791 0x1508c682+6411 6412 4790 0x1508c114+6412 6413 4789 0x1508bbb1+6413 6414 4788 0x1508b64e+6414 6415 4787 0x1508b0f0+6415 6416 4786 0x1508ab92+6416 6417 4785 0x1508a63a+6417 6418 4784 0x1508a0e2+6418 6419 4783 0x15089b8f+6419 6420 4782 0x15089641+6420 6421 4781 0x150890f3+6421 6422 4780 0x15088bab+6422 6423 4779 0x15088662+6423 6424 4778 0x1508811f+6424 6425 4777 0x15087be2+6425 6426 4776 0x150876a4+6426 6427 4775 0x15087167+6427 6428 4774 0x15086c34+6428 6429 4773 0x15086701+6429 6430 4772 0x150861d9+6430 6431 4771 0x15085cab+6431 6432 4770 0x15085783+6432 6433 4769 0x1508525b+6433 6434 4768 0x15084d38+6434 6435 4767 0x15084815+6435 6436 4766 0x150842fd+6436 6437 4765 0x15083de0+6437 6438 4764 0x150838cd+6438 6439 4763 0x150833ba+6439 6440 4762 0x15082ea8+6440 6441 4761 0x1508299a+6441 6442 4760 0x15082493+6442 6443 4759 0x15081f8b+6443 6444 4758 0x15081a88+6444 6445 4757 0x1508158b+6445 6446 4756 0x15081093+6446 6447 4755 0x15080b96+6447 6448 4754 0x1508069e+6448 6449 4753 0x150801ab+6449 6450 4752 0x1507fcb9+6450 6451 4751 0x1507f7cc+6451 6452 4750 0x1507f2e4+6452 6453 4749 0x1507edfc+6453 6454 4748 0x1507e915+6454 6455 4747 0x1507e438+6455 6456 4746 0x1507df55+6456 6457 4745 0x1507da7d+6457 6458 4744 0x1507d5a0+6458 6459 4743 0x1507d0ce+6459 6460 4742 0x1507cbfc+6460 6461 4741 0x1507c72f+6461 6462 4740 0x1507c267+6462 6463 4739 0x1507bd9a+6463 6464 4738 0x1507b8d3+6464 6465 4737 0x1507b411+6465 6466 4736 0x1507af4f+6466 6467 4735 0x1507aa92+6467 6468 4734 0x1507a5da+6468 6469 4733 0x1507a123+6469 6470 4732 0x15079c6b+6470 6471 4731 0x150797b9+6471 6472 4730 0x1507930d+6472 6473 4729 0x15078e60+6473 6474 4728 0x150789b9+6474 6475 4727 0x15078511+6475 6476 4726 0x15078075+6476 6477 4725 0x15077bd3+6477 6478 4724 0x15077736+6478 6479 4723 0x15077299+6479 6480 4722 0x15076e02+6480 6481 4721 0x15076970+6481 6482 4720 0x150764de+6482 6483 4719 0x1507604c+6483 6484 4718 0x15075bc0+6484 6485 4717 0x15075739+6485 6486 4716 0x150752b2+6486 6487 4715 0x15074e30+6487 6488 4714 0x150749ae+6488 6489 4713 0x1507452c+6489 6490 4712 0x150740b5+6490 6491 4711 0x15073c39+6491 6492 4710 0x150737cc+6492 6493 4709 0x15073355+6493 6494 4708 0x15072ee9+6494 6495 4707 0x15072a77+6495 6496 4706 0x15072610+6496 6497 4705 0x150721a9+6497 6498 4704 0x15071d42+6498 6499 4703 0x150718e1+6499 6500 4702 0x1507147f+6500 6501 4701 0x15071023+6501 6502 4700 0x15070bc7+6502 6503 4699 0x15070770+6503 6504 4698 0x1507031f+6504 6505 4697 0x1506fec8+6505 6506 4696 0x1506fa7c+6506 6507 4695 0x1506f635+6507 6508 4694 0x1506f1e9+6508 6509 4693 0x1506eda2+6509 6510 4692 0x1506e95b+6510 6511 4691 0x1506e51a+6511 6512 4690 0x1506e0d8+6512 6513 4689 0x1506dc9c+6513 6514 4688 0x1506d866+6514 6515 4687 0x1506d42a+6515 6516 4686 0x1506cff8+6516 6517 4685 0x1506cbc7+6517 6518 4684 0x1506c796+6518 6519 4683 0x1506c36a+6519 6520 4682 0x1506bf3e+6520 6521 4681 0x1506bb17+6521 6522 4680 0x1506b6f6+6522 6523 4679 0x1506b2d0+6523 6524 4678 0x1506aeb4+6524 6525 4677 0x1506aa93+6525 6526 4676 0x1506a677+6526 6527 4675 0x1506a260+6527 6528 4674 0x15069e4a+6528 6529 4673 0x15069a39+6529 6530 4672 0x15069628+6530 6531 4671 0x15069217+6531 6532 4670 0x15068e0b+6532 6533 4669 0x15068a05+6533 6534 4668 0x150685f9+6534 6535 4667 0x150681f8+6535 6536 4666 0x15067df7+6536 6537 4665 0x150679f6+6537 6538 4664 0x150675fa+6538 6539 4663 0x15067204+6539 6540 4662 0x15066e08+6540 6541 4661 0x15066a12+6541 6542 4660 0x15066621+6542 6543 4659 0x15066230+6543 6544 4658 0x15065e3f+6544 6545 4657 0x15065a54+6545 6546 4656 0x1506566e+6546 6547 4655 0x15065287+6547 6548 4654 0x15064ea1+6548 6549 4653 0x15064abb+6549 6550 4652 0x150646e0+6550 6551 4651 0x150642ff+6551 6552 4650 0x15063f24+6552 6553 4649 0x15063b53+6553 6554 4648 0x15063777+6554 6555 4647 0x150633a7+6555 6556 4646 0x15062fd6+6556 6557 4645 0x15062c05+6557 6558 4644 0x1506283a+6558 6559 4643 0x1506246f+6559 6560 4642 0x150620a3+6560 6561 4641 0x15061cdd+6561 6562 4640 0x15061917+6562 6563 4639 0x15061557+6563 6564 4638 0x15061196+6564 6565 4637 0x15060ddb+6565 6566 4636 0x15060a20+6566 6567 4635 0x1506066a+6567 6568 4634 0x150602b4+6568 6569 4633 0x1505ff04+6569 6570 4632 0x1505fb53+6570 6571 4631 0x1505f7a3+6571 6572 4630 0x1505f3f2+6572 6573 4629 0x1505f047+6573 6574 4628 0x1505eca1+6574 6575 4627 0x1505e8fb+6575 6576 4626 0x1505e556+6576 6577 4625 0x1505e1b5+6577 6578 4624 0x1505de15+6578 6579 4623 0x1505da74+6579 6580 4622 0x1505d6d9+6580 6581 4621 0x1505d33e+6581 6582 4620 0x1505cfa9+6582 6583 4619 0x1505cc13+6583 6584 4618 0x1505c883+6584 6585 4617 0x1505c4f2+6585 6586 4616 0x1505c162+6586 6587 4615 0x1505bdd7+6587 6588 4614 0x1505ba4c+6588 6589 4613 0x1505b6c7+6589 6590 4612 0x1505b33c+6590 6591 4611 0x1505afbc+6591 6592 4610 0x1505ac36+6592 6593 4609 0x1505a8b6+6593 6594 4608 0x1505a53b+6594 6595 4607 0x1505a1c0+6595 6596 4606 0x15059e45+6596 6597 4605 0x15059acb+6597 6598 4604 0x15059755+6598 6599 4603 0x150593e5+6599 6600 4602 0x15059075+6600 6601 4601 0x15058d05+6601 6602 4600 0x1505899a+6602 6603 4599 0x1505862f+6603 6604 4598 0x150582c5+6604 6605 4597 0x15057f5a+6605 6606 4596 0x15057bf4+6606 6607 4595 0x15057894+6607 6608 4594 0x1505752f+6608 6609 4593 0x150571cf+6609 6610 4592 0x15056e74+6610 6611 4591 0x15056b1a+6611 6612 4590 0x150567bf+6612 6613 4589 0x15056465+6613 6614 4588 0x15056115+6614 6615 4587 0x15055dc2+6615 6616 4586 0x15055a75+6616 6617 4585 0x1505572a+6617 6618 4584 0x150553e0+6618 6619 4583 0x1505509b+6619 6620 4582 0x15054d55+6620 6621 4581 0x15054a10+6621 6622 4580 0x150546d0+6622 6623 4579 0x15054391+6623 6624 4578 0x15054054+6624 6625 4577 0x15053d19+6625 6626 4576 0x150539df+6626 6627 4575 0x150536a7+6627 6628 4574 0x15053372+6628 6629 4573 0x1505303d+6629 6630 4572 0x15052d10+6630 6631 4571 0x150529e0+6631 6632 4570 0x150526b0+6632 6633 4569 0x15052386+6633 6634 4568 0x1505205c+6634 6635 4567 0x15051d34+6635 6636 4566 0x15051a0c+6636 6637 4565 0x150516e7+6637 6638 4564 0x150513c5+6638 6639 4563 0x150510a6+6639 6640 4562 0x15050d86+6640 6641 4561 0x15050a69+6641 6642 4560 0x1505074f+6642 6643 4559 0x15050435+6643 6644 4558 0x1505011d+6644 6645 4557 0x1504fe0b+6645 6646 4556 0x1504faf6+6646 6647 4555 0x1504f7e6+6647 6648 4554 0x1504f4d4+6648 6649 4553 0x1504f1c7+6649 6650 4552 0x1504eebb+6650 6651 4551 0x1504ebb1+6651 6652 4550 0x1504e8a9+6652 6653 4549 0x1504e5a2+6653 6654 4548 0x1504e29d+6654 6655 4547 0x1504df98+6655 6656 4546 0x1504dc99+6656 6657 4545 0x1504d999+6657 6658 4544 0x1504d69a+6658 6659 4543 0x1504d3a0+6659 6660 4542 0x1504d0a8+6660 6661 4541 0x1504cdb1+6661 6662 4540 0x1504caba+6662 6663 4539 0x1504c7c5+6663 6664 4538 0x1504c4d3+6664 6665 4537 0x1504c1e1+6665 6666 4536 0x1504bef2+6666 6667 4535 0x1504bc05+6667 6668 4534 0x1504b919+6668 6669 4533 0x1504b62f+6669 6670 4532 0x1504b347+6670 6671 4531 0x1504b060+6671 6672 4530 0x1504ad7c+6672 6673 4529 0x1504aa9a+6673 6674 4528 0x1504a7b8+6674 6675 4527 0x1504a4d9+6675 6676 4526 0x1504a1ff+6676 6677 4525 0x15049f22+6677 6678 4524 0x15049c48+6678 6679 4523 0x1504996f+6679 6680 4522 0x1504969a+6680 6681 4521 0x150493c3+6681 6682 4520 0x150490f1+6682 6683 4519 0x15048e1f+6683 6684 4518 0x15048b50+6684 6685 4517 0x15048881+6685 6686 4516 0x150485b5+6686 6687 4515 0x150482eb+6687 6688 4514 0x15048021+6688 6689 4513 0x15047d5a+6689 6690 4512 0x15047a96+6690 6691 4511 0x150477d7+6691 6692 4510 0x15047512+6692 6693 4509 0x15047253+6693 6694 4508 0x15046f94+6694 6695 4507 0x15046cd8+6695 6696 4506 0x15046a1c+6696 6697 4505 0x15046762+6697 6698 4504 0x150464ab+6698 6699 4503 0x150461f4+6699 6700 4502 0x15045f40+6700 6701 4501 0x15045c8e+6701 6702 4500 0x150459dd+6702 6703 4499 0x1504572e+6703 6704 4498 0x1504547f+6704 6705 4497 0x150451d3+6705 6706 4496 0x15044f2c+6706 6707 4495 0x15044c82+6707 6708 4494 0x150449db+6708 6709 4493 0x15044735+6709 6710 4492 0x15044490+6710 6711 4491 0x150441ef+6711 6712 4490 0x15043f4d+6712 6713 4489 0x15043caf+6713 6714 4488 0x15043a10+6714 6715 4487 0x15043776+6715 6716 4486 0x150434da+6716 6717 4485 0x15043244+6717 6718 4484 0x15042faa+6718 6719 4483 0x15042d16+6719 6720 4482 0x15042a82+6720 6721 4481 0x150427f1+6721 6722 4480 0x15042562+6722 6723 4479 0x150422d3+6723 6724 4478 0x15042044+6724 6725 4477 0x15041db8+6725 6726 4476 0x15041b2f+6726 6727 4475 0x150418a6+6727 6728 4474 0x1504161f+6728 6729 4473 0x15041398+6729 6730 4472 0x15041114+6730 6731 4471 0x15040e93+6731 6732 4470 0x15040c12+6732 6733 4469 0x15040993+6733 6734 4468 0x15040715+6734 6735 4467 0x15040499+6735 6736 4466 0x1504021d+6736 6737 4465 0x1503ffa6+6737 6738 4464 0x1503fd30+6738 6739 4463 0x1503fab9+6739 6740 4462 0x1503f845+6740 6741 4461 0x1503f5d1+6741 6742 4460 0x1503f360+6742 6743 4459 0x1503f0ef+6743 6744 4458 0x1503ee80+6744 6745 4457 0x1503ec15+6745 6746 4456 0x1503e9a9+6746 6747 4455 0x1503e740+6747 6748 4454 0x1503e4d7+6748 6749 4453 0x1503e270+6749 6750 4452 0x1503e00a+6750 6751 4451 0x1503dda6+6751 6752 4450 0x1503db45+6752 6753 4449 0x1503d8e6+6753 6754 4448 0x1503d685+6754 6755 4447 0x1503d427+6755 6756 4446 0x1503d1cb+6756 6757 4445 0x1503cf6f+6757 6758 4444 0x1503cd16+6758 6759 4443 0x1503cabd+6759 6760 4442 0x1503c867+6760 6761 4441 0x1503c613+6761 6762 4440 0x1503c3c0+6762 6763 4439 0x1503c16c+6763 6764 4438 0x1503bf1b+6764 6765 4437 0x1503bccd+6765 6766 4436 0x1503ba7e+6766 6767 4435 0x1503b833+6767 6768 4434 0x1503b5ea+6768 6769 4433 0x1503b39e+6769 6770 4432 0x1503b158+6770 6771 4431 0x1503af0f+6771 6772 4430 0x1503accb+6772 6773 4429 0x1503aa88+6773 6774 4428 0x1503a844+6774 6775 4427 0x1503a603+6775 6776 4426 0x1503a3c2+6776 6777 4425 0x1503a184+6777 6778 4424 0x15039f49+6778 6779 4423 0x15039d0d+6779 6780 4422 0x15039ad2+6780 6781 4421 0x15039899+6781 6782 4420 0x15039663+6782 6783 4419 0x1503942f+6783 6784 4418 0x150391f9+6784 6785 4417 0x15038fc5+6785 6786 4416 0x15038d95+6786 6787 4415 0x15038b64+6787 6788 4414 0x15038933+6788 6789 4413 0x15038708+6789 6790 4412 0x150384da+6790 6791 4411 0x150382ae+6791 6792 4410 0x15038085+6792 6793 4409 0x15037e5d+6793 6794 4408 0x15037c37+6794 6795 4407 0x15037a10+6795 6796 4406 0x150377ea+6796 6797 4405 0x150375ca+6797 6798 4404 0x150373a6+6798 6799 4403 0x15037188+6799 6800 4402 0x15036f6a+6800 6801 4401 0x15036d4c+6801 6802 4400 0x15036b2e+6802 6803 4399 0x15036913+6803 6804 4398 0x150366fa+6804 6805 4397 0x150364e2+6805 6806 4396 0x150362cc+6806 6807 4395 0x150360b6+6807 6808 4394 0x15035ea0+6808 6809 4393 0x15035c8d+6809 6810 4392 0x15035a79+6810 6811 4391 0x15035869+6811 6812 4390 0x1503565b+6812 6813 4389 0x1503544d+6813 6814 4388 0x15035242+6814 6815 4387 0x15035036+6815 6816 4386 0x15034e2b+6816 6817 4385 0x15034c23+6817 6818 4384 0x15034a1a+6818 6819 4383 0x15034814+6819 6820 4382 0x1503460e+6820 6821 4381 0x1503440b+6821 6822 4380 0x15034208+6822 6823 4379 0x15034005+6823 6824 4378 0x15033e04+6824 6825 4377 0x15033c06+6825 6826 4376 0x15033a09+6826 6827 4375 0x1503380b+6827 6828 4374 0x15033610+6828 6829 4373 0x15033417+6829 6830 4372 0x1503321f+6830 6831 4371 0x15033026+6831 6832 4370 0x15032e31+6832 6833 4369 0x15032c3b+6833 6834 4368 0x15032a48+6834 6835 4367 0x15032855+6835 6836 4366 0x15032662+6836 6837 4365 0x15032471+6837 6838 4364 0x15032281+6838 6839 4363 0x15032093+6839 6840 4362 0x15031ea5+6840 6841 4361 0x15031cba+6841 6842 4360 0x15031acf+6842 6843 4359 0x150318e7+6843 6844 4358 0x150316ff+6844 6845 4357 0x15031519+6845 6846 4356 0x15031333+6846 6847 4355 0x1503114e+6847 6848 4354 0x15030f6b+6848 6849 4353 0x15030d88+6849 6850 4352 0x15030ba7+6850 6851 4351 0x150309c7+6851 6852 4350 0x150307e7+6852 6853 4349 0x15030609+6853 6854 4348 0x1503042c+6854 6855 4347 0x15030251+6855 6856 4346 0x15030076+6856 6857 4345 0x1502fe9d+6857 6858 4344 0x1502fcc5+6858 6859 4343 0x1502faed+6859 6860 4342 0x1502f91a+6860 6861 4341 0x1502f745+6861 6862 4340 0x1502f56f+6862 6863 4339 0x1502f39f+6863 6864 4338 0x1502f1cc+6864 6865 4337 0x1502effc+6865 6866 4336 0x1502ee2b+6866 6867 4335 0x1502ec5e+6867 6868 4334 0x1502ea90+6868 6869 4333 0x1502e8c3+6869 6870 4332 0x1502e6f8+6870 6871 4331 0x1502e52d+6871 6872 4330 0x1502e365+6872 6873 4329 0x1502e19d+6873 6874 4328 0x1502dfd7+6874 6875 4327 0x1502de12+6875 6876 4326 0x1502dc4f+6876 6877 4325 0x1502da8a+6877 6878 4324 0x1502d8c7+6878 6879 4323 0x1502d707+6879 6880 4322 0x1502d547+6880 6881 4321 0x1502d387+6881 6882 4320 0x1502d1c7+6882 6883 4319 0x1502d009+6883 6884 4318 0x1502ce4e+6884 6885 4317 0x1502cc91+6885 6886 4316 0x1502cad9+6886 6887 4315 0x1502c91e+6887 6888 4314 0x1502c766+6888 6889 4313 0x1502c5ae+6889 6890 4312 0x1502c3f9+6890 6891 4311 0x1502c246+6891 6892 4310 0x1502c091+6892 6893 4309 0x1502bede+6893 6894 4308 0x1502bd2b+6894 6895 4307 0x1502bb79+6895 6896 4306 0x1502b9c9+6896 6897 4305 0x1502b81b+6897 6898 4304 0x1502b66b+6898 6899 4303 0x1502b4be+6899 6900 4302 0x1502b313+6900 6901 4301 0x1502b166+6901 6902 4300 0x1502afbb+6902 6903 4299 0x1502ae15+6903 6904 4298 0x1502ac6e+6904 6905 4297 0x1502aac9+6905 6906 4296 0x1502a928+6906 6907 4295 0x1502a784+6907 6908 4294 0x1502a5e3+6908 6909 4293 0x1502a441+6909 6910 4292 0x1502a2a1+6910 6911 4291 0x1502a102+6911 6912 4290 0x15029f63+6912 6913 4289 0x15029dc6+6913 6914 4288 0x15029c2a+6914 6915 4287 0x15029a8e+6915 6916 4286 0x150298f5+6916 6917 4285 0x1502975a+6917 6918 4284 0x150295c2+6918 6919 4283 0x1502942b+6919 6920 4282 0x15029294+6920 6921 4281 0x15029100+6921 6922 4280 0x15028f6c+6922 6923 4279 0x15028dd9+6923 6924 4278 0x15028c46+6924 6925 4277 0x15028ab4+6925 6926 4276 0x15028922+6926 6927 4275 0x15028793+6927 6928 4274 0x15028604+6928 6929 4273 0x15028476+6929 6930 4272 0x150282e9+6930 6931 4271 0x1502815d+6931 6932 4270 0x15027fd1+6932 6933 4269 0x15027e47+6933 6934 4268 0x15027cbe+6934 6935 4267 0x15027b36+6935 6936 4266 0x150279ae+6936 6937 4265 0x15027829+6937 6938 4264 0x150276a3+6938 6939 4263 0x1502751f+6939 6940 4262 0x1502739a+6940 6941 4261 0x15027217+6941 6942 4260 0x15027095+6942 6943 4259 0x15026f14+6943 6944 4258 0x15026d94+6944 6945 4257 0x15026c15+6945 6946 4256 0x15026a96+6946 6947 4255 0x15026919+6947 6948 4254 0x1502679c+6948 6949 4253 0x15026621+6949 6950 4252 0x150264a6+6950 6951 4251 0x1502632c+6951 6952 4250 0x150261b4+6952 6953 4249 0x1502603d+6953 6954 4248 0x15025ec5+6954 6955 4247 0x15025d4f+6955 6956 4246 0x15025bda+6956 6957 4245 0x15025a65+6957 6958 4244 0x150258f1+6958 6959 4243 0x1502577f+6959 6960 4242 0x1502560c+6960 6961 4241 0x1502549b+6961 6962 4240 0x1502532c+6962 6963 4239 0x150251bc+6963 6964 4238 0x1502504e+6964 6965 4237 0x15024ee1+6965 6966 4236 0x15024d74+6966 6967 4235 0x15024c0a+6967 6968 4234 0x15024a9e+6968 6969 4233 0x15024935+6969 6970 4232 0x150247cb+6970 6971 4231 0x15024664+6971 6972 4230 0x150244fc+6972 6973 4229 0x15024396+6973 6974 4228 0x15024230+6974 6975 4227 0x150240cb+6975 6976 4226 0x15023f67+6976 6977 4225 0x15023e04+6977 6978 4224 0x15023ca2+6978 6979 4223 0x15023b3f+6979 6980 4222 0x150239e0+6980 6981 4221 0x15023880+6981 6982 4220 0x15023721+6982 6983 4219 0x150235c4+6983 6984 4218 0x15023467+6984 6985 4217 0x1502330a+6985 6986 4216 0x150231af+6986 6987 4215 0x15023055+6987 6988 4214 0x15022efb+6988 6989 4213 0x15022da2+6989 6990 4212 0x15022c49+6990 6991 4211 0x15022af2+6991 6992 4210 0x1502299c+6992 6993 4209 0x15022845+6993 6994 4208 0x150226f1+6994 6995 4207 0x1502259d+6995 6996 4206 0x1502244a+6996 6997 4205 0x150222f8+6997 6998 4204 0x150221a7+6998 6999 4203 0x15022056+6999 7000 4202 0x15021f05+7000 7001 4201 0x15021db7+7001 7002 4200 0x15021c68+7002 7003 4199 0x15021b1b+7003 7004 4198 0x150219cf+7004 7005 4197 0x15021882+7005 7006 4196 0x15021736+7006 7007 4195 0x150215ec+7007 7008 4194 0x150214a4+7008 7009 4193 0x1502135b+7009 7010 4192 0x15021212+7010 7011 4191 0x150210cb+7011 7012 4190 0x15020f85+7012 7013 4189 0x15020e40+7013 7014 4188 0x15020cfc+7014 7015 4187 0x15020bb8+7015 7016 4186 0x15020a75+7016 7017 4185 0x15020933+7017 7018 4184 0x150207f1+7018 7019 4183 0x150206b0+7019 7020 4182 0x15020570+7020 7021 4181 0x15020430+7021 7022 4180 0x150202f2+7022 7023 4179 0x150201b5+7023 7024 4178 0x15020077+7024 7025 4177 0x1501ff3b+7025 7026 4176 0x1501fe00+7026 7027 4175 0x1501fcc5+7027 7028 4174 0x1501fb8b+7028 7029 4173 0x1501fa54+7029 7030 4172 0x1501f91b+7030 7031 4171 0x1501f7e2+7031 7032 4170 0x1501f6ac+7032 7033 4169 0x1501f576+7033 7034 4168 0x1501f441+7034 7035 4167 0x1501f30c+7035 7036 4166 0x1501f1d7+7036 7037 4165 0x1501f0a6+7037 7038 4164 0x1501ef72+7038 7039 4163 0x1501ee40+7039 7040 4162 0x1501ed10+7040 7041 4161 0x1501ebdf+7041 7042 4160 0x1501eab0+7042 7043 4159 0x1501e982+7043 7044 4158 0x1501e856+7044 7045 4157 0x1501e728+7045 7046 4156 0x1501e5fb+7046 7047 4155 0x1501e4d0+7047 7048 4154 0x1501e3a5+7048 7049 4153 0x1501e27b+7049 7050 4152 0x1501e151+7050 7051 4151 0x1501e029+7051 7052 4150 0x1501df00+7052 7053 4149 0x1501ddd9+7053 7054 4148 0x1501dcb2+7054 7055 4147 0x1501db8c+7055 7056 4146 0x1501da66+7056 7057 4145 0x1501d942+7057 7058 4144 0x1501d81f+7058 7059 4143 0x1501d6fd+7059 7060 4142 0x1501d5da+7060 7061 4141 0x1501d4b8+7061 7062 4140 0x1501d398+7062 7063 4139 0x1501d277+7063 7064 4138 0x1501d158+7064 7065 4137 0x1501d039+7065 7066 4136 0x1501cf1b+7066 7067 4135 0x1501cdfd+7067 7068 4134 0x1501cce1+7068 7069 4133 0x1501cbc5+7069 7070 4132 0x1501caaa+7070 7071 4131 0x1501c98e+7071 7072 4130 0x1501c875+7072 7073 4129 0x1501c75b+7073 7074 4128 0x1501c643+7074 7075 4127 0x1501c52c+7075 7076 4126 0x1501c414+7076 7077 4125 0x1501c2fd+7077 7078 4124 0x1501c1e8+7078 7079 4123 0x1501c0d3+7079 7080 4122 0x1501bfbe+7080 7081 4121 0x1501beab+7081 7082 4120 0x1501bd98+7082 7083 4119 0x1501bc85+7083 7084 4118 0x1501bb73+7084 7085 4117 0x1501ba61+7085 7086 4116 0x1501b951+7086 7087 4115 0x1501b840+7087 7088 4114 0x1501b731+7088 7089 4113 0x1501b624+7089 7090 4112 0x1501b516+7090 7091 4111 0x1501b40a+7091 7092 4110 0x1501b2fc+7092 7093 4109 0x1501b1f1+7093 7094 4108 0x1501b0e5+7094 7095 4107 0x1501afdb+7095 7096 4106 0x1501aed0+7096 7097 4105 0x1501adc8+7097 7098 4104 0x1501acbe+7098 7099 4103 0x1501abb6+7099 7100 4102 0x1501aaaf+7100 7101 4101 0x1501a9a8+7101 7102 4100 0x1501a8a2+7102 7103 4099 0x1501a79d+7103 7104 4098 0x1501a697+7104 7105 4097 0x1501a594+7105 7106 4096 0x1501a491+7106 7107 4095 0x1501a38e+7107 7108 4094 0x1501a28b+7108 7109 4093 0x1501a18a+7109 7110 4092 0x1501a088+7110 7111 4091 0x15019f88+7111 7112 4090 0x15019e88+7112 7113 4089 0x15019d89+7113 7114 4088 0x15019c8a+7114 7115 4087 0x15019b8c+7115 7116 4086 0x15019a8f+7116 7117 4085 0x15019991+7117 7118 4084 0x15019895+7118 7119 4083 0x1501979a+7119 7120 4082 0x1501969f+7120 7121 4081 0x150195a6+7121 7122 4080 0x150194ac+7122 7123 4079 0x150193b3+7123 7124 4078 0x150192ba+7124 7125 4077 0x150191c2+7125 7126 4076 0x150190ca+7126 7127 4075 0x15018fd3+7127 7128 4074 0x15018ede+7128 7129 4073 0x15018de8+7129 7130 4072 0x15018cf3+7130 7131 4071 0x15018bff+7131 7132 4070 0x15018b0a+7132 7133 4069 0x15018a18+7133 7134 4068 0x15018925+7134 7135 4067 0x15018832+7135 7136 4066 0x15018742+7136 7137 4065 0x15018650+7137 7138 4064 0x15018560+7138 7139 4063 0x15018470+7139 7140 4062 0x15018381+7140 7141 4061 0x15018292+7141 7142 4060 0x150181a5+7142 7143 4059 0x150180b6+7143 7144 4058 0x15017fca+7144 7145 4057 0x15017ede+7145 7146 4056 0x15017df2+7146 7147 4055 0x15017d06+7147 7148 4054 0x15017c1b+7148 7149 4053 0x15017b31+7149 7150 4052 0x15017a47+7150 7151 4051 0x1501795e+7151 7152 4050 0x15017876+7152 7153 4049 0x1501778e+7153 7154 4048 0x150176a7+7154 7155 4047 0x150175bf+7155 7156 4046 0x150174da+7156 7157 4045 0x150173f3+7157 7158 4044 0x1501730e+7158 7159 4043 0x1501722a+7159 7160 4042 0x15017146+7160 7161 4041 0x15017062+7161 7162 4040 0x15016f7f+7162 7163 4039 0x15016e9c+7163 7164 4038 0x15016db9+7164 7165 4037 0x15016cd8+7165 7166 4036 0x15016bf7+7166 7167 4035 0x15016b18+7167 7168 4034 0x15016a38+7168 7169 4033 0x15016958+7169 7170 4032 0x15016879+7170 7171 4031 0x1501679a+7171 7172 4030 0x150166bd+7172 7173 4029 0x150165e0+7173 7174 4028 0x15016502+7174 7175 4027 0x15016426+7175 7176 4026 0x1501634a+7176 7177 4025 0x1501626e+7177 7178 4024 0x15016193+7178 7179 4023 0x150160b9+7179 7180 4022 0x15015fdf+7180 7181 4021 0x15015f06+7181 7182 4020 0x15015e2e+7182 7183 4019 0x15015d55+7183 7184 4018 0x15015c7d+7184 7185 4017 0x15015ba6+7185 7186 4016 0x15015acf+7186 7187 4015 0x150159f9+7187 7188 4014 0x15015922+7188 7189 4013 0x1501584d+7189 7190 4012 0x15015778+7190 7191 4011 0x150156a5+7191 7192 4010 0x150155d2+7192 7193 4009 0x15015500+7193 7194 4008 0x1501542e+7194 7195 4007 0x1501535d+7195 7196 4006 0x1501528c+7196 7197 4005 0x150151bc+7197 7198 4004 0x150150ed+7198 7199 4003 0x1501501d+7199 7200 4002 0x15014f4f+7200 7201 4001 0x15014e80+7201 7202 4000 0x15014db2+7202 7203 3999 0x15014ce5+7203 7204 3998 0x15014c18+7204 7205 3997 0x15014b4c+7205 7206 3996 0x15014a80+7206 7207 3995 0x150149b4+7207 7208 3994 0x150148e9+7208 7209 3993 0x1501481f+7209 7210 3992 0x15014754+7210 7211 3991 0x1501468b+7211 7212 3990 0x150145c2+7212 7213 3989 0x150144fa+7213 7214 3988 0x15014432+7214 7215 3987 0x1501436a+7215 7216 3986 0x150142a3+7216 7217 3985 0x150141dc+7217 7218 3984 0x15014115+7218 7219 3983 0x15014050+7219 7220 3982 0x15013f8a+7220 7221 3981 0x15013ec5+7221 7222 3980 0x15013e01+7222 7223 3979 0x15013d3d+7223 7224 3978 0x15013c79+7224 7225 3977 0x15013bb6+7225 7226 3976 0x15013af3+7226 7227 3975 0x15013a31+7227 7228 3974 0x15013970+7228 7229 3973 0x150138af+7229 7230 3972 0x150137ee+7230 7231 3971 0x1501372e+7231 7232 3970 0x1501366e+7232 7233 3969 0x150135af+7233 7234 3968 0x150134f0+7234 7235 3967 0x15013431+7235 7236 3966 0x15013373+7236 7237 3965 0x150132b5+7237 7238 3964 0x150131f8+7238 7239 3963 0x1501313c+7239 7240 3962 0x1501307f+7240 7241 3961 0x15012fc3+7241 7242 3960 0x15012f08+7242 7243 3959 0x15012e4e+7243 7244 3958 0x15012d93+7244 7245 3957 0x15012cda+7245 7246 3956 0x15012c20+7246 7247 3955 0x15012b67+7247 7248 3954 0x15012aae+7248 7249 3953 0x150129f6+7249 7250 3952 0x1501293e+7250 7251 3951 0x15012886+7251 7252 3950 0x150127d0+7252 7253 3949 0x15012719+7253 7254 3948 0x15012663+7254 7255 3947 0x150125ad+7255 7256 3946 0x150124f8+7256 7257 3945 0x15012443+7257 7258 3944 0x1501238f+7258 7259 3943 0x150122dc+7259 7260 3942 0x15012228+7260 7261 3941 0x15012175+7261 7262 3940 0x150120c2+7262 7263 3939 0x15012010+7263 7264 3938 0x15011f5e+7264 7265 3937 0x15011ead+7265 7266 3936 0x15011dfc+7266 7267 3935 0x15011d4c+7267 7268 3934 0x15011c9b+7268 7269 3933 0x15011beb+7269 7270 3932 0x15011b3c+7270 7271 3931 0x15011a8e+7271 7272 3930 0x150119e0+7272 7273 3929 0x15011931+7273 7274 3928 0x15011885+7274 7275 3927 0x150117d7+7275 7276 3926 0x1501172b+7276 7277 3925 0x1501167e+7277 7278 3924 0x150115d2+7278 7279 3923 0x15011527+7279 7280 3922 0x1501147c+7280 7281 3921 0x150113d1+7281 7282 3920 0x15011327+7282 7283 3919 0x1501127d+7283 7284 3918 0x150111d3+7284 7285 3917 0x1501112a+7285 7286 3916 0x15011082+7286 7287 3915 0x15010fda+7287 7288 3914 0x15010f32+7288 7289 3913 0x15010e8b+7289 7290 3912 0x15010de4+7290 7291 3911 0x15010d3e+7291 7292 3910 0x15010c97+7292 7293 3909 0x15010bf2+7293 7294 3908 0x15010b4d+7294 7295 3907 0x15010aa7+7295 7296 3906 0x15010a02+7296 7297 3905 0x1501095f+7297 7298 3904 0x150108ba+7298 7299 3903 0x15010817+7299 7300 3902 0x15010774+7300 7301 3901 0x150106d2+7301 7302 3900 0x15010630+7302 7303 3899 0x1501058d+7303 7304 3898 0x150104ec+7304 7305 3897 0x1501044c+7305 7306 3896 0x150103ab+7306 7307 3895 0x1501030b+7307 7308 3894 0x1501026b+7308 7309 3893 0x150101cb+7309 7310 3892 0x1501012d+7310 7311 3891 0x1501008e+7311 7312 3890 0x1500fff0+7312 7313 3889 0x1500ff52+7313 7314 3888 0x1500feb4+7314 7315 3887 0x1500fe17+7315 7316 3886 0x1500fd7a+7316 7317 3885 0x1500fcde+7317 7318 3884 0x1500fc42+7318 7319 3883 0x1500fba6+7319 7320 3882 0x1500fb0b+7320 7321 3881 0x1500fa71+7321 7322 3880 0x1500f9d6+7322 7323 3879 0x1500f93c+7323 7324 3878 0x1500f8a2+7324 7325 3877 0x1500f809+7325 7326 3876 0x1500f770+7326 7327 3875 0x1500f6d8+7327 7328 3874 0x1500f63f+7328 7329 3873 0x1500f5a7+7329 7330 3872 0x1500f50f+7330 7331 3871 0x1500f478+7331 7332 3870 0x1500f3e2+7332 7333 3869 0x1500f34b+7333 7334 3868 0x1500f2b5+7334 7335 3867 0x1500f220+7335 7336 3866 0x1500f18b+7336 7337 3865 0x1500f0f6+7337 7338 3864 0x1500f061+7338 7339 3863 0x1500efcd+7339 7340 3862 0x1500ef39+7340 7341 3861 0x1500eea6+7341 7342 3860 0x1500ee12+7342 7343 3859 0x1500ed7f+7343 7344 3858 0x1500eced+7344 7345 3857 0x1500ec5b+7345 7346 3856 0x1500ebc9+7346 7347 3855 0x1500eb38+7347 7348 3854 0x1500eaa7+7348 7349 3853 0x1500ea16+7349 7350 3852 0x1500e986+7350 7351 3851 0x1500e8f7+7351 7352 3850 0x1500e867+7352 7353 3849 0x1500e7d8+7353 7354 3848 0x1500e749+7354 7355 3847 0x1500e6ba+7355 7356 3846 0x1500e62c+7356 7357 3845 0x1500e59e+7357 7358 3844 0x1500e511+7358 7359 3843 0x1500e483+7359 7360 3842 0x1500e3f7+7360 7361 3841 0x1500e36a+7361 7362 3840 0x1500e2de+7362 7363 3839 0x1500e252+7363 7364 3838 0x1500e1c7+7364 7365 3837 0x1500e13b+7365 7366 3836 0x1500e0b1+7366 7367 3835 0x1500e027+7367 7368 3834 0x1500df9d+7368 7369 3833 0x1500df13+7369 7370 3832 0x1500de8a+7370 7371 3831 0x1500de00+7371 7372 3830 0x1500dd77+7372 7373 3829 0x1500dcef+7373 7374 3828 0x1500dc67+7374 7375 3827 0x1500dbe0+7375 7376 3826 0x1500db58+7376 7377 3825 0x1500dad1+7377 7378 3824 0x1500da4a+7378 7379 3823 0x1500d9c4+7379 7380 3822 0x1500d93d+7380 7381 3821 0x1500d8b8+7381 7382 3820 0x1500d832+7382 7383 3819 0x1500d7ae+7383 7384 3818 0x1500d729+7384 7385 3817 0x1500d6a4+7385 7386 3816 0x1500d620+7386 7387 3815 0x1500d59c+7387 7388 3814 0x1500d518+7388 7389 3813 0x1500d495+7389 7390 3812 0x1500d412+7390 7391 3811 0x1500d390+7391 7392 3810 0x1500d30e+7392 7393 3809 0x1500d28c+7393 7394 3808 0x1500d209+7394 7395 3807 0x1500d189+7395 7396 3806 0x1500d107+7396 7397 3805 0x1500d087+7397 7398 3804 0x1500d007+7398 7399 3803 0x1500cf87+7399 7400 3802 0x1500cf07+7400 7401 3801 0x1500ce87+7401 7402 3800 0x1500ce08+7402 7403 3799 0x1500cd89+7403 7404 3798 0x1500cd0b+7404 7405 3797 0x1500cc8c+7405 7406 3796 0x1500cc0f+7406 7407 3795 0x1500cb90+7407 7408 3794 0x1500cb13+7408 7409 3793 0x1500ca96+7409 7410 3792 0x1500ca19+7410 7411 3791 0x1500c99d+7411 7412 3790 0x1500c921+7412 7413 3789 0x1500c8a6+7413 7414 3788 0x1500c82a+7414 7415 3787 0x1500c7af+7415 7416 3786 0x1500c734+7416 7417 3785 0x1500c6b9+7417 7418 3784 0x1500c63f+7418 7419 3783 0x1500c5c5+7419 7420 3782 0x1500c54b+7420 7421 3781 0x1500c4d1+7421 7422 3780 0x1500c458+7422 7423 3779 0x1500c3df+7423 7424 3778 0x1500c366+7424 7425 3777 0x1500c2ee+7425 7426 3776 0x1500c276+7426 7427 3775 0x1500c1fe+7427 7428 3774 0x1500c187+7428 7429 3773 0x1500c110+7429 7430 3772 0x1500c099+7430 7431 3771 0x1500c022+7431 7432 3770 0x1500bfac+7432 7433 3769 0x1500bf36+7433 7434 3768 0x1500bec0+7434 7435 3767 0x1500be4b+7435 7436 3766 0x1500bdd5+7436 7437 3765 0x1500bd60+7437 7438 3764 0x1500bceb+7438 7439 3763 0x1500bc77+7439 7440 3762 0x1500bc03+7440 7441 3761 0x1500bb8f+7441 7442 3760 0x1500bb1c+7442 7443 3759 0x1500baa8+7443 7444 3758 0x1500ba36+7444 7445 3757 0x1500b9c3+7445 7446 3756 0x1500b950+7446 7447 3755 0x1500b8de+7447 7448 3754 0x1500b86c+7448 7449 3753 0x1500b7fa+7449 7450 3752 0x1500b789+7450 7451 3751 0x1500b717+7451 7452 3750 0x1500b6a7+7452 7453 3749 0x1500b636+7453 7454 3748 0x1500b5c5+7454 7455 3747 0x1500b555+7455 7456 3746 0x1500b4e5+7456 7457 3745 0x1500b476+7457 7458 3744 0x1500b407+7458 7459 3743 0x1500b398+7459 7460 3742 0x1500b329+7460 7461 3741 0x1500b2bb+7461 7462 3740 0x1500b24c+7462 7463 3739 0x1500b1de+7463 7464 3738 0x1500b170+7464 7465 3737 0x1500b103+7465 7466 3736 0x1500b095+7466 7467 3735 0x1500b028+7467 7468 3734 0x1500afbb+7468 7469 3733 0x1500af4e+7469 7470 3732 0x1500aee2+7470 7471 3731 0x1500ae76+7471 7472 3730 0x1500ae0a+7472 7473 3729 0x1500ad9f+7473 7474 3728 0x1500ad34+7474 7475 3727 0x1500acc9+7475 7476 3726 0x1500ac5e+7476 7477 3725 0x1500abf3+7477 7478 3724 0x1500ab8a+7478 7479 3723 0x1500ab20+7479 7480 3722 0x1500aab7+7480 7481 3721 0x1500aa4e+7481 7482 3720 0x1500a9e5+7482 7483 3719 0x1500a97c+7483 7484 3718 0x1500a914+7484 7485 3717 0x1500a8ac+7485 7486 3716 0x1500a844+7486 7487 3715 0x1500a7dd+7487 7488 3714 0x1500a775+7488 7489 3713 0x1500a70f+7489 7490 3712 0x1500a6a8+7490 7491 3711 0x1500a641+7491 7492 3710 0x1500a5db+7492 7493 3709 0x1500a575+7493 7494 3708 0x1500a50f+7494 7495 3707 0x1500a4a9+7495 7496 3706 0x1500a444+7496 7497 3705 0x1500a3df+7497 7498 3704 0x1500a37a+7498 7499 3703 0x1500a315+7499 7500 3702 0x1500a2b1+7500 7501 3701 0x1500a24c+7501 7502 3700 0x1500a1e8+7502 7503 3699 0x1500a185+7503 7504 3698 0x1500a121+7504 7505 3697 0x1500a0be+7505 7506 3696 0x1500a05b+7506 7507 3695 0x15009ff8+7507 7508 3694 0x15009f96+7508 7509 3693 0x15009f33+7509 7510 3692 0x15009ed1+7510 7511 3691 0x15009e6f+7511 7512 3690 0x15009e0e+7512 7513 3689 0x15009dac+7513 7514 3688 0x15009d4b+7514 7515 3687 0x15009cea+7515 7516 3686 0x15009c89+7516 7517 3685 0x15009c29+7517 7518 3684 0x15009bc9+7518 7519 3683 0x15009b68+7519 7520 3682 0x15009b09+7520 7521 3681 0x15009aaa+7521 7522 3680 0x15009a4a+7522 7523 3679 0x150099eb+7523 7524 3678 0x1500998c+7524 7525 3677 0x1500992d+7525 7526 3676 0x150098cf+7526 7527 3675 0x15009870+7527 7528 3674 0x15009812+7528 7529 3673 0x150097b5+7529 7530 3672 0x15009757+7530 7531 3671 0x150096fa+7531 7532 3670 0x1500969c+7532 7533 3669 0x15009640+7533 7534 3668 0x150095e3+7534 7535 3667 0x15009587+7535 7536 3666 0x1500952b+7536 7537 3665 0x150094cf+7537 7538 3664 0x15009473+7538 7539 3663 0x15009417+7539 7540 3662 0x150093bc+7540 7541 3661 0x15009361+7541 7542 3660 0x15009306+7542 7543 3659 0x150092ab+7543 7544 3658 0x15009250+7544 7545 3657 0x150091f6+7545 7546 3656 0x1500919c+7546 7547 3655 0x15009142+7547 7548 3654 0x150090e8+7548 7549 3653 0x1500908f+7549 7550 3652 0x15009036+7550 7551 3651 0x15008fdd+7551 7552 3650 0x15008f85+7552 7553 3649 0x15008f2c+7553 7554 3648 0x15008ed4+7554 7555 3647 0x15008e7b+7555 7556 3646 0x15008e23+7556 7557 3645 0x15008dcc+7557 7558 3644 0x15008d74+7558 7559 3643 0x15008d1d+7559 7560 3642 0x15008cc6+7560 7561 3641 0x15008c6f+7561 7562 3640 0x15008c18+7562 7563 3639 0x15008bc2+7563 7564 3638 0x15008b6b+7564 7565 3637 0x15008b15+7565 7566 3636 0x15008ac0+7566 7567 3635 0x15008a6a+7567 7568 3634 0x15008a15+7568 7569 3633 0x150089bf+7569 7570 3632 0x1500896b+7570 7571 3631 0x15008916+7571 7572 3630 0x150088c1+7572 7573 3629 0x1500886d+7573 7574 3628 0x15008819+7574 7575 3627 0x150087c4+7575 7576 3626 0x15008771+7576 7577 3625 0x1500871d+7577 7578 3624 0x150086ca+7578 7579 3623 0x15008677+7579 7580 3622 0x15008623+7580 7581 3621 0x150085d1+7581 7582 3620 0x1500857e+7582 7583 3619 0x1500852c+7583 7584 3618 0x150084da+7584 7585 3617 0x15008488+7585 7586 3616 0x15008436+7586 7587 3615 0x150083e5+7587 7588 3614 0x15008393+7588 7589 3613 0x15008342+7589 7590 3612 0x150082f1+7590 7591 3611 0x150082a0+7591 7592 3610 0x1500824f+7592 7593 3609 0x150081ff+7593 7594 3608 0x150081af+7594 7595 3607 0x1500815f+7595 7596 3606 0x1500810f+7596 7597 3605 0x150080c0+7597 7598 3604 0x15008070+7598 7599 3603 0x15008021+7599 7600 3602 0x147fd24b+7600 7601 3601 0x147f8379+7601 7602 3600 0x147f34a7+7602 7603 3599 0x147ee62b+7603 7604 3598 0x147e9805+7604 7605 3597 0x147e49df+7605 7606 3596 0x147dfc0f+7606 7607 3595 0x147dae3e+7607 7608 3594 0x147d60c4+7608 7609 3593 0x147d134a+7609 7610 3592 0x147cc625+7610 7611 3591 0x147c7956+7611 7612 3590 0x147c2cde+7612 7613 3589 0x147be00f+7613 7614 3588 0x147b9396+7614 7615 3587 0x147b4773+7615 7616 3586 0x147afb50+7616 7617 3585 0x147aaf83+7617 7618 3584 0x147a640c+7618 7619 3583 0x147a183f+7619 7620 3582 0x1479cd1d+7620 7621 3581 0x147981fc+7621 7622 3580 0x14793730+7622 7623 3579 0x1478ec65+7623 7624 3578 0x1478a199+7624 7625 3577 0x14785724+7625 7626 3576 0x14780d04+7626 7627 3575 0x1477c33a+7627 7628 3574 0x14777970+7628 7629 3573 0x14772ffc+7629 7630 3572 0x1476e688+7630 7631 3571 0x14769d14+7631 7632 3570 0x147653f6+7632 7633 3569 0x14760ad8+7633 7634 3568 0x1475c20f+7634 7635 3567 0x1475799d+7635 7636 3566 0x1475312a+7636 7637 3565 0x1474e90e+7637 7638 3564 0x1474a0f1+7638 7639 3563 0x147458d4+7639 7640 3562 0x1474110e+7640 7641 3561 0x1473c99d+7641 7642 3560 0x14738282+7642 7643 3559 0x14733b67+7643 7644 3558 0x1472f44c+7644 7645 3557 0x1472ad87+7645 7646 3556 0x147266c1+7646 7647 3555 0x14722052+7647 7648 3554 0x1471d9e3+7648 7649 3553 0x147193c9+7649 7650 3552 0x14714db0+7650 7651 3551 0x147107ed+7651 7652 3550 0x1470c27f+7652 7653 3549 0x14707cbb+7653 7654 3548 0x147037a3+7654 7655 3547 0x146ff28c+7655 7656 3546 0x146fad74+7656 7657 3545 0x146f68b2+7657 7658 3544 0x146f2446+7658 7659 3543 0x146edfda+7659 7660 3542 0x146e9b6d+7660 7661 3541 0x146e5757+7661 7662 3540 0x146e1341+7662 7663 3539 0x146dcf81+7663 7664 3538 0x146d8bc0+7664 7665 3537 0x146d4856+7665 7666 3536 0x146d04eb+7666 7667 3535 0x146cc1d6+7667 7668 3534 0x146c7ec2+7668 7669 3533 0x146c3c03+7669 7670 3532 0x146bf944+7670 7671 3531 0x146bb6db+7671 7672 3530 0x146b7472+7672 7673 3529 0x146b32b5+7673 7674 3528 0x146af0a2+7674 7675 3527 0x146aaee5+7675 7676 3526 0x146a6d28+7676 7677 3525 0x146a2b6a+7677 7678 3524 0x1469ea03+7678 7679 3523 0x1469a8f2+7679 7680 3522 0x146967e0+7680 7681 3521 0x146926cf+7681 7682 3520 0x1468e613+7682 7683 3519 0x1468a5ad+7683 7684 3518 0x146864f2+7684 7685 3517 0x146824e2+7685 7686 3516 0x1467e47c+7686 7687 3515 0x1467a4c2+7687 7688 3514 0x14676508+7688 7689 3513 0x1467254e+7689 7690 3512 0x1466e5ea+7690 7691 3511 0x1466a686+7691 7692 3510 0x14666777+7692 7693 3509 0x14662869+7693 7694 3508 0x1465e95b+7694 7695 3507 0x1465aaa2+7695 7696 3506 0x14656bea+7696 7697 3505 0x14652d87+7697 7698 3504 0x1464ef25+7698 7699 3503 0x1464b118+7699 7700 3502 0x1464730b+7700 7701 3501 0x14643555+7701 7702 3500 0x1463f79e+7702 7703 3499 0x1463b9e7+7703 7704 3498 0x14637cdc+7704 7705 3497 0x14633f7b+7705 7706 3496 0x14630270+7706 7707 3495 0x1462c565+7707 7708 3494 0x146288af+7708 7709 3493 0x14624bfa+7709 7710 3492 0x14620f45+7710 7711 3491 0x1461d2e5+7711 7712 3490 0x14619686+7712 7713 3489 0x14615a7c+7713 7714 3488 0x14611e73+7714 7715 3487 0x1460e2bf+7715 7716 3486 0x1460a70c+7716 7717 3485 0x14606bae+7717 7718 3484 0x14602ffa+7718 7719 3483 0x145ff548+7719 7720 3482 0x145fb9ea+7720 7721 3481 0x145f7ee2+7721 7722 3480 0x145f4430+7722 7723 3479 0x145f097e+7723 7724 3478 0x145ececc+7724 7725 3477 0x145e9470+7725 7726 3476 0x145e5a14+7726 7727 3475 0x145e200d+7727 7728 3474 0x145de607+7728 7729 3473 0x145dac01+7729 7730 3472 0x145d7250+7730 7731 3471 0x145d38f6+7731 7732 3470 0x145cff45+7732 7733 3469 0x145cc5ea+7733 7734 3468 0x145c8ce6+7734 7735 3467 0x145c53e1+7735 7736 3466 0x145c1b32+7736 7737 3465 0x145be22d+7737 7738 3464 0x145ba97e+7738 7739 3463 0x145b7125+7739 7740 3462 0x145b3876+7740 7741 3461 0x145b0073+7741 7742 3460 0x145ac81a+7742 7743 3459 0x145a9017+7743 7744 3458 0x145a586a+7744 7745 3457 0x145a2067+7745 7746 3456 0x1459e90f+7746 7747 3455 0x1459b162+7747 7748 3454 0x14597a0a+7748 7749 3453 0x145942b3+7749 7750 3452 0x14590c07+7750 7751 3451 0x1458d506+7751 7752 3450 0x14589e04+7752 7753 3449 0x14586758+7753 7754 3448 0x145830ad+7754 7755 3447 0x1457fa57+7755 7756 3446 0x1457c401+7756 7757 3445 0x14578dab+7757 7758 3444 0x145757ab+7758 7759 3443 0x145721ab+7759 7760 3442 0x1456ebab+7760 7761 3441 0x1456b601+7761 7762 3440 0x14568057+7762 7763 3439 0x14564b03+7763 7764 3438 0x145615ae+7764 7765 3437 0x1455e05a+7765 7766 3436 0x1455abb2+7766 7767 3435 0x14557709+7767 7768 3434 0x14554261+7768 7769 3433 0x14550e0e+7769 7770 3432 0x1454d9bb+7770 7771 3431 0x1454a569+7771 7772 3430 0x1454716c+7772 7773 3429 0x14543d6f+7773 7774 3428 0x1454099d+7774 7775 3427 0x1453d5cb+7775 7776 3426 0x1453a24f+7776 7777 3425 0x14536ed3+7777 7778 3424 0x14533b57+7778 7779 3423 0x14530831+7779 7780 3422 0x1452d536+7780 7781 3421 0x1452a23b+7781 7782 3420 0x14526f6a+7782 7783 3419 0x14523c9a+7783 7784 3418 0x145209f5+7784 7785 3417 0x1451d77a+7785 7786 3416 0x1451a500+7786 7787 3415 0x145172b0+7787 7788 3414 0x1451408c+7788 7789 3413 0x14510e67+7789 7790 3412 0x1450dc98+7790 7791 3411 0x1450aaca+7791 7792 3410 0x145078fb+7792 7793 3409 0x14504757+7793 7794 3408 0x145015de+7794 7795 3407 0x144fe491+7795 7796 3406 0x144fb398+7796 7797 3405 0x144f8276+7797 7798 3404 0x144f517d+7798 7799 3403 0x144f2085+7799 7800 3402 0x144eefb8+7800 7801 3401 0x144ebf16+7801 7802 3400 0x144e8e9f+7802 7803 3399 0x144e5e28+7803 7804 3398 0x144e2ddb+7804 7805 3397 0x144dfd8f+7805 7806 3396 0x144dcd99+7806 7807 3395 0x144d9d77+7807 7808 3394 0x144d6dac+7808 7809 3393 0x144d3de0+7809 7810 3392 0x144d0e40+7810 7811 3391 0x144cdef5+7811 7812 3390 0x144caf7f+7812 7813 3389 0x144c8034+7813 7814 3388 0x144c5115+7814 7815 3387 0x144c21f5+7815 7816 3386 0x144bf300+7816 7817 3385 0x144bc436+7817 7818 3384 0x144b956c+7818 7819 3383 0x144b66cd+7819 7820 3382 0x144b3859+7820 7821 3381 0x144b09e5+7821 7822 3380 0x144adb9c+7822 7823 3379 0x144aad7e+7823 7824 3378 0x144a7f60+7824 7825 3377 0x144a516c+7825 7826 3376 0x144a23a4+7826 7827 3375 0x1449f607+7827 7828 3374 0x1449c869+7828 7829 3373 0x14499acc+7829 7830 3372 0x14496d59+7830 7831 3371 0x14494012+7831 7832 3370 0x144912ca+7832 7833 3369 0x1448e5d8+7833 7834 3368 0x1448b8bc+7834 7835 3367 0x14488bf5+7835 7836 3366 0x14485f2e+7836 7837 3365 0x14483268+7837 7838 3364 0x144805f7+7838 7839 3363 0x1447d986+7839 7840 3362 0x1447ad15+7840 7841 3361 0x144780cf+7841 7842 3360 0x144754df+7842 7843 3359 0x144728ef+7843 7844 3358 0x1446fcff+7844 7845 3357 0x1446d13a+7845 7846 3356 0x1446a574+7846 7847 3355 0x144679da+7847 7848 3354 0x14464e6b+7848 7849 3353 0x144622fc+7849 7850 3352 0x1445f7b7+7850 7851 3351 0x1445cc73+7851 7852 3350 0x1445a159+7852 7853 3349 0x1445766b+7853 7854 3348 0x14454b7c+7854 7855 3347 0x144520b9+7855 7856 3346 0x1444f620+7856 7857 3345 0x1444cbb3+7857 7858 3344 0x1444a145+7858 7859 3343 0x144476d7+7859 7860 3342 0x14444c94+7860 7861 3341 0x1444227d+7861 7862 3340 0x1443f865+7862 7863 3339 0x1443ce78+7863 7864 3338 0x1443a48b+7864 7865 3337 0x14437ac9+7865 7866 3336 0x14435132+7866 7867 3335 0x1443279b+7867 7868 3334 0x1442fe2f+7868 7869 3333 0x1442d4c3+7869 7870 3332 0x1442ab81+7870 7871 3331 0x1442826b+7871 7872 3330 0x14425980+7872 7873 3329 0x14423094+7873 7874 3328 0x144207d4+7874 7875 3327 0x1441df14+7875 7876 3326 0x1441b653+7876 7877 3325 0x14418dbe+7877 7878 3324 0x14416553+7878 7879 3323 0x14413ce9+7879 7880 3322 0x144114a9+7880 7881 3321 0x1440ec95+7881 7882 3320 0x1440c480+7882 7883 3319 0x14409c96+7883 7884 3318 0x144074ac+7884 7885 3317 0x14404cee+7885 7886 3316 0x1440252f+7886 7887 3315 0x143ffd9b+7887 7888 3314 0x143fd65d+7888 7889 3313 0x143faef4+7889 7890 3312 0x143f878b+7890 7891 3311 0x143f6078+7891 7892 3310 0x143f393a+7892 7893 3309 0x143f1252+7893 7894 3308 0x143eeb6a+7894 7895 3307 0x143ec482+7895 7896 3306 0x143e9dc4+7896 7897 3305 0x143e7732+7897 7898 3304 0x143e50a0+7898 7899 3303 0x143e2a38+7899 7900 3302 0x143e03d1+7900 7901 3301 0x143ddd95+7901 7902 3300 0x143db783+7902 7903 3299 0x143d919d+7903 7904 3298 0x143d6b8b+7904 7905 3297 0x143d45a5+7905 7906 3296 0x143d1fe9+7906 7907 3295 0x143cfa2d+7907 7908 3294 0x143cd49d+7908 7909 3293 0x143caf0c+7909 7910 3292 0x143c89a6+7910 7911 3291 0x143c6441+7911 7912 3290 0x143c3f06+7912 7913 3289 0x143c19f6+7913 7914 3288 0x143bf4e6+7914 7915 3287 0x143bcfd6+7915 7916 3286 0x143baaf1+7916 7917 3285 0x143b8637+7917 7918 3284 0x143b61a8+7918 7919 3283 0x143b3d19+7919 7920 3282 0x143b188a+7920 7921 3281 0x143af426+7921 7922 3280 0x143acfc1+7922 7923 3279 0x143aab88+7923 7924 3278 0x143a874f+7924 7925 3277 0x143a6341+7925 7926 3276 0x143a3f32+7926 7927 3275 0x143a1b4f+7927 7928 3274 0x1439f76c+7928 7929 3273 0x1439d3b3+7929 7930 3272 0x1439b026+7930 7931 3271 0x14398c98+7931 7932 3270 0x1439690b+7932 7933 3269 0x143945a8+7933 7934 3268 0x1439229b+7934 7935 3267 0x1438ff64+7935 7936 3266 0x1438dc2c+7936 7937 3265 0x1438b91f+7937 7938 3264 0x1438963e+7938 7939 3263 0x1438735c+7939 7940 3262 0x1438507a+7940 7941 3261 0x14382dee+7941 7942 3260 0x14380b37+7942 7943 3259 0x1437e8ab+7943 7944 3258 0x1437c64a+7944 7945 3257 0x1437a3e9+7945 7946 3256 0x14378188+7946 7947 3255 0x14375f52+7947 7948 3254 0x14373d47+7948 7949 3253 0x14371b67+7949 7950 3252 0x1436f987+7950 7951 3251 0x1436d7a7+7951 7952 3250 0x1436b5c6+7952 7953 3249 0x14369411+7953 7954 3248 0x14367287+7954 7955 3247 0x143650fc+7955 7956 3246 0x14362f72+7956 7957 3245 0x14360e13+7957 7958 3244 0x1435ecde+7958 7959 3243 0x1435cbaa+7959 7960 3242 0x1435aa75+7960 7961 3241 0x1435896c+7961 7962 3240 0x14356862+7962 7963 3239 0x14354784+7963 7964 3238 0x143526d0+7964 7965 3237 0x1435061c+7965 7966 3236 0x1434e569+7966 7967 3235 0x1434c4e0+7967 7968 3234 0x1434a457+7968 7969 3233 0x143483f9+7969 7970 3232 0x1434639c+7970 7971 3231 0x14344369+7971 7972 3230 0x14342336+7972 7973 3229 0x14340303+7973 7974 3228 0x1433e2fb+7974 7975 3227 0x1433c31e+7975 7976 3226 0x1433a341+7976 7977 3225 0x14338364+7977 7978 3224 0x143363b2+7978 7979 3223 0x14334400+7979 7980 3222 0x143324a4+7980 7981 3221 0x1433051c+7981 7982 3220 0x1432e5c0+7982 7983 3219 0x1432c664+7983 7984 3218 0x1432a708+7984 7985 3217 0x143287d7+7985 7986 3216 0x143268d0+7986 7987 3215 0x1432499f+7987 7988 3214 0x14322ac3+7988 7989 3213 0x14320be8+7989 7990 3212 0x1431ed0d+7990 7991 3211 0x1431ce31+7991 7992 3210 0x1431afac+7992 7993 3209 0x143190fb+7993 7994 3208 0x14317276+7994 7995 3207 0x1431541b+7995 7996 3206 0x143135c0+7996 7997 3205 0x14311766+7997 7998 3204 0x1430f936+7998 7999 3203 0x1430db06+7999 8000 3202 0x1430bd02+8000 8001 3201 0x14309efd+8001 8002 3200 0x143080f8+8002 8003 3199 0x1430631e+8003 8004 3198 0x14304544+8004 8005 3197 0x14302795+8005 8006 3196 0x143009e7+8006 8007 3195 0x142fec38+8007 8008 3194 0x142fceb4+8008 8009 3193 0x142fb15b+8009 8010 3192 0x142f9402+8010 8011 3191 0x142f76a9+8011 8012 3190 0x142f597a+8012 8013 3189 0x142f3c4c+8013 8014 3188 0x142f1f1e+8014 8015 3187 0x142f021b+8015 8016 3186 0x142ee518+8016 8017 3185 0x142ec840+8017 8018 3184 0x142eab67+8018 8019 3183 0x142e8eba+8019 8020 3182 0x142e71e2+8020 8021 3181 0x142e5560+8021 8022 3180 0x142e38b2+8022 8023 3179 0x142e1c30+8023 8024 3178 0x142dffd8+8024 8025 3177 0x142de381+8025 8026 3176 0x142dc754+8026 8027 3175 0x142daafd+8027 8028 3174 0x142d8ed0+8028 8029 3173 0x142d72cf+8029 8030 3172 0x142d56a2+8030 8031 3171 0x142d3acc+8031 8032 3170 0x142d1eca+8032 8033 3169 0x142d02f3+8033 8034 3168 0x142ce71d+8034 8035 3167 0x142ccb71+8035 8036 3166 0x142cafc5+8036 8037 3165 0x142c9445+8037 8038 3164 0x142c78c4+8038 8039 3163 0x142c5d43+8039 8040 3162 0x142c41ed+8040 8041 3161 0x142c2697+8041 8042 3160 0x142c0b6c+8042 8043 3159 0x142bf017+8043 8044 3158 0x142bd4ec+8044 8045 3157 0x142bb9ec+8045 8046 3156 0x142b9eec+8046 8047 3155 0x142b83ec+8047 8048 3154 0x142b6917+8048 8049 3153 0x142b4e41+8049 8050 3152 0x142b336c+8050 8051 3151 0x142b18c2+8051 8052 3150 0x142afe18+8052 8053 3149 0x142ae383+8053 8054 3148 0x142ac91a+8054 8055 3147 0x142aaec5+8055 8056 3146 0x142a94b2+8056 8057 3145 0x142a7a73+8057 8058 3144 0x142a605f+8058 8059 3143 0x142a464b+8059 8060 3142 0x142a2c37+8060 8061 3141 0x142a124e+8061 8062 3140 0x1429f865+8062 8063 3139 0x1429de92+8063 8064 3138 0x1429c4d4+8064 8065 3137 0x1429ab16+8065 8066 3136 0x1429916d+8066 8067 3135 0x142977da+8067 8068 3134 0x14295e47+8068 8069 3133 0x142944df+8069 8070 3132 0x14292b61+8070 8071 3131 0x1429120f+8071 8072 3130 0x1428f8e7+8072 8073 3129 0x1428dfaa+8073 8074 3128 0x1428c682+8074 8075 3127 0x1428ad5a+8075 8076 3126 0x14289448+8076 8077 3125 0x14287b4b+8077 8078 3124 0x14286264+8078 8079 3123 0x1428497c+8079 8080 3122 0x142830ab+8080 8081 3121 0x142817d9+8081 8082 3120 0x1427ff32+8082 8083 3119 0x1427e68b+8083 8084 3118 0x1427cde4+8084 8085 3117 0x1427b568+8085 8086 3116 0x14279cec+8086 8087 3115 0x1427849b+8087 8088 3114 0x14276c4a+8088 8089 3113 0x142753f9+8089 8090 3112 0x14273bbd+8090 8091 3111 0x14272381+8091 8092 3110 0x14270b5b+8092 8093 3109 0x1426f34a+8093 8094 3108 0x1426db4f+8094 8095 3107 0x1426c354+8095 8096 3106 0x1426ab6e+8096 8097 3105 0x14269388+8097 8098 3104 0x14267bce+8098 8099 3103 0x14266413+8099 8100 3102 0x14264c58+8100 8101 3101 0x142634c8+8101 8102 3100 0x14261d23+8102 8103 3099 0x142605be+8103 8104 3098 0x1425ee59+8104 8105 3097 0x1425d6f4+8105 8106 3096 0x1425bf8f+8106 8107 3095 0x1425a83f+8107 8108 3094 0x14259105+8108 8109 3093 0x142579e1+8109 8110 3092 0x142562bc+8110 8111 3091 0x14254bad+8111 8112 3090 0x142534b3+8112 8113 3089 0x14251dba+8113 8114 3088 0x142506d6+8114 8115 3087 0x1424eff2+8115 8116 3086 0x1424d923+8116 8117 3085 0x1424c26a+8117 8118 3084 0x1424abdb+8118 8119 3083 0x14249538+8119 8120 3082 0x14247ea9+8120 8121 3081 0x1424681b+8121 8122 3080 0x142451a2+8122 8123 3079 0x14243b3f+8123 8124 3078 0x142424db+8124 8125 3077 0x14240e8d+8125 8126 3076 0x1423f855+8126 8127 3075 0x1423e21d+8127 8128 3074 0x1423cbfa+8128 8129 3073 0x1423b5d7+8129 8130 3072 0x14239fdf+8130 8131 3071 0x142389d1+8131 8132 3070 0x142373ee+8132 8133 3069 0x14235e21+8133 8134 3068 0x14234854+8134 8135 3067 0x14233287+8135 8136 3066 0x14231ccf+8136 8137 3065 0x14230718+8137 8138 3064 0x1422f176+8138 8139 3063 0x1422dbe9+8139 8140 3062 0x1422c672+8140 8141 3061 0x1422b0fa+8141 8142 3060 0x14229b83+8142 8143 3059 0x14228621+8143 8144 3058 0x142270d5+8144 8145 3057 0x14225b9e+8145 8146 3056 0x14224667+8146 8147 3055 0x14223146+8147 8148 3054 0x14221c25+8148 8149 3053 0x1422072e+8149 8150 3052 0x1421f222+8150 8151 3051 0x1421dd2c+8151 8152 3050 0x1421c84b+8152 8153 3049 0x1421b36a+8153 8154 3048 0x14219e9e+8154 8155 3047 0x142189e8+8155 8156 3046 0x14217532+8156 8157 3045 0x14216092+8157 8158 3044 0x14214bf1+8158 8159 3043 0x14213766+8159 8160 3042 0x142122db+8160 8161 3041 0x14210e7a+8161 8162 3040 0x1420fa05+8162 8163 3039 0x1420e5ba+8163 8164 3038 0x1420d16f+8164 8165 3037 0x1420bd3a+8165 8166 3036 0x1420a905+8166 8167 3035 0x142094e5+8167 8168 3034 0x142080c5+8168 8169 3033 0x14206cbb+8169 8170 3032 0x142058b1+8170 8171 3031 0x142044bc+8171 8172 3030 0x142030c7+8172 8173 3029 0x14201ce7+8173 8174 3028 0x1420091e+8174 8175 3027 0x141ff554+8175 8176 3026 0x141fe19f+8176 8177 3025 0x141fce00+8177 8178 3024 0x141fba61+8178 8179 3023 0x141fa6d8+8179 8180 3022 0x141f934e+8180 8181 3021 0x141f7fda+8181 8182 3020 0x141f6c66+8182 8183 3019 0x141f5907+8183 8184 3018 0x141f45a9+8184 8185 3017 0x141f3260+8185 8186 3016 0x141f1f2c+8186 8187 3015 0x141f0bf8+8187 8188 3014 0x141ef8c5+8188 8189 3013 0x141ee5a6+8189 8190 3012 0x141ed29e+8190 8191 3011 0x141ebf95+8191 8192 3010 0x141eaca2+8192 8193 3009 0x141e99ae+8193 8194 3008 0x141e86d0+8194 8195 3007 0x141e741e+8195 8196 3006 0x141e6155+8196 8197 3005 0x141e4e8d+8197 8198 3004 0x141e3bda+8198 8199 3003 0x141e293d+8199 8200 3002 0x141e169f+8200 8201 3001 0x141e0417+8201 8202 3000 0x141df18f+8202 8203 2999 0x141ddf1d+8203 8204 2998 0x141dccaa+8204 8205 2997 0x141dba4d+8205 8206 2996 0x141da806+8206 8207 2995 0x141d95a9+8207 8208 2994 0x141d8377+8208 8209 2993 0x141d7145+8209 8210 2992 0x141d5f28+8210 8211 2991 0x141d4d0b+8211 8212 2990 0x141d3b04+8212 8213 2989 0x141d28fd+8213 8214 2988 0x141d170b+8214 8215 2987 0x141d051a+8215 8216 2986 0x141cf328+8216 8217 2985 0x141ce161+8217 8218 2984 0x141ccf85+8218 8219 2983 0x141cbdd4+8219 8220 2982 0x141cac0d+8220 8221 2981 0x141c9a71+8221 8222 2980 0x141c88c0+8222 8223 2979 0x141c773a+8223 8224 2978 0x141c659e+8224 8225 2977 0x141c5442+8225 8226 2976 0x141c42bc+8226 8227 2975 0x141c3161+8227 8228 2974 0x141c1ff0+8228 8229 2973 0x141c0eaa+8229 8230 2972 0x141bfd64+8230 8231 2971 0x141bec1e+8231 8232 2970 0x141bdaed+8232 8233 2969 0x141bc9bd+8233 8234 2968 0x141bb8a2+8234 8235 2967 0x141ba787+8235 8236 2966 0x141b9681+8236 8237 2965 0x141b857c+8237 8238 2964 0x141b748b+8238 8239 2963 0x141b639b+8239 8240 2962 0x141b52c1+8240 8241 2961 0x141b41fc+8241 8242 2960 0x141b3136+8242 8243 2959 0x141b2071+8243 8244 2958 0x141b0fc2+8244 8245 2957 0x141aff12+8245 8246 2956 0x141aee78+8246 8247 2955 0x141adddd+8247 8248 2954 0x141acd59+8248 8249 2953 0x141abcd4+8249 8250 2952 0x141aac4f+8250 8251 2951 0x141a9be0+8251 8252 2950 0x141a8b86+8252 8253 2949 0x141a7b2c+8253 8254 2948 0x141a6ad2+8254 8255 2947 0x141a5a8e+8255 8256 2946 0x141a4a74+8256 8257 2945 0x141a3a46+8257 8258 2944 0x141a2a17+8258 8259 2943 0x141a19fd+8259 8260 2942 0x141a09e4+8260 8261 2941 0x1419f9e0+8261 8262 2940 0x1419e9dc+8262 8263 2939 0x1419d9ed+8263 8264 2938 0x1419c9ff+8264 8265 2937 0x1419ba26+8265 8266 2936 0x1419aa4d+8266 8267 2935 0x14199a74+8267 8268 2934 0x14198ab0+8268 8269 2933 0x14197aed+8269 8270 2932 0x14196b3e+8270 8271 2931 0x14195ba6+8271 8272 2930 0x14194c0d+8272 8273 2929 0x14193c75+8273 8274 2928 0x14192cf1+8274 8275 2927 0x14191d6e+8275 8276 2926 0x14190e00+8276 8277 2925 0x1418fe93+8277 8278 2924 0x1418ef25+8278 8279 2923 0x1418dfcd+8279 8280 2922 0x1418d075+8280 8281 2921 0x1418c132+8281 8282 2920 0x1418b1ef+8282 8283 2919 0x1418a2c2+8283 8284 2918 0x14189394+8284 8285 2917 0x14188467+8285 8286 2916 0x1418754f+8286 8287 2915 0x1418664d+8287 8288 2914 0x1418574a+8288 8289 2913 0x14184848+8289 8290 2912 0x1418395b+8290 8291 2911 0x14182a6e+8291 8292 2910 0x14181b81+8292 8293 2909 0x14180caa+8293 8294 2908 0x1417fdd2+8294 8295 2907 0x1417ef10+8295 8296 2906 0x1417e04e+8296 8297 2905 0x1417d1a2+8297 8298 2904 0x1417c2f5+8298 8299 2903 0x1417b449+8299 8300 2902 0x1417a5b2+8300 8301 2901 0x1417971b+8301 8302 2900 0x141788af+8302 8303 2899 0x14177a2d+8303 8304 2898 0x14176bac+8304 8305 2897 0x14175d3f+8305 8306 2896 0x14174ed3+8306 8307 2895 0x1417407d+8307 8308 2894 0x14173226+8308 8309 2893 0x141723cf+8309 8310 2892 0x1417158e+8310 8311 2891 0x1417074d+8311 8312 2890 0x1416f921+8312 8313 2889 0x1416eaf5+8313 8314 2888 0x1416dcca+8314 8315 2887 0x1416ceb4+8315 8316 2886 0x1416c09d+8316 8317 2885 0x1416b2b2+8317 8318 2884 0x1416a4b1+8318 8319 2883 0x141696b0+8319 8320 2882 0x141688c5+8320 8321 2881 0x14167ada+8321 8322 2880 0x14166cee+8322 8323 2879 0x14165f18+8323 8324 2878 0x14165158+8324 8325 2877 0x14164382+8325 8326 2876 0x141635c2+8326 8327 2875 0x14162817+8327 8328 2874 0x14161a6c+8328 8329 2873 0x14160cc1+8329 8330 2872 0x1415ff16+8330 8331 2871 0x1415f181+8331 8332 2870 0x1415e3eb+8332 8333 2869 0x1415d681+8333 8334 2868 0x1415c901+8334 8335 2867 0x1415bb81+8335 8336 2866 0x1415ae16+8336 8337 2865 0x1415a0ac+8337 8338 2864 0x14159357+8338 8339 2863 0x141585ec+8339 8340 2862 0x141578ac+8340 8341 2861 0x14156b6d+8341 8342 2860 0x14155e43+8342 8343 2859 0x14155123+8343 8344 2858 0x14154404+8344 8345 2857 0x141536ef+8345 8346 2856 0x141529e5+8346 8347 2855 0x14151cdb+8347 8348 2854 0x14150fe7+8348 8349 2853 0x141502f2+8349 8350 2852 0x1414f609+8350 8351 2851 0x1414e91f+8351 8352 2850 0x1414dc40+8352 8353 2849 0x1414cf6c+8353 8354 2848 0x1414c297+8354 8355 2847 0x1414b5ce+8355 8356 2846 0x1414a90f+8356 8357 2845 0x14149c5b+8357 8358 2844 0x14148fa7+8358 8359 2843 0x141482fe+8359 8360 2842 0x14147654+8360 8361 2841 0x141469c0+8361 8362 2840 0x14145d2c+8362 8363 2839 0x141450ae+8363 8364 2838 0x14144430+8364 8365 2837 0x141437b1+8365 8366 2836 0x14142b3e+8366 8367 2835 0x14141eca+8367 8368 2834 0x14141261+8368 8369 2833 0x14140603+8369 8370 2832 0x1413f9af+8370 8371 2831 0x1413ed5c+8371 8372 2830 0x1413e113+8372 8373 2829 0x1413d4d5+8373 8374 2828 0x1413c8a2+8374 8375 2827 0x1413bc6f+8375 8376 2826 0x1413b046+8376 8377 2825 0x1413a41d+8377 8378 2824 0x14139800+8378 8379 2823 0x14138bf7+8379 8380 2822 0x14137fef+8380 8381 2821 0x141373e7+8381 8382 2820 0x141367e9+8382 8383 2819 0x14135bf6+8383 8384 2818 0x14135003+8384 8385 2817 0x1413441b+8385 8386 2816 0x14133833+8386 8387 2815 0x14132c60+8387 8388 2814 0x14132083+8388 8389 2813 0x141314bb+8389 8390 2812 0x141308f3+8390 8391 2811 0x1412fd36+8391 8392 2810 0x1412f183+8392 8393 2809 0x1412e5d1+8393 8394 2808 0x1412da34+8394 8395 2807 0x1412ce97+8395 8396 2806 0x1412c2fa+8396 8397 2805 0x1412b767+8397 8398 2804 0x1412abe0+8398 8399 2803 0x1412a058+8399 8400 2802 0x141294db+8400 8401 2801 0x1412895f+8401 8402 2800 0x14127ded+8402 8403 2799 0x14127285+8403 8404 2798 0x14126729+8404 8405 2797 0x14125bcc+8405 8406 2796 0x1412507a+8406 8407 2795 0x14124528+8407 8408 2794 0x141239e1+8408 8409 2793 0x14122eaf+8409 8410 2792 0x14122373+8410 8411 2791 0x14121841+8411 8412 2790 0x14120d1a+8412 8413 2789 0x141201f3+8413 8414 2788 0x1411f6d7+8414 8415 2787 0x1411ebc6+8415 8416 2786 0x1411e0b4+8416 8417 2785 0x1411d5ad+8417 8418 2784 0x1411caa7+8418 8419 2783 0x1411bfab+8419 8420 2782 0x1411b4b9+8420 8421 2781 0x1411a9c8+8421 8422 2780 0x14119ee2+8422 8423 2779 0x14119406+8423 8424 2778 0x1411892a+8424 8425 2777 0x14117e63+8425 8426 2776 0x14117392+8426 8427 2775 0x141168cc+8427 8428 2774 0x14115e10+8428 8429 2773 0x14115355+8429 8430 2772 0x141148a4+8430 8431 2771 0x14113dfe+8431 8432 2770 0x14113358+8432 8433 2769 0x141128bc+8433 8434 2768 0x14111e21+8434 8435 2767 0x14111390+8435 8436 2766 0x1411090a+8436 8437 2765 0x1410fe84+8437 8438 2764 0x1410f409+8438 8439 2763 0x1410e98e+8439 8440 2762 0x1410df28+8440 8441 2761 0x1410d4c2+8441 8442 2760 0x1410ca5c+8442 8443 2759 0x1410c001+8443 8444 2758 0x1410b5a6+8444 8445 2757 0x1410ab56+8445 8446 2756 0x1410a110+8446 8447 2755 0x141096cb+8447 8448 2754 0x14108c90+8448 8449 2753 0x14108255+8449 8450 2752 0x14107825+8450 8451 2751 0x14106e00+8451 8452 2750 0x141063da+8452 8453 2749 0x141059c0+8453 8454 2748 0x14104fa5+8454 8455 2747 0x141045a0+8455 8456 2746 0x14103b90+8456 8457 2745 0x1410318b+8457 8458 2744 0x14102790+8458 8459 2743 0x14101d96+8459 8460 2742 0x141013a6+8460 8461 2741 0x141009b7+8461 8462 2740 0x140fffd2+8462 8463 2739 0x140ff5f7+8463 8464 2738 0x140fec1d+8464 8465 2737 0x140fe24e+8465 8466 2736 0x140fd87e+8466 8467 2735 0x140fceb9+8467 8468 2734 0x140fc4f5+8468 8469 2733 0x140fbb3b+8469 8470 2732 0x140fb181+8470 8471 2731 0x140fa7e7+8471 8472 2730 0x140f9e37+8472 8473 2729 0x140f9493+8473 8474 2728 0x140f8af9+8474 8475 2727 0x140f815f+8475 8476 2726 0x140f77d0+8476 8477 2725 0x140f6e4c+8477 8478 2724 0x140f64c7+8478 8479 2723 0x140f5b43+8479 8480 2722 0x140f51c9+8480 8481 2721 0x140f485a+8481 8482 2720 0x140f3eec+8482 8483 2719 0x140f3587+8483 8484 2718 0x140f2c23+8484 8485 2717 0x140f22ca+8485 8486 2716 0x140f197b+8486 8487 2715 0x140f102c+8487 8488 2714 0x140f06de+8488 8489 2713 0x140efd9a+8489 8490 2712 0x140ef460+8490 8491 2711 0x140eeb27+8491 8492 2710 0x140ee1ee+8492 8493 2709 0x140ed8ca+8493 8494 2708 0x140ecf9c+8494 8495 2707 0x140ec678+8495 8496 2706 0x140ebd5f+8496 8497 2705 0x140eb446+8497 8498 2704 0x140eab38+8498 8499 2703 0x140ea229+8499 8500 2702 0x140e9926+8500 8501 2701 0x140e9038+8501 8502 2700 0x140e8734+8502 8503 2699 0x140e7e46+8503 8504 2698 0x140e7558+8504 8505 2697 0x140e6c6a+8505 8506 2696 0x140e6386+8506 8507 2695 0x140e5aae+8507 8508 2694 0x140e51ca+8508 8509 2693 0x140e48fc+8509 8510 2692 0x140e402e+8510 8511 2691 0x140e3760+8511 8512 2690 0x140e2ea8+8512 8513 2689 0x140e25e5+8513 8514 2688 0x140e1d2c+8514 8515 2687 0x140e147f+8515 8516 2686 0x140e0bd1+8516 8517 2685 0x140e032e+8517 8518 2684 0x140dfa8b+8518 8519 2683 0x140df1f3+8519 8520 2682 0x140de95b+8520 8521 2681 0x140de0c2+8521 8522 2680 0x140dd835+8522 8523 2679 0x140dcfb2+8523 8524 2678 0x140dc72f+8524 8525 2677 0x140dbeb7+8525 8526 2676 0x140db63f+8526 8527 2675 0x140dadc7+8527 8528 2674 0x140da55a+8528 8529 2673 0x140d9cf7+8529 8530 2672 0x140d9495+8530 8531 2671 0x140d8c32+8531 8532 2670 0x140d83e5+8532 8533 2669 0x140d7b8d+8533 8534 2668 0x140d7340+8534 8535 2667 0x140d6afe+8535 8536 2666 0x140d62b0+8536 8537 2665 0x140d5a79+8537 8538 2664 0x140d5241+8538 8539 2663 0x140d4a09+8539 8540 2662 0x140d41dd+8540 8541 2661 0x140d39b0+8541 8542 2660 0x140d318d+8542 8543 2659 0x140d296b+8543 8544 2658 0x140d2154+8544 8545 2657 0x140d193c+8545 8546 2656 0x140d1125+8546 8547 2655 0x140d0923+8547 8548 2654 0x140d0121+8548 8549 2653 0x140cf91f+8549 8550 2652 0x140cf11d+8550 8551 2651 0x140ce926+8551 8552 2650 0x140ce12e+8552 8553 2649 0x140cd942+8553 8554 2648 0x140cd155+8554 8555 2647 0x140cc974+8555 8556 2646 0x140cc192+8556 8557 2645 0x140cb9bb+8557 8558 2644 0x140cb1e4+8558 8559 2643 0x140caa17+8559 8560 2642 0x140ca240+8560 8561 2641 0x140c9a7f+8561 8562 2640 0x140c92bd+8562 8563 2639 0x140c8b06+8563 8564 2638 0x140c8350+8564 8565 2637 0x140c7b99+8565 8566 2636 0x140c73e2+8566 8567 2635 0x140c6c40+8567 8568 2634 0x140c6494+8568 8569 2633 0x140c5cf3+8569 8570 2632 0x140c5551+8570 8571 2631 0x140c4dbb+8571 8572 2630 0x140c4624+8572 8573 2629 0x140c3e98+8573 8574 2628 0x140c370c+8574 8575 2627 0x140c2f8b+8575 8576 2626 0x140c280a+8576 8577 2625 0x140c2089+8577 8578 2624 0x140c191d+8578 8579 2623 0x140c11a7+8579 8580 2622 0x140c0a3b+8580 8581 2621 0x140c02cf+8581 8582 2620 0x140bfb63+8582 8583 2619 0x140bf402+8583 8584 2618 0x140beca1+8584 8585 2617 0x140be54b+8585 8586 2616 0x140bddf5+8586 8587 2615 0x140bd6a9+8587 8588 2614 0x140bcf5e+8588 8589 2613 0x140bc812+8589 8590 2612 0x140bc0d2+8590 8591 2611 0x140bb991+8591 8592 2610 0x140bb25b+8592 8593 2609 0x140bab2f+8593 8594 2608 0x140ba3f9+8594 8595 2607 0x140b9cce+8595 8596 2606 0x140b95a3+8596 8597 2605 0x140b8e82+8597 8598 2604 0x140b8761+8598 8599 2603 0x140b8041+8599 8600 2602 0x140b792b+8600 8601 2601 0x140b7215+8601 8602 2600 0x140b6b0a+8602 8603 2599 0x140b63ff+8603 8604 2598 0x140b5cf4+8604 8605 2597 0x140b55f3+8605 8606 2596 0x140b4ef3+8606 8607 2595 0x140b47fd+8607 8608 2594 0x140b4108+8608 8609 2593 0x140b3a1d+8609 8610 2592 0x140b3327+8610 8611 2591 0x140b2c47+8611 8612 2590 0x140b255c+8612 8613 2589 0x140b1e7c+8613 8614 2588 0x140b179c+8614 8615 2587 0x140b10c6+8615 8616 2586 0x140b09f1+8616 8617 2585 0x140b031b+8617 8618 2584 0x140afc50+8618 8619 2583 0x140af586+8619 8620 2582 0x140aeebb+8620 8621 2581 0x140ae7fb+8621 8622 2580 0x140ae13b+8622 8623 2579 0x140ada86+8623 8624 2578 0x140ad3db+8624 8625 2577 0x140acd26+8625 8626 2576 0x140ac67b+8626 8627 2575 0x140abfd1+8627 8628 2574 0x140ab92c+8628 8629 2573 0x140ab291+8629 8630 2572 0x140aabfc+8630 8631 2571 0x140aa56c+8631 8632 2570 0x140a9ee2+8632 8633 2569 0x140a9858+8633 8634 2568 0x140a91d3+8634 8635 2567 0x140a8b53+8635 8636 2566 0x140a84d4+8636 8637 2565 0x140a7e59+8637 8638 2564 0x140a77e4+8638 8639 2563 0x140a717a+8639 8640 2562 0x140a6b0b+8640 8641 2561 0x140a64a1+8641 8642 2560 0x140a5e3c+8642 8643 2559 0x140a57dd+8643 8644 2558 0x140a517d+8644 8645 2557 0x140a4b23+8645 8646 2556 0x140a44c9+8646 8647 2555 0x140a3e7a+8647 8648 2554 0x140a382a+8648 8649 2553 0x140a31db+8649 8650 2552 0x140a2b97+8650 8651 2551 0x140a2552+8651 8652 2550 0x140a1f13+8652 8653 2549 0x140a18d4+8653 8654 2548 0x140a129a+8654 8655 2547 0x140a0c70+8655 8656 2546 0x140a063c+8656 8657 2545 0x140a0012+8657 8658 2544 0x1409f9e8+8658 8659 2543 0x1409f3be+8659 8660 2542 0x1409ed9f+8660 8661 2541 0x1409e780+8661 8662 2540 0x1409e167+8662 8663 2539 0x1409db4d+8663 8664 2538 0x1409d539+8664 8665 2537 0x1409cf2a+8665 8666 2536 0x1409c91b+8666 8667 2535 0x1409c317+8667 8668 2534 0x1409bd0d+8668 8669 2533 0x1409b70e+8669 8670 2532 0x1409b115+8670 8671 2531 0x1409ab1c+8671 8672 2530 0x1409a528+8672 8673 2529 0x14099f33+8673 8674 2528 0x14099945+8674 8675 2527 0x1409935b+8675 8676 2526 0x14098d72+8676 8677 2525 0x1409878e+8677 8678 2524 0x140981aa+8678 8679 2523 0x14097bd1+8679 8680 2522 0x140975f8+8680 8681 2521 0x1409701e+8681 8682 2520 0x14096a4b+8682 8683 2519 0x1409647c+8683 8684 2518 0x14095eb3+8684 8685 2517 0x140958ef+8685 8686 2516 0x1409532b+8686 8687 2515 0x14094d6d+8687 8688 2514 0x140947ae+8688 8689 2513 0x140941f5+8689 8690 2512 0x14093c3c+8690 8691 2511 0x14093689+8691 8692 2510 0x140930da+8692 8693 2509 0x14092b31+8693 8694 2508 0x14092589+8694 8695 2507 0x14091fe0+8695 8696 2506 0x14091a41+8696 8697 2505 0x140914a3+8697 8698 2504 0x14090f05+8698 8699 2503 0x14090971+8699 8700 2502 0x140903de+8700 8701 2501 0x1408fe50+8701 8702 2500 0x1408f8c7+8702 8703 2499 0x1408f339+8703 8704 2498 0x1408edb6+8704 8705 2497 0x1408e832+8705 8706 2496 0x1408e2b4+8706 8707 2495 0x1408dd36+8707 8708 2494 0x1408d7be+8708 8709 2493 0x1408d24a+8709 8710 2492 0x1408ccd7+8710 8711 2491 0x1408c769+8711 8712 2490 0x1408c1fb+8712 8713 2489 0x1408bc98+8713 8714 2488 0x1408b730+8714 8715 2487 0x1408b1d2+8715 8716 2486 0x1408ac79+8716 8717 2485 0x1408a71b+8717 8718 2484 0x1408a1c8+8718 8719 2483 0x14089c70+8719 8720 2482 0x14089722+8720 8721 2481 0x140891d4+8721 8722 2480 0x14088c8c+8722 8723 2479 0x14088744+8723 8724 2478 0x14088201+8724 8725 2477 0x14087cbe+8725 8726 2476 0x14087780+8726 8727 2475 0x14087248+8727 8728 2474 0x14086d10+8728 8729 2473 0x140867dd+8729 8730 2472 0x140862af+8730 8731 2471 0x14085d87+8731 8732 2470 0x1408585f+8732 8733 2469 0x14085337+8733 8734 2468 0x14084e14+8734 8735 2467 0x140848f1+8735 8736 2466 0x140843d4+8736 8737 2465 0x14083ebc+8737 8738 2464 0x140839a4+8738 8739 2463 0x14083491+8739 8740 2462 0x14082f7e+8740 8741 2461 0x14082a71+8741 8742 2460 0x14082569+8742 8743 2459 0x14082061+8743 8744 2458 0x14081b5f+8744 8745 2457 0x1408165c+8745 8746 2456 0x1408115f+8746 8747 2455 0x14080c6c+8747 8748 2454 0x14080775+8748 8749 2453 0x1408027d+8749 8750 2452 0x1407fd90+8750 8751 2451 0x1407f89d+8751 8752 2450 0x1407f3b5+8752 8753 2449 0x1407eece+8753 8754 2448 0x1407e9e6+8754 8755 2447 0x1407e503+8755 8756 2446 0x1407e026+8756 8757 2445 0x1407db49+8757 8758 2444 0x1407d672+8758 8759 2443 0x1407d19a+8759 8760 2442 0x1407ccc8+8760 8761 2441 0x1407c7fb+8761 8762 2440 0x1407c333+8762 8763 2439 0x1407be66+8763 8764 2438 0x1407b99f+8764 8765 2437 0x1407b4dd+8765 8766 2436 0x1407b01b+8766 8767 2435 0x1407ab5e+8767 8768 2434 0x1407a6a1+8768 8769 2433 0x1407a1e9+8769 8770 2432 0x14079d37+8770 8771 2431 0x14079885+8771 8772 2430 0x140793d3+8772 8773 2429 0x14078f26+8773 8774 2428 0x14078a7f+8774 8775 2427 0x140785d8+8775 8776 2426 0x14078136+8776 8777 2425 0x14077c94+8777 8778 2424 0x140777fd+8778 8779 2423 0x14077360+8779 8780 2422 0x14076ec9+8780 8781 2421 0x14076a31+8781 8782 2420 0x1407659f+8782 8783 2419 0x1407610e+8783 8784 2418 0x14075c81+8784 8785 2417 0x140757fa+8785 8786 2416 0x14075373+8786 8787 2415 0x14074eec+8787 8788 2414 0x14074a6f+8788 8789 2413 0x140745ed+8789 8790 2412 0x14074171+8790 8791 2411 0x14073cfa+8791 8792 2410 0x14073883+8792 8793 2409 0x14073417+8793 8794 2408 0x14072fa5+8794 8795 2407 0x14072b39+8795 8796 2406 0x140726cc+8796 8797 2405 0x14072265+8797 8798 2404 0x14071dfe+8798 8799 2403 0x1407199d+8799 8800 2402 0x1407153b+8800 8801 2401 0x140710df+8801 8802 2400 0x14070c83+8802 8803 2399 0x1407082c+8803 8804 2398 0x140703d5+8804 8805 2397 0x1406ff83+8805 8806 2396 0x1406fb32+8806 8807 2395 0x1406f6e6+8807 8808 2394 0x1406f29f+8808 8809 2393 0x1406ee58+8809 8810 2392 0x1406ea12+8810 8811 2391 0x1406e5d0+8811 8812 2390 0x1406e18f+8812 8813 2389 0x1406dd53+8813 8814 2388 0x1406d917+8814 8815 2387 0x1406d4e0+8815 8816 2386 0x1406d0a9+8816 8817 2385 0x1406cc78+8817 8818 2384 0x1406c847+8818 8819 2383 0x1406c41b+8819 8820 2382 0x1406bfef+8820 8821 2381 0x1406bbc8+8821 8822 2380 0x1406b7a2+8822 8823 2379 0x1406b37b+8823 8824 2378 0x1406af60+8824 8825 2377 0x1406ab44+8825 8826 2376 0x1406a728+8826 8827 2375 0x1406a311+8827 8828 2374 0x14069efb+8828 8829 2373 0x14069ae4+8829 8830 2372 0x140696d3+8830 8831 2371 0x140692c2+8831 8832 2370 0x14068eb7+8832 8833 2369 0x14068ab0+8833 8834 2368 0x140686a5+8834 8835 2367 0x140682a4+8835 8836 2366 0x14067e9d+8836 8837 2365 0x14067aa2+8837 8838 2364 0x140676a1+8838 8839 2363 0x140672aa+8839 8840 2362 0x14066eb4+8840 8841 2361 0x14066abe+8841 8842 2360 0x140666c7+8842 8843 2359 0x140662d7+8843 8844 2358 0x14065eeb+8844 8845 2357 0x14065afa+8845 8846 2356 0x14065714+8846 8847 2355 0x1406532e+8847 8848 2354 0x14064f48+8848 8849 2353 0x14064b62+8849 8850 2352 0x14064781+8850 8851 2351 0x140643a5+8851 8852 2350 0x14063fca+8852 8853 2349 0x14063bee+8853 8854 2348 0x1406381e+8854 8855 2347 0x14063448+8855 8856 2346 0x14063077+8856 8857 2345 0x14062ca6+8857 8858 2344 0x140628db+8858 8859 2343 0x14062510+8859 8860 2342 0x14062144+8860 8861 2341 0x14061d7e+8861 8862 2340 0x140619b8+8862 8863 2339 0x140615f8+8863 8864 2338 0x14061237+8864 8865 2337 0x14060e7c+8865 8866 2336 0x14060ac1+8866 8867 2335 0x14060706+8867 8868 2334 0x14060350+8868 8869 2333 0x1405ff9a+8869 8870 2332 0x1405fbef+8870 8871 2331 0x1405f83e+8871 8872 2330 0x1405f493+8872 8873 2329 0x1405f0e3+8873 8874 2328 0x1405ed3d+8874 8875 2327 0x1405e997+8875 8876 2326 0x1405e5f1+8876 8877 2325 0x1405e24b+8877 8878 2324 0x1405deab+8878 8879 2323 0x1405db10+8879 8880 2322 0x1405d775+8880 8881 2321 0x1405d3da+8881 8882 2320 0x1405d03f+8882 8883 2319 0x1405cca9+8883 8884 2318 0x1405c919+8884 8885 2317 0x1405c589+8885 8886 2316 0x1405c1fe+8886 8887 2315 0x1405be6d+8887 8888 2314 0x1405bae3+8888 8889 2313 0x1405b75d+8889 8890 2312 0x1405b3d2+8890 8891 2311 0x1405b052+8891 8892 2310 0x1405accc+8892 8893 2309 0x1405a94c+8893 8894 2308 0x1405a5d1+8894 8895 2307 0x1405a251+8895 8896 2306 0x14059ed6+8896 8897 2305 0x14059b61+8897 8898 2304 0x140597eb+8898 8899 2303 0x14059476+8899 8900 2302 0x14059106+8900 8901 2301 0x14058d9b+8901 8902 2300 0x14058a2b+8902 8903 2299 0x140586c0+8903 8904 2298 0x14058355+8904 8905 2297 0x14057feb+8905 8906 2296 0x14057c85+8906 8907 2295 0x14057925+8907 8908 2294 0x140575c0+8908 8909 2293 0x14057260+8909 8910 2292 0x14056f05+8910 8911 2291 0x14056ba5+8911 8912 2290 0x1405684b+8912 8913 2289 0x140564f5+8913 8914 2288 0x140561a0+8914 8915 2287 0x14055e4b+8915 8916 2286 0x14055b00+8916 8917 2285 0x140557b6+8917 8918 2284 0x1405546b+8918 8919 2283 0x14055126+8919 8920 2282 0x14054de1+8920 8921 2281 0x14054a9c+8921 8922 2280 0x14054759+8922 8923 2279 0x1405441c+8923 8924 2278 0x140540dc+8924 8925 2277 0x14053da2+8925 8926 2276 0x14053a68+8926 8927 2275 0x14053730+8927 8928 2274 0x140533fb+8928 8929 2273 0x140530c6+8929 8930 2272 0x14052d93+8930 8931 2271 0x14052a66+8931 8932 2270 0x14052739+8932 8933 2269 0x1405240c+8933 8934 2268 0x140520e2+8934 8935 2267 0x14051dba+8935 8936 2266 0x14051a93+8936 8937 2265 0x1405176e+8937 8938 2264 0x1405144b+8938 8939 2263 0x14051129+8939 8940 2262 0x14050e09+8940 8941 2261 0x14050aed+8941 8942 2260 0x140507d2+8942 8943 2259 0x140504b8+8943 8944 2258 0x140501a1+8944 8945 2257 0x1404fe8c+8945 8946 2256 0x1404fb79+8946 8947 2255 0x1404f867+8947 8948 2254 0x1404f558+8948 8949 2253 0x1404f248+8949 8950 2252 0x1404ef3e+8950 8951 2251 0x1404ec31+8951 8952 2250 0x1404e92a+8952 8953 2249 0x1404e622+8953 8954 2248 0x1404e31e+8954 8955 2247 0x1404e019+8955 8956 2246 0x1404dd17+8956 8957 2245 0x1404da17+8957 8958 2244 0x1404d71b+8958 8959 2243 0x1404d41e+8959 8960 2242 0x1404d124+8960 8961 2241 0x1404ce2a+8961 8962 2240 0x1404cb38+8962 8963 2239 0x1404c843+8963 8964 2238 0x1404c54f+8964 8965 2237 0x1404c25f+8965 8966 2236 0x1404bf70+8966 8967 2235 0x1404bc81+8967 8968 2234 0x1404b994+8968 8969 2233 0x1404b6aa+8969 8970 2232 0x1404b3c3+8970 8971 2231 0x1404b0dc+8971 8972 2230 0x1404adf7+8972 8973 2229 0x1404ab12+8973 8974 2228 0x1404a833+8974 8975 2227 0x1404a554+8975 8976 2226 0x1404a275+8976 8977 2225 0x14049f9b+8977 8978 2224 0x14049cc1+8978 8979 2223 0x140499ea+8979 8980 2222 0x14049713+8980 8981 2221 0x1404943c+8981 8982 2220 0x1404916a+8982 8983 2219 0x14048e98+8983 8984 2218 0x14048bc9+8984 8985 2217 0x140488fa+8985 8986 2216 0x1404862d+8986 8987 2215 0x14048361+8987 8988 2214 0x1404809a+8988 8989 2213 0x14047dd3+8989 8990 2212 0x14047b0c+8990 8991 2211 0x14047848+8991 8992 2210 0x14047588+8992 8993 2209 0x140472c9+8993 8994 2208 0x1404700a+8994 8995 2207 0x14046d4b+8995 8996 2206 0x14046a92+8996 8997 2205 0x140467d8+8997 8998 2204 0x1404651f+8998 8999 2203 0x14046268+8999 9000 2202 0x14045fb3+9000 9001 2201 0x14045d02+9001 9002 2200 0x14045a50+9002 9003 2199 0x1404579f+9003 9004 2198 0x140454f0+9004 9005 2197 0x14045243+9005 9006 2196 0x14044f9a+9006 9007 2195 0x14044cf0+9007 9008 2194 0x14044a4c+9008 9009 2193 0x140447a5+9009 9010 2192 0x14044501+9010 9011 2191 0x14044260+9011 9012 2190 0x14043fbe+9012 9013 2189 0x14043d1f+9013 9014 2188 0x14043a81+9014 9015 2187 0x140437e4+9015 9016 2186 0x1404354b+9016 9017 2185 0x140432b2+9017 9018 2184 0x1404301b+9018 9019 2183 0x14042d84+9019 9020 2182 0x14042af0+9020 9021 2181 0x1404285c+9021 9022 2180 0x140425cd+9022 9023 2179 0x1404233e+9023 9024 2178 0x140420b2+9024 9025 2177 0x14041e26+9025 9026 2176 0x14041b9a+9026 9027 2175 0x14041911+9027 9028 2174 0x1404168a+9028 9029 2173 0x14041404+9029 9030 2172 0x14041180+9030 9031 2171 0x14040efe+9031 9032 2170 0x14040c7d+9032 9033 2169 0x140409fc+9033 9034 2168 0x14040780+9034 9035 2167 0x14040501+9035 9036 2166 0x14040288+9036 9037 2165 0x1404000f+9037 9038 2164 0x1403fd98+9038 9039 2163 0x1403fb22+9039 9040 2162 0x1403f8ae+9040 9041 2161 0x1403f63a+9041 9042 2160 0x1403f3c9+9042 9043 2159 0x1403f158+9043 9044 2158 0x1403eee9+9044 9045 2157 0x1403ec7d+9045 9046 2156 0x1403ea11+9046 9047 2155 0x1403e7a6+9047 9048 2154 0x1403e53d+9048 9049 2153 0x1403e2d6+9049 9050 2152 0x1403e070+9050 9051 2151 0x1403de0c+9051 9052 2150 0x1403dbab+9052 9053 2149 0x1403d94a+9053 9054 2148 0x1403d6eb+9054 9055 2147 0x1403d48d+9055 9056 2146 0x1403d231+9056 9057 2145 0x1403cfd5+9057 9058 2144 0x1403cd7a+9058 9059 2143 0x1403cb23+9059 9060 2142 0x1403c8ca+9060 9061 2141 0x1403c677+9061 9062 2140 0x1403c423+9062 9063 2139 0x1403c1cf+9063 9064 2138 0x1403bf7e+9064 9065 2137 0x1403bd30+9065 9066 2136 0x1403bae2+9066 9067 2135 0x1403b893+9067 9068 2134 0x1403b648+9068 9069 2133 0x1403b401+9069 9070 2132 0x1403b1b8+9070 9071 2131 0x1403af72+9071 9072 2130 0x1403ad2c+9072 9073 2129 0x1403aae8+9073 9074 2128 0x1403a8a5+9074 9075 2127 0x1403a664+9075 9076 2126 0x1403a423+9076 9077 2125 0x1403a1e5+9077 9078 2124 0x14039fa7+9078 9079 2123 0x14039d6b+9079 9080 2122 0x14039b2f+9080 9081 2121 0x140398f7+9081 9082 2120 0x140396c0+9082 9083 2119 0x1403948a+9083 9084 2118 0x14039257+9084 9085 2117 0x14039023+9085 9086 2116 0x14038df3+9086 9087 2115 0x14038bc2+9087 9088 2114 0x14038991+9088 9089 2113 0x14038763+9089 9090 2112 0x14038537+9090 9091 2111 0x1403830c+9091 9092 2110 0x140380e1+9092 9093 2109 0x14037eb8+9093 9094 2108 0x14037c92+9094 9095 2107 0x14037a6c+9095 9096 2106 0x14037846+9096 9097 2105 0x14037622+9097 9098 2104 0x14037402+9098 9099 2103 0x140371e1+9099 9100 2102 0x14036fc3+9100 9101 2101 0x14036da5+9101 9102 2100 0x14036b8a+9102 9103 2099 0x1403696e+9103 9104 2098 0x14036753+9104 9105 2097 0x1403653a+9105 9106 2096 0x14036324+9106 9107 2095 0x1403610e+9107 9108 2094 0x14035ef8+9108 9109 2093 0x14035ce5+9109 9110 2092 0x14035ad2+9110 9111 2091 0x140358c1+9111 9112 2090 0x140356b3+9112 9113 2089 0x140354a3+9113 9114 2088 0x14035297+9114 9115 2087 0x1403508c+9115 9116 2086 0x14034e81+9116 9117 2085 0x14034c78+9117 9118 2084 0x14034a70+9118 9119 2083 0x1403486a+9119 9120 2082 0x14034664+9120 9121 2081 0x14034461+9121 9122 2080 0x1403425e+9122 9123 2079 0x1403405b+9123 9124 2078 0x14033e5a+9124 9125 2077 0x14033c5c+9125 9126 2076 0x14033a5c+9126 9127 2075 0x14033861+9127 9128 2074 0x14033666+9128 9129 2073 0x1403346a+9129 9130 2072 0x14033272+9130 9131 2071 0x1403307c+9131 9132 2070 0x14032e84+9132 9133 2069 0x14032c8e+9133 9134 2068 0x14032a9b+9134 9135 2067 0x140328a8+9135 9136 2066 0x140326b5+9136 9137 2065 0x140324c4+9137 9138 2064 0x140322d4+9138 9139 2063 0x140320e6+9139 9140 2062 0x14031ef9+9140 9141 2061 0x14031d0e+9141 9142 2060 0x14031b23+9142 9143 2059 0x14031937+9143 9144 2058 0x1403174f+9144 9145 2057 0x14031569+9145 9146 2056 0x14031384+9146 9147 2055 0x1403119e+9147 9148 2054 0x14030fbb+9148 9149 2053 0x14030dd8+9149 9150 2052 0x14030bf8+9150 9151 2051 0x14030a18+9151 9152 2050 0x14030837+9152 9153 2049 0x1403065a+9153 9154 2048 0x1403047c+9154 9155 2047 0x140302a1+9155 9156 2046 0x140300c6+9156 9157 2045 0x1402feeb+9157 9158 2044 0x1402fd13+9158 9159 2043 0x1402fb3b+9159 9160 2042 0x1402f965+9160 9161 2041 0x1402f792+9161 9162 2040 0x1402f5bf+9162 9163 2039 0x1402f3ea+9163 9164 2038 0x1402f21a+9164 9165 2037 0x1402f049+9165 9166 2036 0x1402ee79+9166 9167 2035 0x1402eca9+9167 9168 2034 0x1402eadc+9168 9169 2033 0x1402e911+9169 9170 2032 0x1402e746+9170 9171 2031 0x1402e57b+9171 9172 2030 0x1402e3b0+9172 9173 2029 0x1402e1e8+9173 9174 2028 0x1402e023+9174 9175 2027 0x1402de5d+9175 9176 2026 0x1402dc9a+9176 9177 2025 0x1402dad5+9177 9178 2024 0x1402d912+9178 9179 2023 0x1402d752+9179 9180 2022 0x1402d58f+9180 9181 2021 0x1402d3d2+9181 9182 2020 0x1402d212+9182 9183 2019 0x1402d054+9183 9184 2018 0x1402ce97+9184 9185 2017 0x1402ccdc+9185 9186 2016 0x1402cb21+9186 9187 2015 0x1402c967+9187 9188 2014 0x1402c7af+9188 9189 2013 0x1402c5f9+9189 9190 2012 0x1402c441+9190 9191 2011 0x1402c28c+9191 9192 2010 0x1402c0d9+9192 9193 2009 0x1402bf27+9193 9194 2008 0x1402bd74+9194 9195 2007 0x1402bbc1+9195 9196 2006 0x1402ba11+9196 9197 2005 0x1402b861+9197 9198 2004 0x1402b6b4+9198 9199 2003 0x1402b507+9199 9200 2002 0x1402b359+9200 9201 2001 0x1402b1af+9201 9202 2000 0x1402b004+9202 9203 1999 0x1402ae5b+9203 9204 1998 0x1402acb5+9204 9205 1997 0x1402ab10+9205 9206 1996 0x1402a96b+9206 9207 1995 0x1402a7ca+9207 9208 1994 0x1402a628+9208 9209 1993 0x1402a487+9209 9210 1992 0x1402a2e6+9210 9211 1991 0x1402a147+9211 9212 1990 0x14029fa9+9212 9213 1989 0x14029e0c+9213 9214 1988 0x14029c6e+9214 9215 1987 0x14029ad4+9215 9216 1986 0x14029939+9216 9217 1985 0x1402979f+9217 9218 1984 0x14029607+9218 9219 1983 0x1402946f+9219 9220 1982 0x140292d8+9220 9221 1981 0x14029142+9221 9222 1980 0x14028fb0+9222 9223 1979 0x14028e1c+9223 9224 1978 0x14028c89+9224 9225 1977 0x14028af7+9225 9226 1976 0x14028966+9226 9227 1975 0x140287d6+9227 9228 1974 0x14028646+9228 9229 1973 0x140284b8+9229 9230 1972 0x1402832a+9230 9231 1971 0x1402819f+9231 9232 1970 0x14028013+9232 9233 1969 0x14027e88+9233 9234 1968 0x14027cff+9234 9235 1967 0x14027b76+9235 9236 1966 0x140279ee+9236 9237 1965 0x14027868+9237 9238 1964 0x140276e4+9238 9239 1963 0x1402755f+9239 9240 1962 0x140273dc+9240 9241 1961 0x14027258+9241 9242 1960 0x140270d5+9242 9243 1959 0x14026f54+9243 9244 1958 0x14026dd5+9244 9245 1957 0x14026c55+9245 9246 1956 0x14026ad7+9246 9247 1955 0x14026958+9247 9248 1954 0x140267db+9248 9249 1953 0x14026660+9249 9250 1952 0x140264e5+9250 9251 1951 0x1402636b+9251 9252 1950 0x140261f2+9252 9253 1949 0x1402607c+9253 9254 1948 0x14025f04+9254 9255 1947 0x14025d8e+9255 9256 1946 0x14025c17+9256 9257 1945 0x14025aa4+9257 9258 1944 0x1402592f+9258 9259 1943 0x140257bc+9259 9260 1942 0x1402564a+9260 9261 1941 0x140254d9+9261 9262 1940 0x1402536a+9262 9263 1939 0x140251fa+9263 9264 1938 0x1402508a+9264 9265 1937 0x14024f1d+9265 9266 1936 0x14024db1+9266 9267 1935 0x14024c45+9267 9268 1934 0x14024adb+9268 9269 1933 0x14024971+9269 9270 1932 0x14024808+9270 9271 1931 0x140246a0+9271 9272 1930 0x14024539+9272 9273 1929 0x140243d1+9273 9274 1928 0x1402426b+9274 9275 1927 0x14024106+9275 9276 1926 0x14023fa2+9276 9277 1925 0x14023e3f+9277 9278 1924 0x14023cdd+9278 9279 1923 0x14023b7a+9279 9280 1922 0x14023a1a+9280 9281 1921 0x140238ba+9281 9282 1920 0x1402375c+9282 9283 1919 0x140235fc+9283 9284 1918 0x140234a1+9284 9285 1917 0x14023344+9285 9286 1916 0x140231e9+9286 9287 1915 0x1402308f+9287 9288 1914 0x14022f34+9288 9289 1913 0x14022ddc+9289 9290 1912 0x14022c83+9290 9291 1911 0x14022b2b+9291 9292 1910 0x140229d4+9292 9293 1909 0x1402287f+9293 9294 1908 0x14022729+9294 9295 1907 0x140225d6+9295 9296 1906 0x14022482+9296 9297 1905 0x14022330+9297 9298 1904 0x140221de+9298 9299 1903 0x1402208f+9299 9300 1902 0x14021f3e+9300 9301 1901 0x14021dee+9301 9302 1900 0x14021ca0+9302 9303 1899 0x14021b52+9303 9304 1898 0x14021a06+9304 9305 1897 0x140218ba+9305 9306 1896 0x1402176f+9306 9307 1895 0x14021623+9307 9308 1894 0x140214db+9308 9309 1893 0x14021391+9309 9310 1892 0x14021249+9310 9311 1891 0x14021102+9311 9312 1890 0x14020fbc+9312 9313 1889 0x14020e76+9313 9314 1888 0x14020d33+9314 9315 1887 0x14020bee+9315 9316 1886 0x14020aab+9316 9317 1885 0x14020969+9317 9318 1884 0x14020827+9318 9319 1883 0x140206e5+9319 9320 1882 0x140205a5+9320 9321 1881 0x14020466+9321 9322 1880 0x14020327+9322 9323 1879 0x140201e9+9323 9324 1878 0x140200ac+9324 9325 1877 0x1401ff70+9325 9326 1876 0x1401fe34+9326 9327 1875 0x1401fcf9+9327 9328 1874 0x1401fbbf+9328 9329 1873 0x1401fa85+9329 9330 1872 0x1401f94f+9330 9331 1871 0x1401f817+9331 9332 1870 0x1401f6df+9332 9333 1869 0x1401f5a9+9333 9334 1868 0x1401f474+9334 9335 1867 0x1401f33f+9335 9336 1866 0x1401f20c+9336 9337 1865 0x1401f0d9+9337 9338 1864 0x1401efa5+9338 9339 1863 0x1401ee73+9339 9340 1862 0x1401ed43+9340 9341 1861 0x1401ec12+9341 9342 1860 0x1401eae3+9342 9343 1859 0x1401e9b4+9343 9344 1858 0x1401e886+9344 9345 1857 0x1401e75a+9345 9346 1856 0x1401e62d+9346 9347 1855 0x1401e502+9347 9348 1854 0x1401e3d7+9348 9349 1853 0x1401e2ad+9349 9350 1852 0x1401e183+9350 9351 1851 0x1401e059+9351 9352 1850 0x1401df32+9352 9353 1849 0x1401de09+9353 9354 1848 0x1401dce4+9354 9355 1847 0x1401dbbd+9355 9356 1846 0x1401da98+9356 9357 1845 0x1401d974+9357 9358 1844 0x1401d84f+9358 9359 1843 0x1401d72c+9359 9360 1842 0x1401d60a+9360 9361 1841 0x1401d4e8+9361 9362 1840 0x1401d3c8+9362 9363 1839 0x1401d2a7+9363 9364 1838 0x1401d188+9364 9365 1837 0x1401d069+9365 9366 1836 0x1401cf4a+9366 9367 1835 0x1401ce2c+9367 9368 1834 0x1401cd10+9368 9369 1833 0x1401cbf4+9369 9370 1832 0x1401cad8+9370 9371 1831 0x1401c9bd+9371 9372 1830 0x1401c8a4+9372 9373 1829 0x1401c78a+9373 9374 1828 0x1401c672+9374 9375 1827 0x1401c559+9375 9376 1826 0x1401c443+9376 9377 1825 0x1401c32c+9377 9378 1824 0x1401c217+9378 9379 1823 0x1401c101+9379 9380 1822 0x1401bfed+9380 9381 1821 0x1401bed8+9381 9382 1820 0x1401bdc5+9382 9383 1819 0x1401bcb2+9383 9384 1818 0x1401bba0+9384 9385 1817 0x1401ba8f+9385 9386 1816 0x1401b97e+9386 9387 1815 0x1401b86e+9387 9388 1814 0x1401b75f+9388 9389 1813 0x1401b650+9389 9390 1812 0x1401b542+9390 9391 1811 0x1401b436+9391 9392 1810 0x1401b32a+9392 9393 1809 0x1401b21d+9393 9394 1808 0x1401b112+9394 9395 1807 0x1401b007+9395 9396 1806 0x1401aefc+9396 9397 1805 0x1401adf3+9397 9398 1804 0x1401aceb+9398 9399 1803 0x1401abe2+9399 9400 1802 0x1401aadb+9400 9401 1801 0x1401a9d4+9401 9402 1800 0x1401a8cd+9402 9403 1799 0x1401a7c8+9403 9404 1798 0x1401a6c3+9404 9405 1797 0x1401a5bf+9405 9406 1796 0x1401a4bc+9406 9407 1795 0x1401a3b9+9407 9408 1794 0x1401a2b6+9408 9409 1793 0x1401a1b5+9409 9410 1792 0x1401a0b3+9410 9411 1791 0x14019fb3+9411 9412 1790 0x14019eb3+9412 9413 1789 0x14019db4+9413 9414 1788 0x14019cb5+9414 9415 1787 0x14019bb6+9415 9416 1786 0x14019ab8+9416 9417 1785 0x140199bc+9417 9418 1784 0x140198c0+9418 9419 1783 0x140197c4+9419 9420 1782 0x140196c9+9420 9421 1781 0x140195ce+9421 9422 1780 0x140194d6+9422 9423 1779 0x140193dc+9423 9424 1778 0x140192e3+9424 9425 1777 0x140191ec+9425 9426 1776 0x140190f4+9426 9427 1775 0x14018ffd+9427 9428 1774 0x14018f06+9428 9429 1773 0x14018e10+9429 9430 1772 0x14018d1b+9430 9431 1771 0x14018c27+9431 9432 1770 0x14018b33+9432 9433 1769 0x14018a40+9433 9434 1768 0x1401894d+9434 9435 1767 0x1401885a+9435 9436 1766 0x14018769+9436 9437 1765 0x14018678+9437 9438 1764 0x14018588+9438 9439 1763 0x14018498+9439 9440 1762 0x140183a9+9440 9441 1761 0x140182bb+9441 9442 1760 0x140181cc+9442 9443 1759 0x140180de+9443 9444 1758 0x14017ff1+9444 9445 1757 0x14017f05+9445 9446 1756 0x14017e19+9446 9447 1755 0x14017d2c+9447 9448 1754 0x14017c42+9448 9449 1753 0x14017b58+9449 9450 1752 0x14017a6d+9450 9451 1751 0x14017985+9451 9452 1750 0x1401789c+9452 9453 1749 0x140177b5+9453 9454 1748 0x140176cd+9454 9455 1747 0x140175e6+9455 9456 1746 0x14017500+9456 9457 1745 0x1401741a+9457 9458 1744 0x14017335+9458 9459 1743 0x1401724f+9459 9460 1742 0x1401716b+9460 9461 1741 0x14017087+9461 9462 1740 0x14016fa4+9462 9463 1739 0x14016ec2+9463 9464 1738 0x14016ddf+9464 9465 1737 0x14016cfe+9465 9466 1736 0x14016c1c+9466 9467 1735 0x14016b3c+9467 9468 1734 0x14016a5d+9468 9469 1733 0x1401697d+9469 9470 1732 0x1401689f+9470 9471 1731 0x140167c0+9471 9472 1730 0x140166e1+9472 9473 1729 0x14016604+9473 9474 1728 0x14016526+9474 9475 1727 0x1401644a+9475 9476 1726 0x1401636e+9476 9477 1725 0x14016292+9477 9478 1724 0x140161b8+9478 9479 1723 0x140160dd+9479 9480 1722 0x14016004+9480 9481 1721 0x14015f2a+9481 9482 1720 0x14015e51+9482 9483 1719 0x14015d79+9483 9484 1718 0x14015ca1+9484 9485 1717 0x14015bca+9485 9486 1716 0x14015af2+9486 9487 1715 0x14015a1c+9487 9488 1714 0x14015946+9488 9489 1713 0x14015871+9489 9490 1712 0x1401579c+9490 9491 1711 0x140156c8+9491 9492 1710 0x140155f5+9492 9493 1709 0x14015523+9493 9494 1708 0x14015451+9494 9495 1707 0x14015380+9495 9496 1706 0x140152af+9496 9497 1705 0x140151df+9497 9498 1704 0x1401510f+9498 9499 1703 0x14015040+9499 9500 1702 0x14014f71+9500 9501 1701 0x14014ea3+9501 9502 1700 0x14014dd5+9502 9503 1699 0x14014d07+9503 9504 1698 0x14014c3a+9504 9505 1697 0x14014b6e+9505 9506 1696 0x14014aa1+9506 9507 1695 0x140149d6+9507 9508 1694 0x1401490b+9508 9509 1693 0x14014840+9509 9510 1692 0x14014776+9510 9511 1691 0x140146ac+9511 9512 1690 0x140145e3+9512 9513 1689 0x1401451b+9513 9514 1688 0x14014453+9514 9515 1687 0x1401438b+9515 9516 1686 0x140142c3+9516 9517 1685 0x140141fd+9517 9518 1684 0x14014136+9518 9519 1683 0x14014070+9519 9520 1682 0x14013fab+9520 9521 1681 0x14013ee6+9521 9522 1680 0x14013e21+9522 9523 1679 0x14013d5e+9523 9524 1678 0x14013c9a+9524 9525 1677 0x14013bd7+9525 9526 1676 0x14013b14+9526 9527 1675 0x14013a51+9527 9528 1674 0x14013990+9528 9529 1673 0x140138cf+9529 9530 1672 0x1401380f+9530 9531 1671 0x1401374e+9531 9532 1670 0x1401368e+9532 9533 1669 0x140135ce+9533 9534 1668 0x1401350f+9534 9535 1667 0x14013451+9535 9536 1666 0x14013393+9536 9537 1665 0x140132d5+9537 9538 1664 0x14013218+9538 9539 1663 0x1401315b+9539 9540 1662 0x1401309f+9540 9541 1661 0x14012fe3+9541 9542 1660 0x14012f27+9542 9543 1659 0x14012e6c+9543 9544 1658 0x14012db2+9544 9545 1657 0x14012cf8+9545 9546 1656 0x14012c3f+9546 9547 1655 0x14012b85+9547 9548 1654 0x14012acd+9548 9549 1653 0x14012a14+9549 9550 1652 0x1401295d+9550 9551 1651 0x140128a5+9551 9552 1650 0x140127ee+9552 9553 1649 0x14012737+9553 9554 1648 0x14012681+9554 9555 1647 0x140125cb+9555 9556 1646 0x14012516+9556 9557 1645 0x14012461+9557 9558 1644 0x140123ad+9558 9559 1643 0x140122f9+9559 9560 1642 0x14012246+9560 9561 1641 0x14012193+9561 9562 1640 0x140120e0+9562 9563 1639 0x1401202d+9563 9564 1638 0x14011f7c+9564 9565 1637 0x14011ecb+9565 9566 1636 0x14011e1a+9566 9567 1635 0x14011d69+9567 9568 1634 0x14011cb9+9568 9569 1633 0x14011c09+9569 9570 1632 0x14011b5a+9570 9571 1631 0x14011aab+9571 9572 1630 0x140119fc+9572 9573 1629 0x1401194e+9573 9574 1628 0x140118a0+9574 9575 1627 0x140117f4+9575 9576 1626 0x14011747+9576 9577 1625 0x1401169b+9577 9578 1624 0x140115ef+9578 9579 1623 0x14011544+9579 9580 1622 0x14011499+9580 9581 1621 0x140113ed+9581 9582 1620 0x14011343+9582 9583 1619 0x14011299+9583 9584 1618 0x140111f0+9584 9585 1617 0x14011147+9585 9586 1616 0x1401109e+9586 9587 1615 0x14010ff6+9587 9588 1614 0x14010f4e+9588 9589 1613 0x14010ea6+9589 9590 1612 0x14010dff+9590 9591 1611 0x14010d59+9591 9592 1610 0x14010cb3+9592 9593 1609 0x14010c0d+9593 9594 1608 0x14010b68+9594 9595 1607 0x14010ac3+9595 9596 1606 0x14010a1e+9596 9597 1605 0x1401097a+9597 9598 1604 0x140108d6+9598 9599 1603 0x14010832+9599 9600 1602 0x1401078f+9600 9601 1601 0x140106ed+9601 9602 1600 0x1401064b+9602 9603 1599 0x140105a9+9603 9604 1598 0x14010507+9604 9605 1597 0x14010466+9605 9606 1596 0x140103c6+9606 9607 1595 0x14010326+9607 9608 1594 0x14010286+9608 9609 1593 0x140101e6+9609 9610 1592 0x14010147+9610 9611 1591 0x140100a8+9611 9612 1590 0x1401000a+9612 9613 1589 0x1400ff6c+9613 9614 1588 0x1400fece+9614 9615 1587 0x1400fe31+9615 9616 1586 0x1400fd94+9616 9617 1585 0x1400fcf8+9617 9618 1584 0x1400fc5c+9618 9619 1583 0x1400fbc0+9619 9620 1582 0x1400fb25+9620 9621 1581 0x1400fa8b+9621 9622 1580 0x1400f9f0+9622 9623 1579 0x1400f955+9623 9624 1578 0x1400f8bc+9624 9625 1577 0x1400f822+9625 9626 1576 0x1400f789+9626 9627 1575 0x1400f6f1+9627 9628 1574 0x1400f658+9628 9629 1573 0x1400f5c0+9629 9630 1572 0x1400f529+9630 9631 1571 0x1400f492+9631 9632 1570 0x1400f3fb+9632 9633 1569 0x1400f364+9633 9634 1568 0x1400f2ce+9634 9635 1567 0x1400f238+9635 9636 1566 0x1400f1a3+9636 9637 1565 0x1400f10e+9637 9638 1564 0x1400f07a+9638 9639 1563 0x1400efe6+9639 9640 1562 0x1400ef52+9640 9641 1561 0x1400eebe+9641 9642 1560 0x1400ee2b+9642 9643 1559 0x1400ed98+9643 9644 1558 0x1400ed05+9644 9645 1557 0x1400ec74+9645 9646 1556 0x1400ebe2+9646 9647 1555 0x1400eb50+9647 9648 1554 0x1400eabf+9648 9649 1553 0x1400ea2e+9649 9650 1552 0x1400e99e+9650 9651 1551 0x1400e90e+9651 9652 1550 0x1400e87f+9652 9653 1549 0x1400e7f0+9653 9654 1548 0x1400e760+9654 9655 1547 0x1400e6d2+9655 9656 1546 0x1400e644+9656 9657 1545 0x1400e5b6+9657 9658 1544 0x1400e528+9658 9659 1543 0x1400e49b+9659 9660 1542 0x1400e40e+9660 9661 1541 0x1400e382+9661 9662 1540 0x1400e2f5+9662 9663 1539 0x1400e269+9663 9664 1538 0x1400e1de+9664 9665 1537 0x1400e153+9665 9666 1536 0x1400e0c8+9666 9667 1535 0x1400e03e+9667 9668 1534 0x1400dfb4+9668 9669 1533 0x1400df2a+9669 9670 1532 0x1400dea1+9670 9671 1531 0x1400de17+9671 9672 1530 0x1400dd8e+9672 9673 1529 0x1400dd06+9673 9674 1528 0x1400dc7e+9674 9675 1527 0x1400dbf6+9675 9676 1526 0x1400db6e+9676 9677 1525 0x1400dae7+9677 9678 1524 0x1400da61+9678 9679 1523 0x1400d9da+9679 9680 1522 0x1400d954+9680 9681 1521 0x1400d8ce+9681 9682 1520 0x1400d849+9682 9683 1519 0x1400d7c4+9683 9684 1518 0x1400d73f+9684 9685 1517 0x1400d6ba+9685 9686 1516 0x1400d636+9686 9687 1515 0x1400d5b2+9687 9688 1514 0x1400d52e+9688 9689 1513 0x1400d4ab+9689 9690 1512 0x1400d428+9690 9691 1511 0x1400d3a5+9691 9692 1510 0x1400d323+9692 9693 1509 0x1400d2a1+9693 9694 1508 0x1400d220+9694 9695 1507 0x1400d19e+9695 9696 1506 0x1400d11d+9696 9697 1505 0x1400d09c+9697 9698 1504 0x1400d01c+9698 9699 1503 0x1400cf9c+9699 9700 1502 0x1400cf1c+9700 9701 1501 0x1400ce9d+9701 9702 1500 0x1400ce1d+9702 9703 1499 0x1400cd9e+9703 9704 1498 0x1400cd20+9704 9705 1497 0x1400cca1+9705 9706 1496 0x1400cc23+9706 9707 1495 0x1400cba6+9707 9708 1494 0x1400cb28+9708 9709 1493 0x1400caab+9709 9710 1492 0x1400ca2e+9710 9711 1491 0x1400c9b2+9711 9712 1490 0x1400c935+9712 9713 1489 0x1400c8ba+9713 9714 1488 0x1400c83f+9714 9715 1487 0x1400c7c3+9715 9716 1486 0x1400c748+9716 9717 1485 0x1400c6ce+9717 9718 1484 0x1400c653+9718 9719 1483 0x1400c5d9+9719 9720 1482 0x1400c55f+9720 9721 1481 0x1400c4e5+9721 9722 1480 0x1400c46c+9722 9723 1479 0x1400c3f3+9723 9724 1478 0x1400c37a+9724 9725 1477 0x1400c302+9725 9726 1476 0x1400c28a+9726 9727 1475 0x1400c212+9727 9728 1474 0x1400c19b+9728 9729 1473 0x1400c124+9729 9730 1472 0x1400c0ad+9730 9731 1471 0x1400c036+9731 9732 1470 0x1400bfc0+9732 9733 1469 0x1400bf4a+9733 9734 1468 0x1400bed4+9734 9735 1467 0x1400be5e+9735 9736 1466 0x1400bde9+9736 9737 1465 0x1400bd74+9737 9738 1464 0x1400bcff+9738 9739 1463 0x1400bc8b+9739 9740 1462 0x1400bc16+9740 9741 1461 0x1400bba3+9741 9742 1460 0x1400bb2f+9742 9743 1459 0x1400babb+9743 9744 1458 0x1400ba49+9744 9745 1457 0x1400b9d6+9745 9746 1456 0x1400b963+9746 9747 1455 0x1400b8f1+9747 9748 1454 0x1400b87f+9748 9749 1453 0x1400b80d+9749 9750 1452 0x1400b79c+9750 9751 1451 0x1400b72a+9751 9752 1450 0x1400b6ba+9752 9753 1449 0x1400b649+9753 9754 1448 0x1400b5d8+9754 9755 1447 0x1400b568+9755 9756 1446 0x1400b4f8+9756 9757 1445 0x1400b489+9757 9758 1444 0x1400b419+9758 9759 1443 0x1400b3ab+9759 9760 1442 0x1400b33b+9760 9761 1441 0x1400b2cd+9761 9762 1440 0x1400b25f+9762 9763 1439 0x1400b1f1+9763 9764 1438 0x1400b183+9764 9765 1437 0x1400b115+9765 9766 1436 0x1400b0a7+9766 9767 1435 0x1400b03b+9767 9768 1434 0x1400afcd+9768 9769 1433 0x1400af61+9769 9770 1432 0x1400aef5+9770 9771 1431 0x1400ae89+9771 9772 1430 0x1400ae1d+9772 9773 1429 0x1400adb1+9773 9774 1428 0x1400ad45+9774 9775 1427 0x1400acdb+9775 9776 1426 0x1400ac70+9776 9777 1425 0x1400ac05+9777 9778 1424 0x1400ab9b+9778 9779 1423 0x1400ab31+9779 9780 1422 0x1400aac8+9780 9781 1421 0x1400aa5f+9781 9782 1420 0x1400a9f6+9782 9783 1419 0x1400a98e+9783 9784 1418 0x1400a925+9784 9785 1417 0x1400a8bd+9785 9786 1416 0x1400a855+9786 9787 1415 0x1400a7ee+9787 9788 1414 0x1400a787+9788 9789 1413 0x1400a71f+9789 9790 1412 0x1400a6b9+9790 9791 1411 0x1400a652+9791 9792 1410 0x1400a5ec+9792 9793 1409 0x1400a586+9793 9794 1408 0x1400a520+9794 9795 1407 0x1400a4ba+9795 9796 1406 0x1400a455+9796 9797 1405 0x1400a3f0+9797 9798 1404 0x1400a38b+9798 9799 1403 0x1400a326+9799 9800 1402 0x1400a2c1+9800 9801 1401 0x1400a25d+9801 9802 1400 0x1400a1f9+9802 9803 1399 0x1400a195+9803 9804 1398 0x1400a132+9804 9805 1397 0x1400a0cf+9805 9806 1396 0x1400a06c+9806 9807 1395 0x1400a009+9807 9808 1394 0x14009fa6+9808 9809 1393 0x14009f44+9809 9810 1392 0x14009ee2+9810 9811 1391 0x14009e80+9811 9812 1390 0x14009e1e+9812 9813 1389 0x14009dbc+9813 9814 1388 0x14009d5b+9814 9815 1387 0x14009cfa+9815 9816 1386 0x14009c99+9816 9817 1385 0x14009c39+9817 9818 1384 0x14009bd8+9818 9819 1383 0x14009b78+9819 9820 1382 0x14009b19+9820 9821 1381 0x14009ab9+9821 9822 1380 0x14009a5a+9822 9823 1379 0x140099fb+9823 9824 1378 0x1400999c+9824 9825 1377 0x1400993d+9825 9826 1376 0x140098de+9826 9827 1375 0x14009880+9827 9828 1374 0x14009822+9828 9829 1373 0x140097c4+9829 9830 1372 0x14009767+9830 9831 1371 0x14009709+9831 9832 1370 0x140096ac+9832 9833 1369 0x1400964f+9833 9834 1368 0x140095f2+9834 9835 1367 0x14009596+9835 9836 1366 0x1400953a+9836 9837 1365 0x140094de+9837 9838 1364 0x14009482+9838 9839 1363 0x14009426+9839 9840 1362 0x140093cb+9840 9841 1361 0x14009370+9841 9842 1360 0x14009315+9842 9843 1359 0x140092ba+9843 9844 1358 0x1400925f+9844 9845 1357 0x14009205+9845 9846 1356 0x140091ab+9846 9847 1355 0x14009151+9847 9848 1354 0x140090f8+9848 9849 1353 0x1400909e+9849 9850 1352 0x14009045+9850 9851 1351 0x14008fec+9851 9852 1350 0x14008f93+9852 9853 1349 0x14008f3a+9853 9854 1348 0x14008ee2+9854 9855 1347 0x14008e8a+9855 9856 1346 0x14008e32+9856 9857 1345 0x14008dda+9857 9858 1344 0x14008d83+9858 9859 1343 0x14008d2b+9859 9860 1342 0x14008cd4+9860 9861 1341 0x14008c7d+9861 9862 1340 0x14008c27+9862 9863 1339 0x14008bd0+9863 9864 1338 0x14008b7a+9864 9865 1337 0x14008b24+9865 9866 1336 0x14008ace+9866 9867 1335 0x14008a79+9867 9868 1334 0x14008a23+9868 9869 1333 0x140089ce+9869 9870 1332 0x14008979+9870 9871 1331 0x14008924+9871 9872 1330 0x140088cf+9872 9873 1329 0x1400887b+9873 9874 1328 0x14008827+9874 9875 1327 0x140087d2+9875 9876 1326 0x1400877f+9876 9877 1325 0x1400872b+9877 9878 1324 0x140086d8+9878 9879 1323 0x14008684+9879 9880 1322 0x14008631+9880 9881 1321 0x140085de+9881 9882 1320 0x1400858c+9882 9883 1319 0x1400853a+9883 9884 1318 0x140084e7+9884 9885 1317 0x14008496+9885 9886 1316 0x14008444+9886 9887 1315 0x140083f2+9887 9888 1314 0x140083a0+9888 9889 1313 0x1400834f+9889 9890 1312 0x140082fe+9890 9891 1311 0x140082ad+9891 9892 1310 0x1400825d+9892 9893 1309 0x1400820c+9893 9894 1308 0x140081bc+9894 9895 1307 0x1400816c+9895 9896 1306 0x1400811c+9896 9897 1305 0x140080cd+9897 9898 1304 0x1400807e+9898 9899 1303 0x1400802e+9899 9900 1302 0x137fdf60+9900 9901 1301 0x137f908e+9901 9902 1300 0x137f41bc+9902 9903 1299 0x137ef340+9903 9904 1298 0x137ea51a+9904 9905 1297 0x137e56f4+9905 9906 1296 0x137e08ce+9906 9907 1295 0x137dbafd+9907 9908 1294 0x137d6d83+9908 9909 1293 0x137d2008+9909 9910 1292 0x137cd2e4+9910 9911 1291 0x137c8615+9911 9912 1290 0x137c3947+9912 9913 1289 0x137becce+9913 9914 1288 0x137ba055+9914 9915 1287 0x137b5432+9915 9916 1286 0x137b080f+9916 9917 1285 0x137abc42+9917 9918 1284 0x137a7075+9918 9919 1283 0x137a24fd+9919 9920 1282 0x1379d986+9920 9921 1281 0x13798e65+9921 9922 1280 0x13794399+9922 9923 1279 0x1378f8ce+9923 9924 1278 0x1378ae02+9924 9925 1277 0x1378638d+9925 9926 1276 0x1378196d+9926 9927 1275 0x1377cf4d+9927 9928 1274 0x137785d9+9928 9929 1273 0x13773c0f+9929 9930 1272 0x1376f29b+9930 9931 1271 0x1376a927+9931 9932 1270 0x13766009+9932 9933 1269 0x13761740+9933 9934 1268 0x1375ce22+9934 9935 1267 0x137585b0+9935 9936 1266 0x13753d3d+9936 9937 1265 0x1374f4cb+9937 9938 1264 0x1374acae+9938 9939 1263 0x137464e8+9939 9940 1262 0x13741d21+9940 9941 1261 0x1373d5b0+9941 9942 1260 0x13738e3f+9942 9943 1259 0x13734724+9943 9944 1258 0x13730009+9944 9945 1257 0x1372b944+9945 9946 1256 0x1372727f+9946 9947 1255 0x13722c0f+9947 9948 1254 0x1371e5a0+9948 9949 1253 0x13719f87+9949 9950 1252 0x1371596d+9950 9951 1251 0x137113aa+9951 9952 1250 0x1370cde6+9952 9953 1249 0x13708878+9953 9954 1248 0x1370430b+9954 9955 1247 0x136ffdf3+9955 9956 1246 0x136fb8db+9956 9957 1245 0x136f7419+9957 9958 1244 0x136f2f57+9958 9959 1243 0x136eeb41+9959 9960 1242 0x136ea6d5+9960 9961 1241 0x136e62bf+9961 9962 1240 0x136e1ea8+9962 9963 1239 0x136ddae8+9963 9964 1238 0x136d9728+9964 9965 1237 0x136d53bd+9965 9966 1236 0x136d1052+9966 9967 1235 0x136ccd3e+9967 9968 1234 0x136c8a29+9968 9969 1233 0x136c4715+9969 9970 1232 0x136c04ac+9970 9971 1231 0x136bc1ed+9971 9972 1230 0x136b7f84+9972 9973 1229 0x136b3d71+9973 9974 1228 0x136afbb4+9974 9975 1227 0x136ab9a1+9975 9976 1226 0x136a77e3+9976 9977 1225 0x136a367c+9977 9978 1224 0x1369f515+9978 9979 1223 0x1369b3ad+9979 9980 1222 0x1369729c+9980 9981 1221 0x1369318a+9981 9982 1220 0x1368f0cf+9982 9983 1219 0x1368b069+9983 9984 1218 0x13686fad+9984 9985 1217 0x13682f9d+9985 9986 1216 0x1367ef38+9986 9987 1215 0x1367af28+9987 9988 1214 0x13676f6e+9988 9989 1213 0x1367300a+9989 9990 1212 0x1366f0a6+9990 9991 1211 0x1366b141+9991 9992 1210 0x136671dd+9992 9993 1209 0x136632cf+9993 9994 1208 0x1365f3c1+9994 9995 1207 0x1365b508+9995 9996 1206 0x13657650+9996 9997 1205 0x136537ed+9997 9998 1204 0x1364f98b+9998 9999 1203 0x1364bb7e+9999 10000 1202 0x13647d71+10000 10001 1201 0x13643f64+
haskoin-core.cabal view
@@ -1,158 +1,200 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.31.2.+-- This file has been generated from package.yaml by hpack version 0.38.0. -- -- see: https://github.com/sol/hpack------ hash: 1ce94eac889a0e1f03131ba2275354d9e3fcffbb001fa4c0cd8e70cff3e8940a  name:           haskoin-core-version:        0.12.0+version:        1.2.2 synopsis:       Bitcoin & Bitcoin Cash library for Haskell-description:    Haskoin Core is a complete Bitcoin and Bitcoin Cash library of functions and data types for Haskell developers.+description:    Please see the README on GitHub at <https://github.com/jprupp/haskoin-core#readme> category:       Bitcoin, Finance, Network-homepage:       http://github.com/haskoin/haskoin#readme-bug-reports:    http://github.com/haskoin/haskoin/issues-author:         Philippe Laprade,-                Jean-Pierre Rupp,-                Matthew Wraith+homepage:       http://github.com/jprupp/haskoin-core#readme+bug-reports:    http://github.com/jprupp/haskoin-core/issues+author:         JP Rupp maintainer:     jprupp@protonmail.ch-license:        PublicDomain-license-file:   UNLICENSE+license:        MIT+license-file:   LICENSE build-type:     Simple extra-source-files:+    data/bip341.json+    data/complex_psbt.json     data/forkid_script_tests.json     data/forkid_sighash.json+    data/key_io_invalid.json+    data/key_io_valid.json+    data/rfc6979cash.json+    data/rfc6979core.json+    data/rfc6979DERcash.json+    data/rfc6979DERcore.json     data/script_tests.json     data/sig_nonstrict.json     data/sig_strict.json     data/sighash.json+    data/test_vectors_aserti3-2d_run01.txt+    data/test_vectors_aserti3-2d_run02.txt+    data/test_vectors_aserti3-2d_run03.txt+    data/test_vectors_aserti3-2d_run04.txt+    data/test_vectors_aserti3-2d_run05.txt+    data/test_vectors_aserti3-2d_run06.txt+    data/test_vectors_aserti3-2d_run07.txt+    data/test_vectors_aserti3-2d_run08.txt+    data/test_vectors_aserti3-2d_run09.txt+    data/test_vectors_aserti3-2d_run10.txt+    data/test_vectors_aserti3-2d_run11.txt+    data/test_vectors_aserti3-2d_run12.txt     README.md     CHANGELOG.md  source-repository head   type: git-  location: git://github.com/haskoin/haskoin.git+  location: git://github.com/jprupp/haskoin-core.git  library   exposed-modules:       Haskoin-      Network.Haskoin.Address-      Network.Haskoin.Address.Base58-      Network.Haskoin.Address.CashAddr-      Network.Haskoin.Address.Bech32-      Network.Haskoin.Block-      Network.Haskoin.Block.Headers-      Network.Haskoin.Block.Merkle-      Network.Haskoin.Constants-      Network.Haskoin.Crypto-      Network.Haskoin.Keys-      Network.Haskoin.Network-      Network.Haskoin.Script-      Network.Haskoin.Test-      Network.Haskoin.Transaction-      Network.Haskoin.Transaction.Partial-      Network.Haskoin.Transaction.Segwit-      Network.Haskoin.Util+      Haskoin.Address+      Haskoin.Address.Base58+      Haskoin.Address.Bech32+      Haskoin.Address.CashAddr+      Haskoin.Block+      Haskoin.Block.Common+      Haskoin.Block.Headers+      Haskoin.Block.Merkle+      Haskoin.Crypto+      Haskoin.Crypto.Hash+      Haskoin.Crypto.Keys+      Haskoin.Crypto.Keys.Common+      Haskoin.Crypto.Keys.Extended+      Haskoin.Crypto.Keys.Mnemonic+      Haskoin.Crypto.Signature+      Haskoin.Network+      Haskoin.Network.Bloom+      Haskoin.Network.Common+      Haskoin.Network.Constants+      Haskoin.Network.Data+      Haskoin.Network.Message+      Haskoin.Script+      Haskoin.Script.Common+      Haskoin.Script.SigHash+      Haskoin.Script.Standard+      Haskoin.Transaction+      Haskoin.Transaction.Builder+      Haskoin.Transaction.Builder.Sign+      Haskoin.Transaction.Common+      Haskoin.Transaction.Genesis+      Haskoin.Transaction.Partial+      Haskoin.Transaction.Segwit+      Haskoin.Transaction.Taproot+      Haskoin.Util+      Haskoin.Util.Arbitrary+      Haskoin.Util.Arbitrary.Address+      Haskoin.Util.Arbitrary.Block+      Haskoin.Util.Arbitrary.Crypto+      Haskoin.Util.Arbitrary.Keys+      Haskoin.Util.Arbitrary.Message+      Haskoin.Util.Arbitrary.Network+      Haskoin.Util.Arbitrary.Script+      Haskoin.Util.Arbitrary.Transaction+      Haskoin.Util.Arbitrary.Util+      Haskoin.Util.Helpers+      Haskoin.Util.Marshal   other-modules:-      Network.Haskoin.Block.Common-      Network.Haskoin.Crypto.Hash-      Network.Haskoin.Crypto.Signature-      Network.Haskoin.Keys.Common-      Network.Haskoin.Keys.Extended-      Network.Haskoin.Keys.Mnemonic-      Network.Haskoin.Network.Bloom-      Network.Haskoin.Network.Common-      Network.Haskoin.Network.Message-      Network.Haskoin.Script.Common-      Network.Haskoin.Script.SigHash-      Network.Haskoin.Script.Standard-      Network.Haskoin.Test.Address-      Network.Haskoin.Test.Block-      Network.Haskoin.Test.Crypto-      Network.Haskoin.Test.Keys-      Network.Haskoin.Test.Message-      Network.Haskoin.Test.Network-      Network.Haskoin.Test.Script-      Network.Haskoin.Test.Transaction-      Network.Haskoin.Test.Util-      Network.Haskoin.Transaction.Builder-      Network.Haskoin.Transaction.Builder.Sign-      Network.Haskoin.Transaction.Common-      Paths_haskoin_core+      Haskoin.Crypto.Keys.Extended.Internal   hs-source-dirs:       src   build-depends:-      HUnit-    , QuickCheck-    , aeson-    , array+      QuickCheck >=2.13.2+    , aeson >=1.4.6.0+    , array >=0.5.4.0     , base >=4.9 && <5-    , base16-bytestring-    , bytestring-    , cereal-    , conduit-    , containers-    , cryptonite-    , deepseq-    , entropy-    , hashable-    , hspec-    , memory-    , mtl+    , base16 >=1.0+    , binary >=0.8.8+    , bytes >=0.17+    , bytestring >=0.10.10.0+    , cereal >=0.5.8+    , conduit >=1.3.1.2+    , containers >=0.6.2.1+    , crypton >=1.0.0+    , data-default >=0.7.1.1+    , deepseq >=1.4.4.0+    , entropy >=0.4.1.5+    , hashable >=1.3.0.0+    , hspec >=2.7.1+    , memory >=0.15.0+    , mtl >=2.2.2     , murmur3 >=1.0.3-    , network-    , safe-    , scientific-    , secp256k1-haskell >=0.2.1-    , split-    , string-conversions-    , text-    , time-    , transformers-    , unordered-containers-    , vector+    , network >=3.1.1.1+    , safe >=0.3.18+    , scientific >=0.3.6.2+    , secp256k1-haskell >=1.2.0+    , split >=0.2.3.3+    , string-conversions >=0.4.0.1+    , text >=1.2.3.0+    , time >=1.9.3+    , transformers >=0.5.6.2+    , unordered-containers >=0.2.10.0+    , vector >=0.12.1.2   default-language: Haskell2010  test-suite spec   type: exitcode-stdio-1.0   main-is: Spec.hs   other-modules:-      Network.Haskoin.Address.Bech32Spec-      Network.Haskoin.Address.CashAddrSpec-      Network.Haskoin.AddressSpec-      Network.Haskoin.BlockSpec-      Network.Haskoin.Crypto.HashSpec-      Network.Haskoin.Crypto.SignatureSpec-      Network.Haskoin.CryptoSpec-      Network.Haskoin.Keys.ExtendedSpec-      Network.Haskoin.Keys.MnemonicSpec-      Network.Haskoin.KeysSpec-      Network.Haskoin.NetworkSpec-      Network.Haskoin.ScriptSpec-      Network.Haskoin.Transaction.PartialSpec-      Network.Haskoin.TransactionSpec-      Network.Haskoin.UtilSpec+      Haskoin.Address.Bech32Spec+      Haskoin.Address.CashAddrSpec+      Haskoin.AddressSpec+      Haskoin.BlockSpec+      Haskoin.Crypto.HashSpec+      Haskoin.Crypto.Keys.ExtendedSpec+      Haskoin.Crypto.Keys.MnemonicSpec+      Haskoin.Crypto.KeysSpec+      Haskoin.Crypto.SignatureSpec+      Haskoin.NetworkSpec+      Haskoin.ScriptSpec+      Haskoin.Transaction.PartialSpec+      Haskoin.Transaction.TaprootSpec+      Haskoin.TransactionSpec+      Haskoin.UtilSpec       Paths_haskoin_core   hs-source-dirs:       test   build-depends:-      HUnit-    , QuickCheck-    , aeson+      HUnit >=1.6.0.0+    , QuickCheck >=2.13.2+    , aeson >=1.4.6.0+    , array >=0.5.4.0     , base >=4.9 && <5-    , bytestring-    , cereal-    , containers-    , deepseq+    , base16 >=1.0+    , base64+    , binary >=0.8.8+    , bytes >=0.17+    , bytestring >=0.10.10.0+    , cereal >=0.5.8+    , conduit >=1.3.1.2+    , containers >=0.6.2.1+    , crypton >=1.0.0+    , data-default >=0.7.1.1+    , deepseq >=1.4.4.0+    , entropy >=0.4.1.5+    , hashable >=1.3.0.0     , haskoin-core-    , hspec-    , mtl-    , safe-    , split-    , string-conversions-    , text-    , unordered-containers-    , vector+    , hspec >=2.7.1+    , lens >=4.18.1+    , lens-aeson >=1.1+    , memory >=0.15.0+    , mtl >=2.2.2+    , murmur3 >=1.0.3+    , network >=3.1.1.1+    , safe >=0.3.18+    , scientific >=0.3.6.2+    , secp256k1-haskell >=1.2.0+    , split >=0.2.3.3+    , string-conversions >=0.4.0.1+    , text >=1.2.3.0+    , time >=1.9.3+    , transformers >=0.5.6.2+    , unordered-containers >=0.2.10.0+    , vector >=0.12.1.2   default-language: Haskell2010   build-tool-depends: hspec-discover:hspec-discover
src/Haskoin.hs view
@@ -1,45 +1,31 @@-{-|-Module      : Haskoin-Description : Bitcoin (BTC/BCH) Libraries for Haskell-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX+{-# LANGUAGE DuplicateRecordFields #-} -This module exports almost all of Haskoin Core, excluding only a few highly-specialized address and block-related functions.--}+-- |+-- Module      : Haskoin+-- Description : Bitcoin (BTC/BCH) Libraries for Haskell+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- This module exports almost all of Haskoin Core, excluding only a few highly+-- specialized address and block-related functions. module Haskoin-    ( -- * Address (Base58, Bech32, CashAddr)-      module Address-      -- * Network Messages-    , module Network-      -- * Network Constants-    , module Constants-      -- * Blocks-    , module Block-      -- * Transactions-    , module Transaction-      -- * Partially Signed Bitcoin Transactions-    , module Partial-      -- * Scripts-    , module Script-      -- * Cryptographic Keys-    , module Keys-      -- * Cryptographic Primitives-    , module Crypto-      -- * Various Utilities-    , module Util-    ) where+  ( module Address,+    module Block,+    module Transaction,+    module Script,+    module Crypto,+    module Network,+    module Util,+  )+where -import           Network.Haskoin.Address             as Address-import           Network.Haskoin.Block               as Block-import           Network.Haskoin.Constants           as Constants-import           Network.Haskoin.Crypto              as Crypto-import           Network.Haskoin.Keys                as Keys-import           Network.Haskoin.Network             as Network-import           Network.Haskoin.Script              as Script-import           Network.Haskoin.Transaction         as Transaction-import           Network.Haskoin.Transaction.Partial as Partial-import           Network.Haskoin.Util                as Util+import Haskoin.Address as Address+import Haskoin.Block as Block+import Haskoin.Crypto as Crypto+import Haskoin.Network as Network+import Haskoin.Script as Script+import Haskoin.Transaction as Transaction+import Haskoin.Util as Util
+ src/Haskoin/Address.hs view
@@ -0,0 +1,366 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Address+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Base58, CashAddr, Bech32 address and WIF private key serialization support.+module Haskoin.Address+  ( -- * Addresses+    Address (..),+    isPubKeyAddress,+    isScriptAddress,+    isWitnessAddress,+    isWitnessPubKeyAddress,+    isWitnessScriptAddress,+    addrToText,+    textToAddr,+    bech32ToAddr,+    cashToAddr,+    base58ToAddr,+    pubKeyAddr,+    pubKeyWitnessAddr,+    pubKeyCompatWitnessAddr,+    p2pkhAddr,+    p2wpkhAddr,+    p2shAddr,+    p2wshAddr,+    inputAddress,+    outputAddress,+    addressToScript,+    addressToScriptBS,+    addressToOutput,+    payToScriptAddress,+    payToWitnessScriptAddress,+    payToNestedScriptAddress,+    scriptToAddress,+    scriptToAddressBS,+    module Haskoin.Address.Base58,+    module Haskoin.Address.Bech32,+    module Haskoin.Address.CashAddr,+  )+where++import Control.Applicative (Alternative ((<|>)))+import Control.Arrow (second)+import Control.DeepSeq (NFData)+import Control.Monad ((<=<))+import Crypto.Secp256k1+import Data.Aeson (ToJSON (toJSON), Value, withText)+import Data.Aeson.Encoding (Encoding, null_, text)+import Data.Aeson.Types (Encoding, Parser, ToJSON (toJSON), Value, withText)+import Data.Binary (Binary (..))+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Bytes.Get (MonadGet (getByteString, getWord64be, getWord8), runGetS)+import Data.Bytes.Put (MonadPut (putByteString, putWord64be, putWord8), runPutS)+import Data.Bytes.Serial (Serial (..))+import Data.Hashable (Hashable)+import Data.Maybe (isNothing)+import Data.Serialize (Serialize (..))+import Data.Text (Text)+import Data.Text qualified as T+import Data.Word (Word8)+import GHC.Generics (Generic)+import Haskoin.Address.Base58+import Haskoin.Address.Bech32+import Haskoin.Address.CashAddr+import Haskoin.Crypto.Hash+import Haskoin.Crypto.Keys.Common+import Haskoin.Network.Data+import Haskoin.Script.Common+import Haskoin.Script.Standard+import Haskoin.Util++-- | Address format for Bitcoin and Bitcoin Cash.+data Address+  = -- | pay to public key hash (regular)+    PubKeyAddress+      { -- | RIPEMD160 hash of public key's SHA256 hash+        hash160 :: !Hash160+      }+  | -- | pay to script hash+    ScriptAddress+      { -- | RIPEMD160 hash of script's SHA256 hash+        hash160 :: !Hash160+      }+  | -- | pay to witness public key hash+    WitnessPubKeyAddress+      { -- | RIPEMD160 hash of public key's SHA256 hash+        hash160 :: !Hash160+      }+  | -- | pay to witness script hash+    WitnessScriptAddress+      { -- | HASH256 hash of script+        hash256 :: !Hash256+      }+  | -- | other witness address+    WitnessAddress+      { version :: !Word8,+        bytes :: !ByteString+      }+  deriving+    (Eq, Ord, Generic, Show, Read, Hashable, NFData)++instance Serial Address where+  serialize (PubKeyAddress k) = do+    putWord8 0x00+    serialize k+  serialize (ScriptAddress s) = do+    putWord8 0x01+    serialize s+  serialize (WitnessPubKeyAddress h) = do+    putWord8 0x02+    serialize h+  serialize (WitnessScriptAddress s) = do+    putWord8 0x03+    serialize s+  serialize (WitnessAddress v d) = do+    putWord8 0x04+    putWord8 v+    putWord64be (fromIntegral (B.length d))+    putByteString d++  deserialize =+    getWord8 >>= \case+      0x00 -> PubKeyAddress <$> deserialize+      0x01 -> ScriptAddress <$> deserialize+      0x02 -> WitnessPubKeyAddress <$> deserialize+      0x03 -> WitnessScriptAddress <$> deserialize+      0x04 ->+        WitnessAddress+          <$> getWord8+          <*> (getByteString . fromIntegral =<< getWord64be)+      b ->+        fail . T.unpack $+          "Could not decode address type byte: "+            <> encodeHex (B.singleton b)++instance Serialize Address where+  put = serialize+  get = deserialize++instance Binary Address where+  put = serialize+  get = deserialize++-- | 'Address' pays to a public key hash.+isPubKeyAddress :: Address -> Bool+isPubKeyAddress PubKeyAddress {} = True+isPubKeyAddress _ = False++-- | 'Address' pays to a script hash.+isScriptAddress :: Address -> Bool+isScriptAddress ScriptAddress {} = True+isScriptAddress _ = False++-- | 'Address' pays to a witness public key hash. Only valid for SegWit+-- networks.+isWitnessPubKeyAddress :: Address -> Bool+isWitnessPubKeyAddress WitnessPubKeyAddress {} = True+isWitnessPubKeyAddress _ = False++isWitnessScriptAddress :: Address -> Bool+isWitnessScriptAddress WitnessScriptAddress {} = True+isWitnessScriptAddress _ = False++isWitnessAddress :: Address -> Bool+isWitnessAddress WitnessAddress {} = True+isWitnessAddress _ = False++instance MarshalJSON Network Address where+  marshalValue net a = toJSON (addrToText net a)+  marshalEncoding net = maybe null_ text . addrToText net+  unmarshalValue net =+    withText "address" $ \t ->+      case textToAddr net t of+        Nothing -> fail "could not decode address"+        Just x -> return x++-- | Convert address to human-readable string. Uses 'Base58', 'Bech32', or+-- 'CashAddr' depending on network.+addrToText :: Network -> Address -> Maybe Text+addrToText net a@PubKeyAddress {hash160 = h}+  | isNothing net.cashAddrPrefix =+      Just . encodeBase58Check . runPutS $ base58put net a+  | otherwise = cashAddrEncode net 0 (runPutS $ serialize h)+addrToText net a@ScriptAddress {hash160 = h}+  | isNothing net.cashAddrPrefix =+      Just . encodeBase58Check . runPutS $ base58put net a+  | otherwise =+      cashAddrEncode net 1 (runPutS $ serialize h)+addrToText net WitnessPubKeyAddress {hash160 = h} = do+  hrp <- net.bech32Prefix+  segwitEncode hrp 0 (B.unpack (runPutS $ serialize h))+addrToText net WitnessScriptAddress {hash256 = h} = do+  hrp <- net.bech32Prefix+  segwitEncode hrp 0 (B.unpack (runPutS $ serialize h))+addrToText net WitnessAddress {version = v, bytes = d} = do+  hrp <- net.bech32Prefix+  segwitEncode hrp v (B.unpack d)++-- | Parse 'Base58', 'Bech32' or 'CashAddr' address, depending on network.+textToAddr :: Network -> Text -> Maybe Address+textToAddr net txt =+  cashToAddr net txt <|> bech32ToAddr net txt <|> base58ToAddr net txt++cashToAddr :: Network -> Text -> Maybe Address+cashToAddr net txt = do+  (ver, bs) <- cashAddrDecode net txt+  case ver of+    0 -> PubKeyAddress <$> eitherToMaybe (runGetS deserialize bs)+    1 -> ScriptAddress <$> eitherToMaybe (runGetS deserialize bs)+    _ -> Nothing++bech32ToAddr :: Network -> Text -> Maybe Address+bech32ToAddr net txt = do+  hrp <- net.bech32Prefix+  (ver, bs) <- second B.pack <$> segwitDecode hrp txt+  case ver of+    0 -> case B.length bs of+      20 -> WitnessPubKeyAddress <$> eitherToMaybe (runGetS deserialize bs)+      32 -> WitnessScriptAddress <$> eitherToMaybe (runGetS deserialize bs)+      _ -> Nothing+    _ -> Just $ WitnessAddress ver bs++base58ToAddr :: Network -> Text -> Maybe Address+base58ToAddr net txt =+  eitherToMaybe . runGetS (base58get net) =<< decodeBase58Check txt++base58get :: (MonadGet m) => Network -> m Address+base58get net = do+  pfx <- getWord8+  addr <- deserialize+  f pfx addr+  where+    f x a+      | x == net.addrPrefix = return $ PubKeyAddress a+      | x == net.scriptPrefix = return $ ScriptAddress a+      | otherwise = fail "Does not recognize address prefix"++base58put :: (MonadPut m) => Network -> Address -> m ()+base58put net (PubKeyAddress h) = do+  putWord8 net.addrPrefix+  serialize h+base58put net (ScriptAddress h) = do+  putWord8 net.scriptPrefix+  serialize h+base58put _ _ = error "Cannot serialize this address as Base58"++-- | Obtain a standard pay-to-public-key-hash address from a public key.+pubKeyAddr :: Ctx -> PublicKey -> Address+pubKeyAddr ctx = PubKeyAddress . addressHash . marshal ctx++-- | Obtain a standard pay-to-public-key-hash (P2PKH) address from a 'Hash160'.+p2pkhAddr :: Hash160 -> Address+p2pkhAddr = PubKeyAddress++-- | Obtain a SegWit pay-to-witness-public-key-hash (P2WPKH) address from a+-- public key.+pubKeyWitnessAddr :: Ctx -> PublicKey -> Address+pubKeyWitnessAddr ctx =+  WitnessPubKeyAddress . addressHash . marshal ctx++-- | Obtain a backwards-compatible SegWit P2SH-P2WPKH address from a public key.+pubKeyCompatWitnessAddr :: Ctx -> PublicKey -> Address+pubKeyCompatWitnessAddr ctx =+  p2shAddr+    . addressHash+    . marshal ctx+    . PayWitnessPKHash+    . addressHash+    . marshal ctx++-- | Obtain a SegWit pay-to-witness-public-key-hash (P2WPKH) address from a+-- 'Hash160'.+p2wpkhAddr :: Hash160 -> Address+p2wpkhAddr = WitnessPubKeyAddress++-- | Obtain a standard pay-to-script-hash (P2SH) address from a 'Hash160'.+p2shAddr :: Hash160 -> Address+p2shAddr = ScriptAddress++-- | Obtain a SegWit pay-to-witness-script-hash (P2WSH) address from a 'Hash256'+p2wshAddr :: Hash256 -> Address+p2wshAddr = WitnessScriptAddress++-- | Compute a standard pay-to-script-hash (P2SH) address for an output script.+payToScriptAddress :: Ctx -> ScriptOutput -> Address+payToScriptAddress ctx = p2shAddr . addressHash . marshal ctx++-- | Compute a SegWit pay-to-witness-script-hash (P2WSH) address for an output+-- script.+payToWitnessScriptAddress :: Ctx -> ScriptOutput -> Address+payToWitnessScriptAddress ctx = p2wshAddr . sha256 . marshal ctx++-- | Compute a backwards-compatible SegWit P2SH-P2WSH address.+payToNestedScriptAddress :: Ctx -> ScriptOutput -> Address+payToNestedScriptAddress ctx =+  p2shAddr . addressHash . marshal ctx . toP2WSH . encodeOutput ctx++-- | Encode an output script from an address. Will fail if using a+-- pay-to-witness address on a non-SegWit network.+addressToOutput :: Address -> ScriptOutput+addressToOutput =+  \case+    PubKeyAddress h -> PayPKHash h+    ScriptAddress h -> PayScriptHash h+    WitnessPubKeyAddress h -> PayWitnessPKHash h+    WitnessScriptAddress h -> PayWitnessScriptHash h+    WitnessAddress v d -> PayWitness v d++-- | Get output script AST for an 'Address'.+addressToScript :: Ctx -> Address -> Script+addressToScript ctx = encodeOutput ctx . addressToOutput++-- | Encode address as output script in 'ByteString' form.+addressToScriptBS :: Ctx -> Address -> ByteString+addressToScriptBS ctx = runPutS . serialize . addressToScript ctx++-- | Decode an output script into an 'Address' if it has such representation.+scriptToAddress :: Ctx -> Script -> Either String Address+scriptToAddress ctx =+  maybeToEither e . outputAddress ctx <=< decodeOutput ctx+  where+    e = "Could not decode address"++-- | Decode a serialized script into an 'Address'.+scriptToAddressBS :: Ctx -> ByteString -> Either String Address+scriptToAddressBS ctx =+  maybeToEither e . outputAddress ctx <=< unmarshal ctx+  where+    e = "Could not decode address"++-- | Get the 'Address' of a 'ScriptOutput'.+outputAddress :: Ctx -> ScriptOutput -> Maybe Address+outputAddress ctx =+  \case+    PayPKHash h -> Just $ PubKeyAddress h+    PayScriptHash h -> Just $ ScriptAddress h+    PayPK k -> Just $ pubKeyAddr ctx k+    PayWitnessPKHash h -> Just $ WitnessPubKeyAddress h+    PayWitnessScriptHash h -> Just $ WitnessScriptAddress h+    PayWitness v d -> Just $ WitnessAddress v d+    _ -> Nothing++-- | Infer the 'Address' of a 'ScriptInput'.+inputAddress :: Ctx -> ScriptInput -> Maybe Address+inputAddress ctx =+  \case+    (RegularInput (SpendPKHash _ key)) -> Just $ pubKeyAddr ctx key+    (ScriptHashInput _ rdm) -> Just $ payToScriptAddress ctx rdm+    _ -> Nothing
+ src/Haskoin/Address/Base58.hs view
@@ -0,0 +1,125 @@+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Address.Base58+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Support for legacy 'Base58' addresses. Superseded by Bech32 for Bitcoin SegWit+-- (BTC) and CashAddr for Bitcoin Cash (BCH).+module Haskoin.Address.Base58+  ( -- * Base58+    Base58,+    encodeBase58,+    decodeBase58,+    encodeBase58Check,+    decodeBase58Check,+  )+where++import Control.Monad (guard)+import Data.Array (Array, assocs, listArray, (!), (//))+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.ByteString.Char8 qualified as C+import Data.Bytes.Get ()+import Data.Bytes.Put (runPutS)+import Data.Bytes.Serial (Serial (serialize))+import Data.Char (chr, ord)+import Data.Maybe (fromMaybe, isJust, listToMaybe)+import Data.String.Conversions (cs)+import Data.Text (Text)+import Data.Text qualified as T+import Data.Word (Word8)+import Haskoin.Crypto.Hash (checkSum32)+import Haskoin.Util.Helpers (bsToInteger, integerToBS)+import Numeric (readInt, showIntAtBase)++-- | 'Base58' classic Bitcoin address format.+type Base58 = Text++-- | Symbols for Base58 encoding.+b58Data :: ByteString+b58Data = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"++b58Array :: Array Int Word8+b58Array = listArray (0, 57) (B.unpack b58Data)++b58InvArray :: Array Word8 (Maybe Int)+b58InvArray =+  listArray+    (minBound, maxBound)+    (repeat Nothing)+    // map swap (assocs b58Array)+  where+    swap (i, c) = (c, Just i)++-- | Convert a number less than or equal to provided integer into a 'Base58'+-- character.+b58 :: Int -> Word8+b58 = (b58Array !)++-- | Convert a 'Base58' character into the number it represents.+b58' :: Word8 -> Maybe Int+b58' = (b58InvArray !)++-- | Encode an arbitrary-length 'Integer' into a 'Base58' string. Leading zeroes+-- will not be part of the resulting string.+encodeBase58I :: Integer -> Base58+encodeBase58I i = cs $ showIntAtBase 58 (chr . fromIntegral . b58) i ""++-- | Decode a 'Base58' string into an arbitrary-length 'Integer'.+decodeBase58I :: Base58 -> Maybe Integer+decodeBase58I s =+  case go of+    Just (r, []) -> Just r+    _ -> Nothing+  where+    p = isJust . b58' . fromIntegral . ord+    f = fromMaybe e . b58' . fromIntegral . ord+    go = listToMaybe $ readInt 58 p f (cs s)+    e = error "Could not decode base58"++-- | Encode an arbitrary 'ByteString' into a its 'Base58' representation,+-- preserving leading zeroes.+encodeBase58 :: ByteString -> Base58+encodeBase58 bs =+  l <> r+  where+    (z, b) = B.span (== 0) bs+    l = cs $ B.replicate (B.length z) (b58 0) -- preserve leading 0's+    r+      | B.null b = T.empty+      | otherwise = encodeBase58I $ bsToInteger b++-- | Decode a 'Base58'-encoded 'Text' to a 'ByteString'.+decodeBase58 :: Base58 -> Maybe ByteString+decodeBase58 t =+  B.append prefix <$> r+  where+    (z, b) = B.span (== b58 0) (cs t)+    prefix = B.replicate (B.length z) 0 -- preserve leading 1's+    r+      | B.null b = Just B.empty+      | otherwise = integerToBS <$> decodeBase58I (cs b)++-- | Computes a checksum for the input 'ByteString' and encodes the input and+-- the checksum as 'Base58'.+encodeBase58Check :: ByteString -> Base58+encodeBase58Check bs =+  (encodeBase58 . B.append bs . runPutS . serialize . checkSum32) bs++-- | Decode a 'Base58'-encoded string that contains a checksum. This function+-- returns 'Nothing' if the input string contains invalid 'Base58' characters or+-- if the checksum fails.+decodeBase58Check :: Base58 -> Maybe ByteString+decodeBase58Check bs = do+  rs <- decodeBase58 bs+  let (res, chk) = B.splitAt (B.length rs - 4) rs+  guard $ chk == (runPutS . serialize . checkSum32) res+  return res
+ src/Haskoin/Address/Bech32.hs view
@@ -0,0 +1,265 @@+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Address.Base58+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Support for Bitcoin SegWit (BTC) Bech32 addresses. This module is a modified+-- version of Marko Bencun's reference implementation.+module Haskoin.Address.Bech32+  ( -- * Bech32+    HRP,+    Bech32,+    Bech32Encoding (..),+    bech32Const,+    Data,+    bech32Encode,+    bech32Decode,+    toBase32,+    toBase256,+    segwitEncode,+    segwitDecode,+    Word5 (..),+    word5,+    fromWord5,+  )+where++import Control.Monad (guard)+import Data.Array+  ( Array,+    assocs,+    bounds,+    listArray,+    (!),+    (//),+  )+import Data.Bits+  ( Bits,+    testBit,+    unsafeShiftL,+    unsafeShiftR,+    xor,+    (.&.),+    (.|.),+  )+import Data.ByteString qualified as B+import Data.Char (toUpper)+import Data.Foldable (foldl')+import Data.Functor.Identity (Identity, runIdentity)+import Data.Ix (Ix (..))+import Data.Text (Text)+import Data.Text qualified as T+import Data.Text.Encoding qualified as E+import Data.Word (Word8)++data Bech32Encoding = Bech32 | Bech32m+  deriving (Eq, Show, Ord, Enum)++-- | Bech32 human-readable string.+type Bech32 = Text++-- | Human-readable part of 'Bech32' address.+type HRP = Text++-- | Data part of 'Bech32' address.+type Data = [Word8]++(.>>.), (.<<.) :: (Bits a) => a -> Int -> a+(.>>.) = unsafeShiftR+(.<<.) = unsafeShiftL++-- | Five-bit word for Bech32.+newtype Word5+  = UnsafeWord5 Word8+  deriving (Eq, Ord)++instance Ix Word5 where+  range (UnsafeWord5 m, UnsafeWord5 n) = map UnsafeWord5 $ range (m, n)+  index (UnsafeWord5 m, UnsafeWord5 n) (UnsafeWord5 i) = index (m, n) i+  inRange (m, n) i = m <= i && i <= n++-- | Convert an integer number into a five-bit word.+word5 :: (Integral a) => a -> Word5+word5 x = UnsafeWord5 (fromIntegral x .&. 31)+{-# INLINE word5 #-}+{-# SPECIALIZE INLINE word5 :: Word8 -> Word5 #-}++-- | Convert a five-bit word into a number.+fromWord5 :: (Num a) => Word5 -> a+fromWord5 (UnsafeWord5 x) = fromIntegral x+{-# INLINE fromWord5 #-}+{-# SPECIALIZE INLINE fromWord5 :: Word5 -> Word8 #-}++-- | 'Bech32' character map as array of five-bit integers to character.+charset :: Array Word5 Char+charset =+  listArray (UnsafeWord5 0, UnsafeWord5 31) "qpzry9x8gf2tvdw0s3jn54khce6mua7l"++-- | Convert a character to its five-bit value from 'Bech32' 'charset'.+charsetMap :: Char -> Maybe Word5+charsetMap c+  | inRange (bounds inv) upperC = inv ! upperC+  | otherwise = Nothing+  where+    upperC = toUpper c+    inv = listArray ('0', 'Z') (repeat Nothing) // map swap (assocs charset)+    swap (a, b) = (toUpper b, Just a)++-- | Calculate or validate 'Bech32' checksum.+bech32Polymod :: [Word5] -> Word+bech32Polymod values = foldl' go 1 values .&. 0x3fffffff+  where+    go chk value =+      foldl' xor chk' [g | (g, i) <- zip generator [25 ..], testBit chk i]+      where+        generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]+        chk' = chk .<<. 5 `xor` fromWord5 value++-- | Convert human-readable part of 'Bech32' string into a list of five-bit+-- words.+bech32HRPExpand :: HRP -> [Word5]+bech32HRPExpand hrp =+  map (UnsafeWord5 . (.>>. 5)) hrpBytes+    ++ [UnsafeWord5 0]+    ++ map word5 hrpBytes+  where+    hrpBytes = B.unpack $ E.encodeUtf8 hrp++bech32Const :: Bech32Encoding -> Word+bech32Const Bech32 = 0x00000001+bech32Const Bech32m = 0x2bc830a3++-- | Calculate Bech32 checksum for a string of five-bit words.+bech32CreateChecksum :: Bech32Encoding -> HRP -> [Word5] -> [Word5]+bech32CreateChecksum enc hrp dat = [word5 (polymod .>>. i) | i <- [25, 20 .. 0]]+  where+    values = bech32HRPExpand hrp ++ dat+    w5 = values ++ map UnsafeWord5 [0, 0, 0, 0, 0, 0]+    polymod = bech32Polymod w5 `xor` bech32Const enc++-- | Verify Bech32 checksum for a human-readable part and string of five-bit words.+bech32VerifyChecksum :: HRP -> [Word5] -> Maybe Bech32Encoding+bech32VerifyChecksum hrp dat =+  let poly = bech32Polymod (bech32HRPExpand hrp ++ dat)+   in if+        | poly == bech32Const Bech32 -> Just Bech32+        | poly == bech32Const Bech32m -> Just Bech32m+        | otherwise -> Nothing++-- | Maximum length of a Bech32 result.+maxBech32Length :: Int+maxBech32Length = 90++-- | Encode string of five-bit words into 'Bech32' using a provided+-- human-readable part. Can fail if 'HRP' is invalid or result would be longer+-- than 90 characters.+bech32Encode :: Bech32Encoding -> HRP -> [Word5] -> Maybe Bech32+bech32Encode enc hrp dat = do+  guard $ checkHRP hrp+  let dat' = dat ++ bech32CreateChecksum enc (T.toLower hrp) dat+      rest = map (charset !) dat'+      result = T.concat [T.toLower hrp, T.pack "1", T.pack rest]+  guard $ T.length result <= maxBech32Length+  return result++-- | Check that human-readable part is valid for a 'Bech32' string.+checkHRP :: HRP -> Bool+checkHRP hrp =+  not (T.null hrp)+    && T.all (\char -> char >= '\x21' && char <= '\x7e') hrp++-- | Decode human-readable 'Bech32' string into a human-readable part and a+-- string of five-bit words.+bech32Decode :: Bech32 -> Maybe (Bech32Encoding, HRP, [Word5])+bech32Decode bech32 = do+  guard $ T.length bech32 <= maxBech32Length+  guard $ T.toUpper bech32 == bech32 || lowerBech32 == bech32+  let (hrp, dat) = T.breakOnEnd "1" lowerBech32+  guard $ T.length dat >= 6+  hrp' <- T.stripSuffix "1" hrp+  guard $ checkHRP hrp'+  dat' <- mapM charsetMap $ T.unpack dat+  enc <- bech32VerifyChecksum hrp' dat'+  return (enc, hrp', take (T.length dat - 6) dat')+  where+    lowerBech32 = T.toLower bech32++type Pad f = Int -> Int -> Word -> [[Word]] -> f [[Word]]++yesPadding :: Pad Identity+yesPadding _ 0 _ result = return result+yesPadding _ _ padValue result = return $ [padValue] : result+{-# INLINE yesPadding #-}++noPadding :: Pad Maybe+noPadding frombits bits padValue result = do+  guard $ bits < frombits && padValue == 0+  return result+{-# INLINE noPadding #-}++-- | Big endian conversion of a bytestring from base \(2^{frombits}\) to base+-- \(2^{tobits}\). {frombits} and {twobits} must be positive and+-- \(2^{frombits}\) and \(2^{tobits}\) must be smaller than the size of Word.+-- Every value in 'dat' must be strictly smaller than \(2^{frombits}\).+convertBits :: (Functor f) => [Word] -> Int -> Int -> Pad f -> f [Word]+convertBits dat frombits tobits pad = concat . reverse <$> go dat 0 0 []+  where+    go [] acc bits result =+      let padValue = (acc .<<. (tobits - bits)) .&. maxv+       in pad frombits bits padValue result+    go (value : dat') acc bits result =+      go dat' acc' (bits' `rem` tobits) (result' : result)+      where+        acc' = (acc .<<. frombits) .|. fromIntegral value+        bits' = bits + frombits+        result' =+          [ (acc' .>>. b) .&. maxv+            | b <- [bits' - tobits, bits' - 2 * tobits .. 0]+          ]+    maxv = (1 .<<. tobits) - 1+{-# INLINE convertBits #-}++-- | Convert from eight-bit to five-bit word string, adding padding as required.+toBase32 :: [Word8] -> [Word5]+toBase32 dat =+  map word5 $ runIdentity $ convertBits (map fromIntegral dat) 8 5 yesPadding++-- | Convert from five-bit word string to eight-bit word string, ignoring padding.+toBase256 :: [Word5] -> Maybe [Word8]+toBase256 dat =+  map fromIntegral <$> convertBits (map fromWord5 dat) 5 8 noPadding++-- | Check if witness version and program are valid.+segwitCheck :: Bech32Encoding -> Word8 -> Data -> Bool+segwitCheck enc witver witprog =+  witver <= 16+    && if witver == 0+      then enc == Bech32 && (length witprog == 20 || length witprog == 32)+      else enc == Bech32m && (length witprog >= 2 && length witprog <= 40)++-- | Decode SegWit 'Bech32' address from a string and expected human-readable part.+segwitDecode :: HRP -> Bech32 -> Maybe (Word8, Data)+segwitDecode hrp addr = do+  (enc, hrp', dat) <- bech32Decode addr+  guard $ (hrp == hrp') && not (null dat)+  let (UnsafeWord5 witver : datBase32) = dat+  decoded <- toBase256 datBase32+  guard $ segwitCheck enc witver decoded+  return (witver, decoded)++-- | Encode 'Data' as a SegWit 'Bech32' address. Needs human-readable part and+-- witness program version.+segwitEncode :: HRP -> Word8 -> Data -> Maybe Text+segwitEncode hrp witver witprog = do+  guard $ segwitCheck enc witver witprog+  bech32Encode enc hrp $ UnsafeWord5 witver : toBase32 witprog+  where+    enc = if witver == 0 then Bech32 else Bech32m
+ src/Haskoin/Address/CashAddr.hs view
@@ -0,0 +1,221 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Address.CashAddr+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Support for Bitcoin Cash (BCH) CashAddr format.+module Haskoin.Address.CashAddr+  ( -- * CashAddr+    CashPrefix,+    CashVersion,+    CashAddr,+    Cash32,+    cashAddrDecode,+    cashAddrEncode,+    cash32decodeType,+    cash32encodeType,+    cash32decode,+    cash32encode,+  )+where++import Control.Monad (guard)+import Data.Bits+  ( Bits+      ( shiftL,+        shiftR,+        testBit,+        xor,+        (.&.),+        (.|.)+      ),+  )+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.ByteString.Char8 qualified as C+import Data.Char (ord, toLower, toUpper)+import Data.List (elemIndex, foldl')+import Data.Text (Text)+import Data.Text qualified as T+import Data.Text.Encoding (decodeUtf8, encodeUtf8)+import Data.Word (Word64, Word8)+import Haskoin.Network.Data (Network (cashAddrPrefix))+import Haskoin.Util.Helpers (convertBits)++-- | 'CashAddr' prefix, usually shown before the colon in addresses, but sometimes+-- omitted. It is used in the checksum calculation to avoid parsing an address+-- from the wrong network.+type CashPrefix = Text++-- | 'CashAddr' version, until new address schemes appear it will be zero.+type CashVersion = Word8++-- | High level 'CashAddr' human-reabale string, with explicit or implicit prefix.+type CashAddr = Text++-- | Low level 'Cash32' is the human-readable low-level encoding used by 'CashAddr'.+-- It need not encode a valid address but any binary data.+type Cash32 = Text++-- | Symbols for encoding 'Cash32' data in human-readable strings.+charset :: String+charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"++-- | Get the 32-bit number associated with this 'Cash32' character.+base32char :: Char -> Maybe Word8+base32char = fmap fromIntegral . (`elemIndex` charset)++-- | High-Level: decode 'CashAddr' string if it is valid for the+-- provided 'Network'. Prefix may be omitted from the string.+cashAddrDecode :: Network -> CashAddr -> Maybe (CashVersion, ByteString)+cashAddrDecode net ca = do+  epfx <- net.cashAddrPrefix+  let (cpfx, cdat) = T.breakOnEnd ":" (T.toLower ca)+  guard (T.null cpfx || T.init cpfx == epfx)+  (dpfx, ver, bs) <- cash32decodeType (epfx <> ":" <> cdat)+  guard (dpfx == epfx)+  return (ver, bs)++-- | High-Level: encode 'CashAddr' string for the provided network and hash.+-- Fails if the 'CashVersion' or length of hash 'ByteString' is invalid.+cashAddrEncode :: Network -> CashVersion -> ByteString -> Maybe CashAddr+cashAddrEncode net cv bs = do+  pfx <- net.cashAddrPrefix+  cash32encodeType pfx cv bs++-- | Mid-Level: decode 'CashAddr' string containing arbitrary prefix, plus a+-- version byte before the 'ByteString' that encodes type and length.+cash32decodeType :: Cash32 -> Maybe (CashPrefix, CashVersion, ByteString)+cash32decodeType ca' = do+  guard (T.toUpper ca' == ca' || ca == ca')+  (dpfx, bs) <- cash32decode ca+  guard (not (B.null bs))+  let vb = B.head bs+      pay = B.tail bs+  (ver, len) <- decodeVersionByte vb+  guard (B.length pay == len)+  return (dpfx, ver, pay)+  where+    ca = T.toLower ca'++-- | Mid-Level: encode 'CashAddr' string containing arbitrary prefix and+-- 'CashVersion'. Length must be among those allowed by the standard.+cash32encodeType :: CashPrefix -> CashVersion -> ByteString -> Maybe Cash32+cash32encodeType pfx cv bs = do+  let len = B.length bs+  vb <- encodeVersionByte cv len+  let pl = vb `B.cons` bs+  return (cash32encode pfx pl)++-- | Low-Level: decode 'Cash32' string. 'CashPrefix' must be part of the string.+-- No version or hash length validation is performed.+cash32decode :: Cash32 -> Maybe (CashPrefix, ByteString)+cash32decode text = do+  let bs = C.map toLower bs'+  guard (C.map toUpper bs' == bs' || bs == bs')+  let (pfx', dat) = C.breakEnd (== ':') bs+  pfx <-+    if B.null pfx' || pfx' == C.singleton ':'+      then Nothing+      else Just (B.init pfx')+  b32 <- B.pack <$> mapM base32char (C.unpack dat)+  let px = B.map (.&. 0x1f) pfx+      pd = px <> B.singleton 0 <> b32+      cs = cash32Polymod pd+      bb = B.take (B.length b32 - 8) b32+  guard (verifyCash32Polymod cs)+  let out = toBase256 bb+  return (decodeUtf8 pfx, out)+  where+    bs' = encodeUtf8 text++-- | Low-Level: encode 'Cash32' string for 'CashPrefix' provided. Can encode+-- arbitrary data. No prefix or length validation is performed.+cash32encode :: CashPrefix -> ByteString -> Cash32+cash32encode pfx bs =+  let b32 = toBase32 bs+      px = B.map (.&. 0x1f) (encodeUtf8 pfx)+      pd = px <> B.singleton 0 <> b32 <> B.replicate 8 0+      cs = cash32Polymod pd+      c32 = B.map f (b32 <> cs)+      f = fromIntegral . ord . (charset !!) . fromIntegral+   in pfx <> ":" <> decodeUtf8 c32++-- | Convert base of 'ByteString' from eight bits per byte to five bits per+-- byte, adding padding as necessary.+toBase32 :: ByteString -> ByteString+toBase32 =+  B.pack+    . map fromIntegral+    . fst+    . convertBits True 8 5+    . map fromIntegral+    . B.unpack++-- | Convert base of 'ByteString' from five to eight bits per byte. Ignore+-- padding to be symmetric with respect to 'toBase32' function.+toBase256 :: ByteString -> ByteString+toBase256 =+  B.pack+    . map fromIntegral+    . fst+    . convertBits False 5 8+    . map fromIntegral+    . B.unpack++-- | Obtain 'CashVersion' and payload length from 'CashAddr' version byte.+decodeVersionByte :: Word8 -> Maybe (CashVersion, Int)+decodeVersionByte vb = do+  guard (vb .&. 0x80 == 0)+  return (ver, len)+  where+    ver = vb `shiftR` 3+    len = ls !! fromIntegral (vb .&. 0x07)+    ls = [20, 24, 28, 32, 40, 48, 56, 64]++-- | Encode 'CashVersion' and length into version byte. Fail if version is+-- larger than five bits, or length incorrect, since that is invalid.+encodeVersionByte :: CashVersion -> Int -> Maybe Word8+encodeVersionByte ver len = do+  guard (ver == ver .&. 0x0f)+  l <- case len of+    20 -> Just 0+    24 -> Just 1+    28 -> Just 2+    32 -> Just 3+    40 -> Just 4+    48 -> Just 5+    56 -> Just 6+    64 -> Just 7+    _ -> Nothing+  return ((ver `shiftL` 3) .|. l)++-- | Calculate or validate checksum from base32 'ByteString' (excluding prefix).+cash32Polymod :: ByteString -> ByteString+cash32Polymod v =+  B.pack+    [fromIntegral (polymod `shiftR` (5 * (7 - i))) .&. 0x1f | i <- [0 .. 7]]+  where+    polymod = B.foldl' outer (1 :: Word64) v `xor` 1+    outer c d =+      let c0 = (fromIntegral (c `shiftR` 35) :: Word8)+          c' = ((c .&. 0x07ffffffff) `shiftL` 5) `xor` fromIntegral d+       in foldl' (inner c0) c' (zip [0 ..] generator)+    generator =+      [0x98f2bc8e61, 0x79b76d99e2, 0xf33e5fb3c4, 0xae2eabe2a8, 0x1e4f43e470]+    inner c0 c (b, g)+      | c0 `testBit` b = c `xor` g+      | otherwise = c++-- | Validate that polymod 'ByteString' (eight bytes) is equal to zero.+verifyCash32Polymod :: ByteString -> Bool+verifyCash32Polymod = (== B.replicate 8 0)
+ src/Haskoin/Block.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE DuplicateRecordFields #-}++-- |+-- Module      : Haskoin.Block+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Most functions relating to blocks are exported by this module.+module Haskoin.Block+  ( module Haskoin.Block.Common,+    module Haskoin.Block.Headers,+    module Haskoin.Block.Merkle,+  )+where++import Haskoin.Block.Common+import Haskoin.Block.Headers+import Haskoin.Block.Merkle
+ src/Haskoin/Block/Common.hs view
@@ -0,0 +1,427 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Block.Common+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Common data types and functions to handle blocks from the block chain.+module Haskoin.Block.Common+  ( -- * Blocks+    Block (..),+    BlockHeight,+    Timestamp,+    BlockHeader (..),+    headerHash,+    BlockLocator,+    GetBlocks (..),+    GetHeaders (..),+    BlockHeaderCount,+    BlockHash (..),+    blockHashToHex,+    hexToBlockHash,+    Headers (..),+    decodeCompact,+    encodeCompact,+  )+where++import Control.DeepSeq+import Control.Monad+import Data.Aeson+import Data.Aeson.Encoding+import Data.Binary (Binary (..))+import Data.Bits+import Data.ByteString qualified as B+import Data.ByteString.Builder (char7)+import Data.ByteString.Lazy qualified as L+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Hashable+import Data.Maybe+import Data.Serialize (Serialize (..))+import Data.String+import Data.String.Conversions+import Data.Text (Text)+import Data.Word+import GHC.Generics+import Haskoin.Crypto.Hash+import Haskoin.Network.Common+import Haskoin.Transaction.Common+import Haskoin.Util.Helpers+import Text.Read qualified as R++-- | Height of a block in the block chain, starting at 0 for Genesis.+type BlockHeight = Word32++-- | Block timestamp as Unix time (seconds since 1970-01-01 00:00 UTC).+type Timestamp = Word32++-- | Block header and transactions.+data Block = Block+  { header :: !BlockHeader,+    txs :: ![Tx]+  }+  deriving (Eq, Show, Read, Generic, Hashable, NFData)++instance Serial Block where+  deserialize = do+    header <- deserialize+    (VarInt c) <- deserialize+    txs <- replicateM (fromIntegral c) deserialize+    return $ Block header txs+  serialize (Block h txs) = do+    serialize h+    putVarInt $ length txs+    forM_ txs serialize++instance Serialize Block where+  get = deserialize+  put = serialize++instance Binary Block where+  get = deserialize+  put = serialize++instance ToJSON Block where+  toJSON (Block h t) =+    object ["header" .= h, "transactions" .= t]+  toEncoding (Block h t) =+    pairs $+      mconcat+        [ "header" `pair` toEncoding h,+          "transactions" `pair` list toEncoding t+        ]++instance FromJSON Block where+  parseJSON =+    withObject "Block" $ \o ->+      Block <$> o .: "header" <*> o .: "transactions"++-- | Block header hash. To be serialized reversed for display purposes.+newtype BlockHash = BlockHash {get :: Hash256}+  deriving (Eq, Ord, Generic, Hashable, Serial, NFData)++instance Serialize BlockHash where+  put = serialize+  get = deserialize++instance Binary BlockHash where+  put = serialize+  get = deserialize++instance Show BlockHash where+  showsPrec _ = shows . blockHashToHex++instance Read BlockHash where+  readPrec = do+    R.String str <- R.lexP+    maybe R.pfail return $ hexToBlockHash $ cs str++instance IsString BlockHash where+  fromString s =+    fromMaybe (error "Could not read block hash from hex string") $+      hexToBlockHash $+        cs s++instance FromJSON BlockHash where+  parseJSON =+    withText "BlockHash" $+      maybe mzero return . hexToBlockHash++instance ToJSON BlockHash where+  toJSON = String . blockHashToHex+  toEncoding = hexEncoding . L.reverse . runPutL . serialize++-- | Block hashes are reversed with respect to the in-memory byte order in a+-- block hash when displayed.+blockHashToHex :: BlockHash -> Text+blockHashToHex (BlockHash h) = encodeHex (B.reverse (runPutS (serialize h)))++-- | Convert a human-readable hex block hash into a 'BlockHash'. Bytes are+-- reversed as normal.+hexToBlockHash :: Text -> Maybe BlockHash+hexToBlockHash hex = do+  bs <- B.reverse <$> decodeHex hex+  h <- eitherToMaybe (runGetS deserialize bs)+  return $ BlockHash h++-- | Data type recording information of a 'Block'. The hash of a block is+-- defined as the hash of this data structure, serialized. The block mining+-- process involves finding a partial hash collision by varying the nonce in the+-- 'BlockHeader' and/or additional entropy in the coinbase 'Transaction' of this+-- 'Block'. Variations in the coinbase will result in different merkle roots in+-- the 'BlockHeader'.+data BlockHeader = BlockHeader+  { version :: !Word32, --  4 bytes++    -- | hash of the previous block (parent)+    prev :: !BlockHash, -- 32 bytes++    -- | root of the merkle tree of transactions+    merkle :: !Hash256, -- 32 bytes++    -- | unix timestamp+    timestamp :: !Timestamp, --  4 bytes++    -- | difficulty target+    bits :: !Word32, --  4 bytes++    -- | random nonce+    nonce :: !Word32 --  4 bytes+  }+  deriving (Eq, Ord, Show, Read, Generic, Hashable, NFData)++-- 80 bytes++instance ToJSON BlockHeader where+  toJSON (BlockHeader v p m t b n) =+    object+      [ "version" .= v,+        "prevblock" .= p,+        "merkleroot" .= encodeHex (runPutS $ serialize m),+        "timestamp" .= t,+        "bits" .= b,+        "nonce" .= n+      ]+  toEncoding (BlockHeader v p m t b n) =+    pairs $+      mconcat+        [ "version" `pair` word32 v,+          "prevblock" `pair` toEncoding p,+          "merkleroot" `pair` hexEncoding (runPutL $ serialize m),+          "timestamp" `pair` toEncoding t,+          "bits" `pair` toEncoding b,+          "nonce" `pair` toEncoding n+        ]++instance FromJSON BlockHeader where+  parseJSON =+    withObject "BlockHeader" $ \o ->+      BlockHeader+        <$> o .: "version"+        <*> o .: "prevblock"+        <*> (f =<< o .: "merkleroot")+        <*> o .: "timestamp"+        <*> o .: "bits"+        <*> o .: "nonce"+    where+      f = maybe mzero return . (eitherToMaybe . runGetS deserialize <=< decodeHex)++instance Serial BlockHeader where+  deserialize = do+    v <- getWord32le+    p <- deserialize+    m <- deserialize+    t <- getWord32le+    b <- getWord32le+    n <- getWord32le+    return+      BlockHeader+        { version = v,+          prev = p,+          merkle = m,+          timestamp = t,+          bits = b,+          nonce = n+        }+  serialize (BlockHeader v p m bt bb n) = do+    putWord32le v+    serialize p+    serialize m+    putWord32le bt+    putWord32le bb+    putWord32le n++instance Binary BlockHeader where+  put = serialize+  get = deserialize++instance Serialize BlockHeader where+  put = serialize+  get = deserialize++-- | Compute hash of 'BlockHeader'.+headerHash :: BlockHeader -> BlockHash+headerHash = BlockHash . doubleSHA256 . runPutS . serialize++-- | A block locator is a set of block headers, denser towards the best block+-- and sparser towards the genesis block. It starts at the highest block known.+-- It is used by a node to synchronize against the network. When the locator is+-- provided to a peer, it will send back block hashes starting from the first+-- block in the locator that it recognizes.+type BlockLocator = [BlockHash]++-- | Data type representing a getblocks message request. It is used in the+-- bitcoin protocol to retrieve blocks from a peer by providing it a+-- 'BlockLocator' object. The response to a 'GetBlocks' message is an 'Inv'+-- message containing a list of block hashes that the peer believes this node is+-- missing. The number of block hashes in that inv message will end at the stop+-- block hash, at at the tip of the chain, or after 500 entries, whichever comes+-- earlier.+data GetBlocks = GetBlocks+  { version :: !Word32,+    -- | block locator object+    locator :: !BlockLocator,+    -- | hash of the last desired block+    stop :: !BlockHash+  }+  deriving (Eq, Show, Read, Generic, NFData)++instance Serial GetBlocks where+  deserialize =+    GetBlocks+      <$> getWord32le+      <*> (repList =<< deserialize)+      <*> deserialize+    where+      repList (VarInt c) = replicateM (fromIntegral c) deserialize+  serialize (GetBlocks v xs h) = do+    putWord32le v+    putVarInt $ length xs+    forM_ xs serialize+    serialize h++instance Serialize GetBlocks where+  put = serialize+  get = deserialize++-- | Similar to the 'GetBlocks' message type but for retrieving block headers+-- only. The response to a 'GetHeaders' request is a 'Headers' message+-- containing a list of block headers. A maximum of 2000 block headers can be+-- returned. 'GetHeaders' is used by simplified payment verification (SPV)+-- clients to exclude block contents when synchronizing the block chain.+data GetHeaders = GetHeaders+  { version :: !Word32,+    -- | block locator object+    locator :: !BlockLocator,+    -- | hash of the last desired block header+    stop :: !BlockHash+  }+  deriving (Eq, Show, Read, Generic, NFData)++instance Serial GetHeaders where+  deserialize =+    GetHeaders+      <$> getWord32le+      <*> (repList =<< deserialize)+      <*> deserialize+    where+      repList (VarInt c) = replicateM (fromIntegral c) deserialize+  serialize (GetHeaders v xs h) = do+    putWord32le v+    putVarInt $ length xs+    forM_ xs serialize+    serialize h++instance Serialize GetHeaders where+  put = serialize+  get = deserialize++instance Binary GetHeaders where+  put = serialize+  get = deserialize++-- | 'BlockHeader' type with a transaction count as 'VarInt'+type BlockHeaderCount = (BlockHeader, VarInt)++-- | The 'Headers' type is used to return a list of block headers in+-- response to a 'GetHeaders' message.+newtype Headers = Headers+  { -- | list of block headers with transaction count+    list :: [BlockHeaderCount]+  }+  deriving (Eq, Show, Read, Generic, NFData)++instance Serial Headers where+  deserialize = Headers <$> (repList =<< deserialize)+    where+      repList (VarInt c) = replicateM (fromIntegral c) action+      action = liftM2 (,) deserialize deserialize+  serialize (Headers xs) = do+    putVarInt $ length xs+    forM_ xs $ \(a, b) -> serialize a >> serialize b++instance Serialize Headers where+  put = serialize+  get = deserialize++instance Binary Headers where+  put = serialize+  get = deserialize++-- | Decode the compact number used in the difficulty target of a block.+--+-- The compact format is a representation of a whole number \(N\) using an+-- unsigned 32-bit number similar to a floating point format. The most+-- significant 8 bits are the unsigned exponent of base 256. This exponent can+-- be thought of as the number of bytes of \(N\). The lower 23 bits are the+-- mantissa. Bit number 24 represents the sign of \(N\).+--+-- \[+-- N = -1^{sign} \times mantissa \times 256^{exponent-3}+-- \]+decodeCompact ::+  Word32 ->+  -- | true means overflow+  (Integer, Bool)+decodeCompact nCompact = (if neg then res * (-1) else res, over)+  where+    nSize :: Int+    nSize = fromIntegral nCompact `shiftR` 24+    nWord' :: Word32+    nWord' = nCompact .&. 0x007fffff+    nWord :: Word32+    nWord+      | nSize <= 3 = nWord' `shiftR` (8 * (3 - nSize))+      | otherwise = nWord'+    res :: Integer+    res+      | nSize <= 3 = fromIntegral nWord+      | otherwise = fromIntegral nWord `shiftL` (8 * (nSize - 3))+    neg = nWord /= 0 && (nCompact .&. 0x00800000) /= 0+    over =+      nWord /= 0+        && ( nSize > 34+               || nWord > 0xff && nSize > 33+               || nWord > 0xffff && nSize > 32+           )++-- | Encode an 'Integer' to the compact number format used in the difficulty+-- target of a block.+encodeCompact :: Integer -> Word32+encodeCompact i = nCompact+  where+    i' = abs i+    neg = i < 0+    nSize' :: Int+    nSize' =+      let f 0 = 0+          f n = 1 + f (n `shiftR` 8)+       in f i'+    nCompact''' :: Word32+    nCompact'''+      | nSize' <= 3 = fromIntegral $ (low64 .&. i') `shiftL` (8 * (3 - nSize'))+      | otherwise = fromIntegral $ low64 .&. (i' `shiftR` (8 * (nSize' - 3)))+    nCompact'' :: Word32+    nSize :: Int+    (nCompact'', nSize)+      | nCompact''' .&. 0x00800000 /= 0 = (nCompact''' `shiftR` 8, nSize' + 1)+      | otherwise = (nCompact''', nSize')+    nCompact' :: Word32+    nCompact' = nCompact'' .|. (fromIntegral nSize `shiftL` 24)+    nCompact :: Word32+    nCompact+      | neg && (nCompact' .&. 0x007fffff /= 0) = nCompact' .|. 0x00800000+      | otherwise = nCompact'+    low64 :: Integer+    low64 = 0xffffffffffffffff
+ src/Haskoin/Block/Headers.hs view
@@ -0,0 +1,974 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Block.Headers+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Block chain header synchronization and proof-of-work consensus functions.+module Haskoin.Block.Headers+  ( -- * Block Headers+    BlockNode (..),+    BlockHeaders (..),+    BlockWork,+    genesisNode,+    genesisBlock,+    isGenesis,+    chooseBest,++    -- ** Header Store+    parentBlock,+    getParents,+    getAncestor,+    splitPoint,+    connectBlocks,+    connectBlock,+    blockLocator,++    -- ** Header Memory Store+    HeaderMemory (..),+    ShortBlockHash,+    BlockMap,+    shortBlockHash,+    initialChain,+    genesisMap,++    -- ** Helper Functions+    appendBlocks,+    validBlock,+    validCP,+    afterLastCP,+    bip34,+    validVersion,+    lastNoMinDiff,+    nextWorkRequired,+    nextEdaWorkRequired,+    nextDaaWorkRequired,+    nextAsertWorkRequired,+    computeAsertBits,+    computeTarget,+    getSuitableBlock,+    nextPowWorkRequired,+    calcNextWork,+    isValidPOW,+    blockPOW,+    headerWork,+    diffInterval,+    blockLocatorNodes,+    mineBlock,+    computeSubsidy,+    mtp,+    firstGreaterOrEqual,+    lastSmallerOrEqual,+  )+where++import Control.Applicative ((<|>))+import Control.DeepSeq+import Control.Monad (guard, mzero, unless, when)+import Control.Monad.Except (ExceptT (..), runExceptT, throwError)+import Control.Monad.State.Strict as State (StateT, get, gets, lift, modify)+import Control.Monad.Trans.Maybe+import Data.Binary (Binary (..))+import Data.Bits (shiftL, shiftR, (.&.))+import Data.ByteString qualified as B+import Data.ByteString.Short (ShortByteString, fromShort, toShort)+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Function (on)+import Data.HashMap.Strict (HashMap)+import Data.HashMap.Strict qualified as HashMap+import Data.Hashable+import Data.List (sort, sortBy)+import Data.Maybe (fromMaybe, listToMaybe)+import Data.Serialize (Serialize (..))+import Data.Typeable (Typeable)+import Data.Word (Word32, Word64)+import GHC.Generics (Generic)+import Haskoin.Block.Common+import Haskoin.Crypto+import Haskoin.Network.Data+import Haskoin.Transaction.Genesis+import Haskoin.Util++-- | Short version of the block hash. Uses the good end of the hash (the part+-- that doesn't have a long string of zeroes).+type ShortBlockHash = Word64++-- | Memory-based map to a serialized 'BlockNode' data structure.+-- 'ShortByteString' is used to avoid memory fragmentation and make the data+-- structure compact.+type BlockMap = HashMap ShortBlockHash ShortByteString++-- | Represents accumulated work in the block chain so far.+type BlockWork = Integer++-- | Data structure representing a block header and its position in the+-- block chain.+data BlockNode = BlockNode+  { header :: !BlockHeader,+    height :: !BlockHeight,+    -- | accumulated work so far+    work :: !BlockWork,+    -- | skip magic block hash+    skip :: !BlockHash+  }+  deriving (Show, Read, Generic, Hashable, NFData)++instance Serial BlockNode where+  deserialize = do+    header <- deserialize+    height <- getWord32le+    work <- getInteger+    if height == 0+      then do+        let skip = headerHash header+        return BlockNode {..}+      else do+        skip <- deserialize+        return BlockNode {..}+  serialize bn = do+    serialize $ bn.header+    putWord32le $ bn.height+    putInteger $ bn.work+    case bn.height of+      0 -> return ()+      _ -> serialize $ bn.skip++instance Serialize BlockNode where+  put = serialize+  get = deserialize++instance Binary BlockNode where+  put = serialize+  get = deserialize++instance Eq BlockNode where+  (==) = (==) `on` (.header)++instance Ord BlockNode where+  compare = compare `on` (.height)++-- | Memory-based header tree.+data HeaderMemory = HeaderMemory+  { blocks :: !BlockMap,+    best :: !BlockNode+  }+  deriving (Eq, Typeable, Show, Read, Generic, Hashable, NFData)++-- | Typeclass for block header chain storage monad.+class (Monad m) => BlockHeaders m where+  -- | Add a new 'BlockNode' to the chain. Does not validate.+  addBlockHeader :: BlockNode -> m ()++  -- | Get a 'BlockNode' associated with a 'BlockHash'.+  getBlockHeader :: BlockHash -> m (Maybe BlockNode)++  -- | Locate the 'BlockNode' for the highest block in the chain+  getBestBlockHeader :: m BlockNode++  -- | Set the highest block in the chain.+  setBestBlockHeader :: BlockNode -> m ()++  -- | Add a continuous bunch of block headers the chain. Does not validate.+  addBlockHeaders :: [BlockNode] -> m ()+  addBlockHeaders = mapM_ addBlockHeader++instance (Monad m) => BlockHeaders (StateT HeaderMemory m) where+  addBlockHeader = modify . addBlockHeaderMemory+  getBlockHeader bh = getBlockHeaderMemory bh <$> State.get+  getBestBlockHeader = gets (.best)+  setBestBlockHeader bn = modify $ \s -> s {best = bn}++-- | Initialize memory-based chain.+initialChain :: Network -> HeaderMemory+initialChain net =+  HeaderMemory+    { blocks = genesisMap net,+      best = genesisNode net+    }++-- | Initialize map for memory-based chain.+genesisMap :: Network -> BlockMap+genesisMap net =+  HashMap.singleton+    (shortBlockHash (headerHash net.genesisHeader))+    (toShort (runPutS (serialize (genesisNode net))))++-- | Add block header to memory block map.+addBlockHeaderMemory :: BlockNode -> HeaderMemory -> HeaderMemory+addBlockHeaderMemory bn s = s {blocks = addBlockToMap bn s.blocks}++-- | Get block header from memory block map.+getBlockHeaderMemory :: BlockHash -> HeaderMemory -> Maybe BlockNode+getBlockHeaderMemory bh s = do+  bs <- shortBlockHash bh `HashMap.lookup` s.blocks+  eitherToMaybe . runGetS deserialize $ fromShort bs++-- | Calculate short block hash taking eight non-zero bytes from the 16-byte+-- hash. This function will take the bytes that are not on the zero-side of the+-- hash, making colissions between short block hashes difficult.+shortBlockHash :: BlockHash -> ShortBlockHash+shortBlockHash =+  either error id . runGetS deserialize . B.take 8 . runPutS . serialize++-- | Add a block to memory-based block map.+addBlockToMap :: BlockNode -> BlockMap -> BlockMap+addBlockToMap node =+  HashMap.insert+    (shortBlockHash $ headerHash $ node.header)+    (toShort $ runPutS $ serialize node)++-- | Get the ancestor of the provided 'BlockNode' at the specified+-- 'BlockHeight'.+getAncestor ::+  (BlockHeaders m) =>+  BlockHeight ->+  BlockNode ->+  m (Maybe BlockNode)+getAncestor height node+  | height > node.height = return Nothing+  | otherwise = go node+  where+    e1 = error "Could not get current walk skip"+    e2 = error "Could not get previous walk skip"+    go walk+      | walk.height > height =+          let height_b = skipHeight (walk.height)+              height_a = skipHeight (walk.height - 1)+              not_genesis = not (isGenesis walk)+              is_b = height_b == height+              below_b = height_b > height+              at_or_below_a = height <= height_a+              far_enough = height_b - 2 > height_a && at_or_below_a+              recurse_b = below_b && not far_enough+              cond = not_genesis && (is_b || recurse_b)+           in if cond+                then do+                  walk' <- fromMaybe e1 <$> getBlockHeader walk.skip+                  go walk'+                else do+                  walk' <- fromMaybe e2 <$> getBlockHeader walk.header.prev+                  go walk'+      | otherwise = return $ Just walk++-- | Is the provided 'BlockNode' the Genesis block?+isGenesis :: BlockNode -> Bool+isGenesis BlockNode {height = 0} = True+isGenesis _ = False++-- | Build the genesis 'BlockNode' for the supplied 'Network'.+genesisNode :: Network -> BlockNode+genesisNode net =+  BlockNode+    { header = net.genesisHeader,+      height = 0,+      work = headerWork net.genesisHeader,+      skip = headerHash net.genesisHeader+    }++-- | Validate a list of continuous block headers and import them to the+-- block chain. Return 'Left' on failure with error information.+connectBlocks ::+  (BlockHeaders m) =>+  Network ->+  -- | current time+  Timestamp ->+  [BlockHeader] ->+  m (Either String [BlockNode])+connectBlocks _ _ [] = return $ Right []+connectBlocks net t bhs@(bh : _) =+  runExceptT $ do+    unless (chained bhs) $+      throwError "Blocks to connect do not form a chain"+    par <-+      maybeToExceptT+        "Could not get parent block"+        (MaybeT (parentBlock bh))+    pars <- lift $ getParents 10 par+    bb <- lift getBestBlockHeader+    go par [] bb par pars bhs >>= \case+      bns@(bn : _) -> do+        lift $ addBlockHeaders bns+        let bb' = chooseBest bn bb+        when (bb' /= bb) $ lift $ setBestBlockHeader bb'+        return bns+      _ -> undefined+  where+    chained (h1 : h2 : hs) = headerHash h1 == h2.prev && chained (h2 : hs)+    chained _ = True+    skipit lbh ls par+      | sh == lbh.height = return lbh+      | sh < lbh.height = do+          skM <- lift $ getAncestor sh lbh+          case skM of+            Just sk -> return sk+            Nothing ->+              throwError $+                "BUG: Could not get skip for block "+                  ++ show (headerHash $ par.header)+      | otherwise = do+          let sn = ls !! fromIntegral (par.height - sh)+          when (sn.height /= sh) $+            throwError "BUG: Node height not right in skip"+          return sn+      where+        sh = skipHeight (par.height + 1)+    go _ acc _ _ _ [] = return acc+    go lbh acc bb par pars (h : hs) = do+      sk <- skipit lbh acc par+      bn <- ExceptT . return $ validBlock net t bb par pars h sk+      go lbh (bn : acc) (chooseBest bn bb) bn (take 10 $ par : pars) hs++-- | Block's parent. If the block header is in the store, its parent must also+-- be there. No block header get deleted or pruned from the store.+parentBlock ::+  (BlockHeaders m) =>+  BlockHeader ->+  m (Maybe BlockNode)+parentBlock = getBlockHeader . (.prev)++-- | Validate and connect single block header to the block chain. Return 'Left'+-- if fails to be validated.+connectBlock ::+  (BlockHeaders m) =>+  Network ->+  -- | current time+  Timestamp ->+  BlockHeader ->+  m (Either String BlockNode)+connectBlock net t bh =+  runExceptT $ do+    par <-+      maybeToExceptT+        "Could not get parent block"+        (MaybeT (parentBlock bh))+    pars <- lift $ getParents 10 par+    skM <- lift $ getAncestor (skipHeight (par.height + 1)) par+    sk <-+      case skM of+        Just sk -> return sk+        Nothing ->+          throwError $+            "BUG: Could not get skip for block "+              ++ show (headerHash $ par.header)+    bb <- lift getBestBlockHeader+    bn <- ExceptT . return $ validBlock net t bb par pars bh sk+    let bb' = chooseBest bb bn+    lift $ addBlockHeader bn+    when (bb /= bb') . lift $ setBestBlockHeader bb'+    return bn++-- | Validate this block header. Build a 'BlockNode' if successful.+validBlock ::+  Network ->+  -- | current time+  Timestamp ->+  -- | best block+  BlockNode ->+  -- | immediate parent+  BlockNode ->+  -- | 10 parents above+  [BlockNode] ->+  -- | header to validate+  BlockHeader ->+  -- | skip node (black magic)+  BlockNode ->+  Either String BlockNode+validBlock net t bb par pars bh sk = do+  let mt = medianTime . map (.header.timestamp) $ par : pars+      nt = bh.timestamp+      hh = headerHash bh+      nv = bh.version+      ng = par.height + 1+      aw = par.work + headerWork bh+  unless (isValidPOW net bh) $+    Left $+      "Proof of work failed: " ++ show (headerHash bh)+  unless (nt <= t + 2 * 60 * 60) $+    Left $+      "Invalid header timestamp: " ++ show nt+  unless (nt >= mt) $+    Left $+      "Block timestamp too early: " ++ show nt+  unless (afterLastCP net (bb.height) ng) $+    Left $+      "Rewriting pre-checkpoint chain: " ++ show ng+  unless (validCP net ng hh) $+    Left $+      "Rejected checkpoint: " ++ show ng+  unless (bip34 net ng hh) $+    Left $+      "Rejected BIP-34 block: " ++ show hh+  unless (validVersion net ng nv) $+    Left $+      "Invalid block version: " ++ show nv+  return+    BlockNode+      { header = bh,+        height = ng,+        work = aw,+        skip = headerHash sk.header+      }++-- | Return the median of all provided timestamps. Can be unsorted. Error on+-- empty list.+medianTime :: [Timestamp] -> Timestamp+medianTime ts+  | null ts = error "Cannot compute median time of empty header list"+  | otherwise = sort ts !! (length ts `div` 2)++-- | Calculate the height of the skip (magic) block that corresponds to the+-- given height. The block hash of the ancestor at that height will be placed on+-- the 'BlockNode' structure to help locate ancestors at any height quickly.+skipHeight :: BlockHeight -> BlockHeight+skipHeight height+  | height < 2 = 0+  | height .&. 1 /= 0 = invertLowestOne (invertLowestOne $ height - 1) + 1+  | otherwise = invertLowestOne height++-- | Part of the skip black magic calculation.+invertLowestOne :: BlockHeight -> BlockHeight+invertLowestOne height = height .&. (height - 1)++-- | Get a number of parents for the provided block.+getParents ::+  (BlockHeaders m) =>+  Int ->+  BlockNode ->+  -- | starts from immediate parent+  m [BlockNode]+getParents = getpars []+  where+    getpars acc 0 _ = return $ reverse acc+    getpars acc n BlockNode {..}+      | height == 0 = return $ reverse acc+      | otherwise = do+          parM <- getBlockHeader header.prev+          case parM of+            Just bn -> getpars (bn : acc) (n - 1) bn+            Nothing -> error "BUG: All non-genesis blocks should have a parent"++-- | Verify that checkpoint location is valid.+validCP ::+  Network ->+  -- | new child height+  BlockHeight ->+  -- | new child hash+  BlockHash ->+  Bool+validCP net height newChildHash =+  case lookup height net.checkpoints of+    Just cpHash -> cpHash == newChildHash+    Nothing -> True++-- | New block height above the last checkpoint imported. Used to prevent a+-- reorg below the highest checkpoint that was already imported.+afterLastCP ::+  Network ->+  -- | best height+  BlockHeight ->+  -- | new imported block height+  BlockHeight ->+  Bool+afterLastCP net bestHeight newChildHeight =+  case lM of+    Just l -> l < newChildHeight+    Nothing -> True+  where+    lM =+      listToMaybe . reverse $+        [c | (c, _) <- net.checkpoints, c <= bestHeight]++-- | This block should be at least version 2 (BIP34). Block height must be+-- included in the coinbase transaction to prevent non-unique transaction+-- hashes.+bip34 ::+  Network ->+  -- | new child height+  BlockHeight ->+  -- | new child hash+  BlockHash ->+  Bool+bip34 net height hsh+  | fst net.bip34Block == 0 = True+  | fst net.bip34Block == height = snd net.bip34Block == hsh+  | otherwise = True++-- | Check if the provided block height and version are valid.+validVersion ::+  Network ->+  -- | new child height+  BlockHeight ->+  -- | new child version+  Word32 ->+  Bool+validVersion net height version+  | version < 2 = height < fst net.bip34Block+  | version < 3 = height < net.bip66Height+  | version < 4 = height < net.bip65Height+  | otherwise = True++-- | Find last block with normal, as opposed to minimum difficulty (for test+-- networks).+lastNoMinDiff :: (BlockHeaders m) => Network -> BlockNode -> m BlockNode+lastNoMinDiff _ bn@BlockNode {height = 0} = return bn+lastNoMinDiff net bn = do+  let i = bn.height `mod` diffInterval net /= 0+      c = encodeCompact net.powLimit+      l = bn.header.bits == c+      e1 =+        error $+          "Could not get block header for parent of "+            ++ show (headerHash bn.header)+  if i && l+    then do+      bn' <- fromMaybe e1 <$> getBlockHeader (bn.header.prev)+      lastNoMinDiff net bn'+    else return bn++-- | Returns the work required on a block header given the previous block. This+-- coresponds to @bitcoind@ function @GetNextWorkRequired@ in @main.cpp@.+nextWorkRequired ::+  (BlockHeaders m) =>+  Network ->+  BlockNode ->+  BlockHeader ->+  m Word32+nextWorkRequired net par bh = do+  ma <- getAsertAnchor net+  case asert ma <|> daa <|> eda <|> pow of+    Just f -> f par bh+    Nothing -> error "Could not determine difficulty algorithm"+  where+    asert ma = do+      anchor <- ma+      guard (par.height > anchor.height)+      return $ nextAsertWorkRequired net anchor+    daa = do+      daa_height <- net.daaHeight+      guard (par.height + 1 >= daa_height)+      return $ nextDaaWorkRequired net+    eda = do+      eda_height <- net.edaHeight+      guard (par.height + 1 >= eda_height)+      return $ nextEdaWorkRequired net+    pow = return $ nextPowWorkRequired net++-- | Find out the next amount of work required according to the Emergency+-- Difficulty Adjustment (EDA) algorithm from Bitcoin Cash.+nextEdaWorkRequired ::+  (BlockHeaders m) => Network -> BlockNode -> BlockHeader -> m Word32+nextEdaWorkRequired net par bh+  | par.height + 1 `mod` diffInterval net == 0 =+      nextWorkRequired net par bh+  | mindiff = return (encodeCompact net.powLimit)+  | par.header.bits == encodeCompact net.powLimit =+      return (encodeCompact net.powLimit)+  | otherwise = do+      par6 <- fromMaybe e1 <$> getAncestor (par.height - 6) par+      pars <- getParents 10 par+      pars6 <- getParents 10 par6+      let par6med =+            medianTime $ map (.header.timestamp) (par6 : pars6)+          parmed = medianTime $ map (.header.timestamp) (par : pars)+          mtp6 = parmed - par6med+      if mtp6 < 12 * 3600+        then return $ par.header.bits+        else+          return $+            let (diff, _) = decodeCompact par.header.bits+                ndiff = diff + (diff `shiftR` 2)+             in if net.powLimit > ndiff+                  then encodeCompact net.powLimit+                  else encodeCompact ndiff+  where+    mindiff = bh.timestamp > par.header.timestamp + net.targetSpacing * 2+    e1 = error "Could not get seventh ancestor of block"++-- | Find the next amount of work required according to the Difficulty+-- Adjustment Algorithm (DAA) from Bitcoin Cash.+nextDaaWorkRequired ::+  (BlockHeaders m) => Network -> BlockNode -> BlockHeader -> m Word32+nextDaaWorkRequired net par bh+  | mindiff = return (encodeCompact net.powLimit)+  | otherwise = do+      unless (par.height >= diffInterval net) $+        error "Block height below difficulty interval"+      l <- getSuitableBlock par+      par144 <- fromMaybe e1 <$> getAncestor (par.height - 144) par+      f <- getSuitableBlock par144+      let nextTarget = computeTarget net f l+      if nextTarget > net.powLimit+        then return $ encodeCompact net.powLimit+        else return $ encodeCompact nextTarget+  where+    e1 = error "Cannot get ancestor at parent - 144 height"+    mindiff = bh.timestamp > par.header.timestamp + net.targetSpacing * 2++mtp :: (BlockHeaders m) => BlockNode -> m Timestamp+mtp bn+  | bn.height == 0 = return 0+  | otherwise = do+      pars <- getParents 11 bn+      return $ medianTime (map (.header.timestamp) pars)++firstGreaterOrEqual ::+  (BlockHeaders m) =>+  Network ->+  (BlockNode -> m Ordering) ->+  m (Maybe BlockNode)+firstGreaterOrEqual = binSearch False++lastSmallerOrEqual ::+  (BlockHeaders m) =>+  Network ->+  (BlockNode -> m Ordering) ->+  m (Maybe BlockNode)+lastSmallerOrEqual = binSearch True++binSearch ::+  (BlockHeaders m) =>+  Bool ->+  Network ->+  (BlockNode -> m Ordering) ->+  m (Maybe BlockNode)+binSearch top net f = runMaybeT $ do+  (a, b) <- lift $ extremes net+  go a b+  where+    go a b = do+      m <- lift $ middleBlock a b+      a' <- lift $ f a+      b' <- lift $ f b+      m' <- lift $ f m+      r (a, a') (b, b') (m, m')+    r (a, a') (b, b') (m, m')+      | out_of_bounds a' b' = mzero+      | select_first a' = return a+      | select_last b' = return b+      | no_middle a b = choose_one a b+      | is_between a' m' = go a m+      | is_between m' b' = go m b+      | otherwise = mzero+    select_first a'+      | not top = a' /= LT+      | otherwise = False+    select_last b'+      | top = b' /= GT+      | otherwise = False+    out_of_bounds a' b'+      | top = a' == GT+      | otherwise = b' == LT+    no_middle a b = b.height - a.height <= 1+    is_between a' b' = a' /= GT && b' /= LT+    choose_one a b+      | top = return a+      | otherwise = return b++extremes :: (BlockHeaders m) => Network -> m (BlockNode, BlockNode)+extremes net = do+  b <- getBestBlockHeader+  return (genesisNode net, b)++middleBlock :: (BlockHeaders m) => BlockNode -> BlockNode -> m BlockNode+middleBlock a b =+  getAncestor h b >>= \case+    Nothing -> error "You fell into a pit full of mud and snakes"+    Just x -> return x+  where+    h = middleOf a.height b.height++middleOf :: (Integral a) => a -> a -> a+middleOf a b = a + ((b - a) `div` 2)++-- TODO: Use known anchor after fork+getAsertAnchor :: (BlockHeaders m) => Network -> m (Maybe BlockNode)+getAsertAnchor net =+  case net.asertActivationTime of+    Nothing -> return Nothing+    Just act -> firstGreaterOrEqual net (f act)+  where+    f act bn = do+      m <- mtp bn+      return $ compare m act++-- | Find the next amount of work required according to the aserti3-2d algorithm.+nextAsertWorkRequired ::+  (BlockHeaders m) =>+  Network ->+  BlockNode ->+  BlockNode ->+  BlockHeader ->+  m Word32+nextAsertWorkRequired net anchor par bh = do+  anchor_parent <-+    fromMaybe e_fork <$> getBlockHeader anchor.header.prev+  let anchor_parent_time = toInteger anchor_parent.header.timestamp+      time_diff = current_time - anchor_parent_time+  return $ computeAsertBits halflife anchor_bits time_diff height_diff+  where+    halflife = net.asertHalfLife+    anchor_height = toInteger anchor.height+    anchor_bits = anchor.header.bits+    current_height = toInteger par.height + 1+    height_diff = current_height - anchor_height+    current_time = toInteger bh.timestamp+    e_fork = error "Could not get fork block header"++idealBlockTime :: Integer+idealBlockTime = 10 * 60++rBits :: Int+rBits = 16++radix :: Integer+radix = 1 `shiftL` rBits++maxBits :: Word32+maxBits = 0x1d00ffff++maxTarget :: Integer+maxTarget = fst $ decodeCompact maxBits++computeAsertBits ::+  Integer ->+  Word32 ->+  Integer ->+  Integer ->+  Word32+computeAsertBits halflife anchor_bits time_diff height_diff =+  if e2 >= 0 && e2 < 65536+    then+      if g4 == 0+        then encodeCompact 1+        else+          if g4 > maxTarget+            then maxBits+            else encodeCompact g4+    else error $ "Exponent not in range: " ++ show e2+  where+    g1 = fst (decodeCompact anchor_bits)+    e1 =+      ((time_diff - idealBlockTime * (height_diff + 1)) * radix)+        `quot` halflife+    s = e1 `shiftR` rBits+    e2 = e1 - s * radix+    g2 =+      g1+        * ( radix+              + ( (195766423245049 * e2 + 971821376 * e2 ^ 2 + 5127 * e2 ^ 3 + 2 ^ 47)+                    `shiftR` (rBits * 3)+                )+          )+    g3 =+      if s < 0+        then g2 `shiftR` negate (fromIntegral s)+        else g2 `shiftL` fromIntegral s+    g4 = g3 `shiftR` rBits++-- | Compute Bitcoin Cash DAA target for a new block.+computeTarget :: Network -> BlockNode -> BlockNode -> Integer+computeTarget net f l =+  let work = (l.work - f.work) * fromIntegral net.targetSpacing+      tspan = l.header.timestamp - f.header.timestamp+      tspan'+        | tspan > 288 * net.targetSpacing =+            288 * net.targetSpacing+        | tspan < 72 * net.targetSpacing =+            72 * net.targetSpacing+        | otherwise = tspan+      work' = work `div` fromIntegral tspan'+   in 2 ^ (256 :: Integer) `div` work'++-- | Get suitable block for Bitcoin Cash DAA computation.+getSuitableBlock :: (BlockHeaders m) => BlockNode -> m BlockNode+getSuitableBlock par = do+  unless (par.height >= 3) $ error "Block height is less than three"+  blocks <- (par :) <$> getParents 2 par+  return $ sortBy (compare `on` (.header.timestamp)) blocks !! 1++-- | Returns the work required on a block header given the previous block. This+-- coresponds to bitcoind function GetNextWorkRequired in main.cpp.+nextPowWorkRequired ::+  (BlockHeaders m) => Network -> BlockNode -> BlockHeader -> m Word32+nextPowWorkRequired net par bh+  | par.height + 1 `mod` diffInterval net /= 0 =+      if net.minDiffBlocks+        then+          if ht > pt + delta+            then return $ encodeCompact net.powLimit+            else do+              d <- lastNoMinDiff net par+              return d.header.bits+        else return par.header.bits+  | otherwise = do+      let rh = par.height - diffInterval net - 1+      a <- fromMaybe e1 <$> getAncestor rh par+      let t = a.header.timestamp+      return $ calcNextWork net par.header t+  where+    e1 = error "Could not get ancestor for block header"+    pt = par.header.timestamp+    ht = bh.timestamp+    delta = net.targetSpacing * 2++-- | Computes the work required for the first block in a new retarget period.+calcNextWork ::+  Network ->+  -- | last block in previous retarget (parent)+  BlockHeader ->+  -- | timestamp of first block in previous retarget+  Timestamp ->+  Word32+calcNextWork net header time+  | net.powNoRetarget = header.bits+  | new > net.powLimit = encodeCompact net.powLimit+  | otherwise = encodeCompact new+  where+    s = header.timestamp - time+    n+      | s < net.targetTimespan `div` 4 = net.targetTimespan `div` 4+      | s > net.targetTimespan * 4 = net.targetTimespan * 4+      | otherwise = s+    l = fst $ decodeCompact header.bits+    new = l * fromIntegral n `div` fromIntegral net.targetTimespan++-- | Returns True if the difficulty target (bits) of the header is valid and the+-- proof of work of the header matches the advertised difficulty target. This+-- function corresponds to the function @CheckProofOfWork@ from @bitcoind@ in+-- @main.cpp@.+isValidPOW :: Network -> BlockHeader -> Bool+isValidPOW net h+  | target <= 0 || over || target > net.powLimit = False+  | otherwise = blockPOW (headerHash h) <= fromIntegral target+  where+    (target, over) = decodeCompact h.bits++-- | Returns the proof of work of a block header hash as an 'Integer' number.+blockPOW :: BlockHash -> Integer+blockPOW = bsToInteger . B.reverse . runPutS . serialize++-- | Returns the work represented by this block. Work is defined as the number+-- of tries needed to solve a block in the average case with respect to the+-- target.+headerWork :: BlockHeader -> Integer+headerWork bh = largestHash `div` (target + 1)+  where+    target = fst $ decodeCompact bh.bits+    largestHash = 1 `shiftL` 256++-- | Number of blocks on average between difficulty cycles (2016 blocks).+diffInterval :: Network -> Word32+diffInterval net = net.targetTimespan `div` net.targetSpacing++-- | Compare two blocks to get the best.+chooseBest :: BlockNode -> BlockNode -> BlockNode+chooseBest b1 b2+  | b1.work == b2.work =+      if b1.height >= b2.height+        then b1+        else b2+  | b1.work > b2.work = b1+  | otherwise = b2++-- | Get list of blocks for a block locator.+blockLocatorNodes :: (BlockHeaders m) => BlockNode -> m [BlockNode]+blockLocatorNodes best =+  reverse <$> go [] best 1+  where+    e1 = error "Could not get ancestor"+    go loc bn n =+      let loc' = bn : loc+          n' =+            if length loc' > 10+              then n * 2+              else 1+       in if bn.height < n'+            then do+              a <- fromMaybe e1 <$> getAncestor 0 bn+              return $ a : loc'+            else do+              let h = bn.height - n'+              bn' <- fromMaybe e1 <$> getAncestor h bn+              go loc' bn' n'++-- | Get block locator.+blockLocator :: (BlockHeaders m) => BlockNode -> m BlockLocator+blockLocator bn = map (headerHash . (.header)) <$> blockLocatorNodes bn++-- | Become rich beyond your wildest dreams.+mineBlock :: Network -> Word32 -> BlockHeader -> BlockHeader+mineBlock net seed h =+  head+    [ j+      | i <- (+ seed) <$> [0 .. maxBound],+        let j = h {nonce = i},+        isValidPOW net j+    ]++-- | Generate and append new blocks (mining). Only practical in regtest network.+appendBlocks ::+  Network ->+  -- | random seed+  Word32 ->+  BlockHeader ->+  Int ->+  [BlockHeader]+appendBlocks _ _ _ 0 = []+appendBlocks net seed bh i =+  bh' : appendBlocks net seed bh' (i - 1)+  where+    bh' =+      mineBlock+        net+        seed+        bh+          { prev = headerHash bh,+            -- Just to make it different in every header+            merkle = sha256 $ runPutS $ serialize seed+          }++-- | Find the last common block ancestor between provided block headers.+splitPoint :: (BlockHeaders m) => BlockNode -> BlockNode -> m BlockNode+splitPoint l r = do+  let h = min l.height r.height+  ll <- fromMaybe e <$> getAncestor h l+  lr <- fromMaybe e <$> getAncestor h r+  f ll lr+  where+    e = error "BUG: Could not get ancestor at lowest height"+    f ll lr =+      if ll == lr+        then return lr+        else do+          let h = ll.height - 1+          pl <- fromMaybe e <$> getAncestor h ll+          pr <- fromMaybe e <$> getAncestor h lr+          f pl pr++-- | Generate the entire Genesis block for 'Network'.+genesisBlock :: Network -> Ctx -> Block+genesisBlock net ctx = Block net.genesisHeader [genesisTx ctx]++-- | Compute block subsidy at particular height.+computeSubsidy :: Network -> BlockHeight -> Word64+computeSubsidy net height =+  let halvings = height `div` net.halvingInterval+      ini = 50 * 100 * 1000 * 1000+   in if halvings >= 64+        then 0+        else ini `shiftR` fromIntegral halvings
+ src/Haskoin/Block/Merkle.hs view
@@ -0,0 +1,302 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Block.Merkle+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Function to deal with Merkle trees inside blocks.+module Haskoin.Block.Merkle+  ( -- * Merkle Blocks+    MerkleBlock (..),+    MerkleRoot,+    FlagBits,+    PartialMerkleTree,+    buildMerkleRoot,+    merkleBlockTxs,+    testMerkleRoot,+    buildPartialMerkle,+    decodeMerkleFlags,+    encodeMerkleFlags,+    calcTreeHeight,+    calcTreeWidth,+    hash2,+    calcHash,+    traverseAndBuild,+    traverseAndExtract,+    extractMatches,+    splitIn,+    boolsToWord8,+  )+where++import Control.DeepSeq+import Control.Monad (forM_, replicateM, when)+import Data.Binary (Binary (..))+import Data.Bits+import qualified Data.ByteString as BS+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Either (isRight)+import Data.Hashable+import Data.Maybe+import Data.Serialize (Serialize (..))+import Data.Word (Word32, Word8)+import GHC.Generics+import Haskoin.Block.Common+import Haskoin.Crypto.Hash+import Haskoin.Network.Common+import Haskoin.Network.Data+import Haskoin.Transaction.Common++-- | Hash of the block's Merkle root.+type MerkleRoot = Hash256++-- | Bits that are used to rebuild partial merkle tree transaction hash list.+type FlagBits = [Bool]++-- | Partial Merkle tree for a filtered block.+type PartialMerkleTree = [Hash256]++-- | Filtered block: a block with a partial Merkle tree that only includes the+-- transactions that pass a bloom filter that was negotiated.+data MerkleBlock = MerkleBlock+  { -- | block header+    header :: !BlockHeader,+    -- | total number of transactions in block+    txn :: !Word32,+    -- | hashes in depth-first order+    hashes :: !PartialMerkleTree,+    -- | bits to rebuild partial merkle tree+    flags :: !FlagBits+  }+  deriving (Eq, Show, Read, Generic, Hashable, NFData)++instance Serial MerkleBlock where+  deserialize = do+    header <- deserialize+    ntx <- getWord32le+    (VarInt matchLen) <- deserialize+    hashes <- replicateM (fromIntegral matchLen) deserialize+    (VarInt flagLen) <- deserialize+    ws <- replicateM (fromIntegral flagLen) getWord8+    return $ MerkleBlock header ntx hashes (decodeMerkleFlags ws)++  serialize (MerkleBlock h ntx hashes flags) = do+    serialize h+    putWord32le ntx+    putVarInt $ length hashes+    forM_ hashes serialize+    let ws = encodeMerkleFlags flags+    putVarInt $ length ws+    forM_ ws putWord8++instance Binary MerkleBlock where+  put = serialize+  get = deserialize++instance Serialize MerkleBlock where+  put = serialize+  get = deserialize++-- | Unpack Merkle flags into 'FlagBits' structure.+decodeMerkleFlags :: [Word8] -> FlagBits+decodeMerkleFlags ws =+  [ b | p <- [0 .. length ws * 8 - 1], b <- [testBit (ws !! (p `div` 8)) (p `mod` 8)]+  ]++-- | Pack Merkle flags from 'FlagBits'.+encodeMerkleFlags :: FlagBits -> [Word8]+encodeMerkleFlags bs = map boolsToWord8 $ splitIn 8 bs++-- | Computes the height of a Merkle tree.+calcTreeHeight ::+  -- | number of transactions (leaf nodes)+  Int ->+  -- | height of the merkle tree+  Int+calcTreeHeight ntx+  | ntx < 2 = 0+  | even ntx = 1 + calcTreeHeight (ntx `div` 2)+  | otherwise = calcTreeHeight $ ntx + 1++-- | Computes the width of a Merkle tree at a specific height. The transactions+-- are at height 0.+calcTreeWidth ::+  -- | number of transactions (leaf nodes)+  Int ->+  -- | height at which we want to compute the width+  Int ->+  -- | width of the Merkle tree+  Int+calcTreeWidth ntx h = (ntx + (1 `shiftL` h) - 1) `shiftR` h++-- | Computes the root of a Merkle tree from a list of leaf node hashes.+buildMerkleRoot ::+  -- | transaction hashes (leaf nodes)+  [TxHash] ->+  -- | root of the Merkle tree+  MerkleRoot+buildMerkleRoot txs = calcHash (calcTreeHeight $ length txs) 0 txs++-- | Concatenate and compute double SHA256.+hash2 :: Hash256 -> Hash256 -> Hash256+hash2 a b = doubleSHA256 $ runPutS (serialize a) <> runPutS (serialize b)++-- | Computes the hash of a specific node in a Merkle tree.+calcHash ::+  -- | height of the node+  Int ->+  -- | position of the node (0 for the leftmost node)+  Int ->+  -- | transaction hashes (leaf nodes)+  [TxHash] ->+  -- | hash of the node at the specified position+  Hash256+calcHash height pos txs+  | height < 0 || pos < 0 = error "calcHash: Invalid parameters"+  | height == 0 = (txs !! pos).get+  | otherwise = hash2 left right+  where+    left = calcHash (height - 1) (pos * 2) txs+    right+      | pos * 2 + 1 < calcTreeWidth (length txs) (height - 1) =+          calcHash (height - 1) (pos * 2 + 1) txs+      | otherwise = left++-- | Build a partial Merkle tree. Provide a list of tuples with all transaction+-- hashes in the block, and whether the transaction is to be included in the+-- partial tree. Returns a flag bits structure and the computed partial Merkle+-- tree.+buildPartialMerkle ::+  -- | transaction hash and whether to include+  [(TxHash, Bool)] ->+  -- | flag bits and partial Merkle tree+  (FlagBits, PartialMerkleTree)+buildPartialMerkle hs = traverseAndBuild (calcTreeHeight $ length hs) 0 hs++-- | Helper function to build partial Merkle tree. Used by 'buildPartialMerkle'+-- above.+traverseAndBuild ::+  Int -> Int -> [(TxHash, Bool)] -> (FlagBits, PartialMerkleTree)+traverseAndBuild height pos txs+  | height < 0 || pos < 0 = error "traverseAndBuild: Invalid parameters"+  | height == 0 || not match = ([match], [calcHash height pos t])+  | otherwise = (match : lb ++ rb, lh ++ rh)+  where+    t = map fst txs+    s = pos `shiftL` height+    e = min (length txs) $ (pos + 1) `shiftL` height+    match = any snd $ take (e - s) $ drop s txs+    (lb, lh) = traverseAndBuild (height - 1) (pos * 2) txs+    (rb, rh)+      | (pos * 2 + 1) < calcTreeWidth (length txs) (height - 1) =+          traverseAndBuild (height - 1) (pos * 2 + 1) txs+      | otherwise = ([], [])++-- | Helper function to extract transaction hashes from partial Merkle tree.+traverseAndExtract ::+  Int ->+  Int ->+  Int ->+  FlagBits ->+  PartialMerkleTree ->+  Maybe (MerkleRoot, [TxHash], Int, Int)+traverseAndExtract height pos ntx flags hashes+  | null flags = Nothing+  | height == 0 || not match = leafResult+  | isNothing leftM = Nothing+  | (pos * 2 + 1) >= calcTreeWidth ntx (height - 1) =+      Just (hash2 lh lh, lm, lcf + 1, lch)+  | isNothing rightM = Nothing+  | otherwise =+      Just (hash2 lh rh, lm ++ rm, lcf + rcf + 1, lch + rch)+  where+    leafResult+      | null hashes = Nothing+      | otherwise = Just (h, [TxHash h | height == 0 && match], 1, 1)+    (match : fs) = flags+    (h : _) = hashes+    leftM = traverseAndExtract (height - 1) (pos * 2) ntx fs hashes+    (lh, lm, lcf, lch) = fromMaybe e leftM+    rightM =+      traverseAndExtract+        (height - 1)+        (pos * 2 + 1)+        ntx+        (drop lcf fs)+        (drop lch hashes)+    (rh, rm, rcf, rch) = fromMaybe e rightM+    e = error "traverseAndExtract: unexpected error extracting a Maybe value"++-- | Extracts the matching hashes from a partial merkle tree. This will return+-- the list of transaction hashes that have been included (set to true) in+-- a call to 'buildPartialMerkle'.+extractMatches ::+  Network ->+  FlagBits ->+  PartialMerkleTree ->+  -- | number of transaction at height 0 (leaf nodes)+  Int ->+  -- | Merkle root and list of matching transaction hashes+  Either String (MerkleRoot, [TxHash])+extractMatches net flags hashes ntx+  | ntx == 0 =+      Left+        "extractMatches: number of transactions can not be 0"+  | ntx > net.maxBlockSize `div` 60 =+      Left+        "extractMatches: number of transactions excessively high"+  | length hashes > ntx =+      Left+        "extractMatches: More hashes provided than the number of transactions"+  | length flags < length hashes =+      Left+        "extractMatches: At least one bit per node and one bit per hash"+  | isNothing resM =+      Left+        "extractMatches: traverseAndExtract failed"+  | (nBitsUsed + 7) `div` 8 /= (length flags + 7) `div` 8 =+      Left+        "extractMatches: All bits were not consumed"+  | nHashUsed /= length hashes =+      Left $+        "extractMatches: All hashes were not consumed: " ++ show nHashUsed+  | otherwise = return (merkRoot, matches)+  where+    resM = traverseAndExtract (calcTreeHeight ntx) 0 ntx flags hashes+    (merkRoot, matches, nBitsUsed, nHashUsed) = fromMaybe e resM+    e = error "extractMatches: unexpected error extracting a Maybe value"++-- | Helper function to split a list in chunks 'Int' length. Last chunk may be+-- smaller.+splitIn :: Int -> [a] -> [[a]]+splitIn _ [] = []+splitIn c xs = xs1 : splitIn c xs2+  where+    (xs1, xs2) = splitAt c xs++-- | Pack up to eight bools in a byte.+boolsToWord8 :: [Bool] -> Word8+boolsToWord8 [] = 0+boolsToWord8 xs = foldl setBit 0 (map snd $ filter fst $ zip xs [0 .. 7])++-- | Get matching transactions from Merkle block.+merkleBlockTxs :: Network -> MerkleBlock -> Either String [TxHash]+merkleBlockTxs net b = do+  (root, ths) <- extractMatches net b.flags b.hashes (fromIntegral b.txn)+  when (root /= b.header.merkle) $+    Left "merkleBlockTxs: Merkle root incorrect"+  return ths++-- | Check if Merkle block root is valid against the block header.+testMerkleRoot :: Network -> MerkleBlock -> Bool+testMerkleRoot net = isRight . merkleBlockTxs net
+ src/Haskoin/Crypto.hs view
@@ -0,0 +1,23 @@+{-# LANGUAGE DuplicateRecordFields #-}++-- |+-- Module      : Haskoin.Crypto+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Hashing functions and ECDSA signatures.+module Haskoin.Crypto+  ( module Secp256k1,+    module Hash,+    module Keys,+    module Signature,+  )+where++import Crypto.Secp256k1 as Secp256k1+import Haskoin.Crypto.Hash as Hash+import Haskoin.Crypto.Keys as Keys+import Haskoin.Crypto.Signature as Signature
+ src/Haskoin/Crypto/Hash.hs view
@@ -0,0 +1,267 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Crypto.Hash+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Hashing functions and corresponding data types. Uses functions from the+-- cryptonite library.+module Haskoin.Crypto.Hash+  ( -- * Hashes+    Hash512 (get),+    Hash256 (get),+    Hash160 (get),+    CheckSum32 (get),+    sha512,+    sha256,+    ripemd160,+    sha1,+    doubleSHA256,+    addressHash,+    checkSum32,+    hmac512,+    hmac256,+    split512,+    join512,+    initTaggedHash,+  )+where++import Control.DeepSeq+import Crypto.Hash+import Crypto.MAC.HMAC (HMAC, hmac)+import Data.Binary (Binary (..))+import Data.ByteArray (ByteArrayAccess, convert)+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.ByteString.Short (ShortByteString, fromShort, toShort)+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial (Serial (..))+import Data.Either (fromRight)+import Data.Function (on)+import Data.Hashable (Hashable)+import Data.Serialize (Serialize (..))+import Data.String (IsString, fromString)+import Data.String.Conversions (cs)+import Data.Void (Void)+import Data.Word (Word32)+import GHC.Generics (Generic)+import Haskoin.Util.Helpers+import Haskoin.Util.Marshal+import Text.Read as R++-- | 'Word32' wrapped for type-safe 32-bit checksums.+newtype CheckSum32 = CheckSum32+  { get :: Word32+  }+  deriving (Eq, Ord, Show, Read, Generic)+  deriving newtype (Hashable, NFData)++instance Serial CheckSum32 where+  serialize (CheckSum32 c) = putWord32be c+  deserialize = CheckSum32 <$> getWord32be++instance Serialize CheckSum32 where+  put = serialize+  get = deserialize++instance Binary CheckSum32 where+  put = serialize+  get = deserialize++-- | Type for 512-bit hashes.+newtype Hash512 = Hash512 {get :: ShortByteString}+  deriving (Eq, Ord, Generic)+  deriving newtype (Hashable, NFData)++-- | Type for 256-bit hashes.+newtype Hash256 = Hash256 {get :: ShortByteString}+  deriving (Eq, Ord, Generic)+  deriving newtype (Hashable, NFData)++-- | Type for 160-bit hashes.+newtype Hash160 = Hash160 {get :: ShortByteString}+  deriving (Eq, Ord, Generic)+  deriving newtype (Hashable, NFData)++instance Show Hash512 where+  showsPrec _ = shows . encodeHex . fromShort . (.get)++instance Read Hash512 where+  readPrec = do+    R.String str <- lexP+    maybe pfail (return . Hash512 . toShort) (decodeHex (cs str))++instance Show Hash256 where+  showsPrec _ = shows . encodeHex . fromShort . (.get)++instance Read Hash256 where+  readPrec = do+    R.String str <- lexP+    maybe pfail (return . Hash256 . toShort) (decodeHex (cs str))++instance Show Hash160 where+  showsPrec _ = shows . encodeHex . fromShort . (.get)++instance Read Hash160 where+  readPrec = do+    R.String str <- lexP+    maybe pfail (return . Hash160 . toShort) (decodeHex (cs str))++instance IsString Hash512 where+  fromString str =+    case decodeHex $ cs str of+      Nothing -> e+      Just bs ->+        case B.length bs of+          64 -> Hash512 (toShort bs)+          _ -> e+    where+      e = error "Could not decode hash from hex string"++instance Serial Hash512 where+  deserialize = Hash512 . toShort <$> getByteString 64+  serialize = putByteString . fromShort . (.get)++instance Serialize Hash512 where+  put = serialize+  get = deserialize++instance Binary Hash512 where+  put = serialize+  get = deserialize++instance IsString Hash256 where+  fromString str =+    case decodeHex $ cs str of+      Nothing -> e+      Just bs ->+        case B.length bs of+          32 -> Hash256 (toShort bs)+          _ -> e+    where+      e = error "Could not decode hash from hex string"++instance Serial Hash256 where+  deserialize = Hash256 . toShort <$> getByteString 32+  serialize = putByteString . fromShort . (.get)++instance Serialize Hash256 where+  put = serialize+  get = deserialize++instance Binary Hash256 where+  put = serialize+  get = deserialize++instance IsString Hash160 where+  fromString str =+    case decodeHex $ cs str of+      Nothing -> e+      Just bs ->+        case B.length bs of+          20 -> Hash160 (toShort bs)+          _ -> e+    where+      e = error "Could not decode hash from hex string"++instance Serial Hash160 where+  deserialize = Hash160 . toShort <$> getByteString 20+  serialize = putByteString . fromShort . (.get)++instance Serialize Hash160 where+  put = serialize+  get = deserialize++instance Binary Hash160 where+  put = serialize+  get = deserialize++-- | Calculate SHA512 hash.+sha512 :: (ByteArrayAccess b) => b -> Hash512+sha512 = Hash512 . toShort . convert . hashWith SHA512++-- | Calculate SHA256 hash.+sha256 :: (ByteArrayAccess b) => b -> Hash256+sha256 = Hash256 . toShort . convert . hashWith SHA256++-- | Calculate RIPEMD160 hash.+ripemd160 :: (ByteArrayAccess b) => b -> Hash160+ripemd160 = Hash160 . toShort . convert . hashWith RIPEMD160++-- | Claculate SHA1 hash.+sha1 :: (ByteArrayAccess b) => b -> Hash160+sha1 = Hash160 . toShort . convert . hashWith SHA1++-- | Compute two rounds of SHA-256.+doubleSHA256 :: (ByteArrayAccess b) => b -> Hash256+doubleSHA256 =+  Hash256 . toShort . convert . hashWith SHA256 . hashWith SHA256++-- | Compute SHA-256 followed by RIPMED-160.+addressHash :: (ByteArrayAccess b) => b -> Hash160+addressHash =+  Hash160 . toShort . convert . hashWith RIPEMD160 . hashWith SHA256++{- CheckSum -}++-- | Computes a 32 bit checksum.+checkSum32 :: (ByteArrayAccess b) => b -> CheckSum32+checkSum32 =+  fromRight (error "Could not decode bytes as CheckSum32")+    . runGetS deserialize+    . B.take 4+    . convert+    . hashWith SHA256+    . hashWith SHA256++{- HMAC -}++-- | Computes HMAC over SHA-512.+hmac512 :: ByteString -> ByteString -> Hash512+hmac512 key msg =+  Hash512 $ toShort $ convert (hmac key msg :: HMAC SHA512)++-- | Computes HMAC over SHA-256.+hmac256 :: (ByteArrayAccess k, ByteArrayAccess m) => k -> m -> Hash256+hmac256 key msg =+  Hash256 $ toShort $ convert (hmac key msg :: HMAC SHA256)++-- | Split a 'Hash512' into a pair of 'Hash256'.+split512 :: Hash512 -> (Hash256, Hash256)+split512 h =+  (Hash256 (toShort a), Hash256 (toShort b))+  where+    (a, b) = B.splitAt 32 $ fromShort h.get++-- | Join a pair of 'Hash256' into a 'Hash512'.+join512 :: (Hash256, Hash256) -> Hash512+join512 (a, b) = Hash512 (toShort (a.get `app` b.get))+  where+    app = B.append `on` fromShort++-- | Initialize tagged hash specified in BIP340+--+-- @since 0.21.0+initTaggedHash ::+  -- | Hash tag+  ByteString ->+  Context SHA256+initTaggedHash tag =+  (`hashUpdates` [hashedTag, hashedTag]) $+    hashInit @SHA256+  where+    hashedTag = hashWith SHA256 tag
+ src/Haskoin/Crypto/Keys.hs view
@@ -0,0 +1,22 @@+{-# LANGUAGE DuplicateRecordFields #-}++-- |+-- Module      : Haskoin.Keys+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- ECDSA private and public keys, extended keys (BIP-32) and mnemonic sentences+-- (BIP-39).+module Haskoin.Crypto.Keys+  ( module Haskoin.Crypto.Keys.Common,+    module Haskoin.Crypto.Keys.Extended,+    module Haskoin.Crypto.Keys.Mnemonic,+  )+where++import Haskoin.Crypto.Keys.Common+import Haskoin.Crypto.Keys.Extended+import Haskoin.Crypto.Keys.Mnemonic
+ src/Haskoin/Crypto/Keys/Common.hs view
@@ -0,0 +1,185 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoFieldSelectors #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++-- |+-- Module      : Haskoin.Keys.Common+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- ECDSA private and public key functions.+module Haskoin.Crypto.Keys.Common+  ( -- * Bitcoin Public & Private Keys+    PublicKey (..),+    PrivateKey (..),+    wrapPubKey,+    derivePublicKey,+    wrapSecKey,+    fromMiniKey,+    tweakPubKey,+    tweakSecKey,++    -- ** Private Key Wallet Import Format (WIF)+    fromWif,+    toWif,+  )+where++import Control.DeepSeq+import Control.Monad (guard, mzero, (<=<))+import Crypto.Secp256k1+import Data.Aeson+  ( Encoding,+    FromJSON,+    ToJSON (..),+    Value (String),+    object,+    parseJSON,+    withText,+  )+import Data.Aeson.Encoding (text, unsafeToEncoding)+import Data.Aeson.Types (Parser)+import Data.Binary (Binary (..))+import Data.Bool (bool)+import Data.ByteString (ByteString)+import Data.ByteString qualified as BS+import Data.ByteString.Builder (char7)+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Hashable+import Data.Maybe (fromMaybe)+import Data.Serialize (Serialize (..))+import Data.String (IsString, fromString)+import Data.String.Conversions (cs)+import GHC.Generics (Generic)+import Haskoin.Address.Base58+import Haskoin.Crypto.Hash+import Haskoin.Network.Data+import Haskoin.Util++-- | Elliptic curve public key type with expected serialized compression flag.+data PublicKey = PublicKey+  { point :: !PubKey,+    compress :: !Bool+  }+  deriving (Generic, Show, Read, Hashable, Eq, NFData)++instance MarshalJSON Ctx PublicKey where+  marshalValue ctx = String . encodeHex . runPutS . marshalPut ctx++  marshalEncoding ctx = hexEncoding . runPutL . marshalPut ctx++  unmarshalValue ctx =+    withText "PublicKey" $ \t -> do+      bs <- maybe (fail "Expected hex public key") return $ decodeHex t+      either fail return $ unmarshal ctx bs++instance Marshal Ctx PublicKey where+  marshalGet ctx = do+    c <-+      lookAhead $+        getWord8 >>= \case+          0x02 -> return True+          0x03 -> return True+          0x04 -> return False+          _ -> fail "Not a public key"+    bs <- getByteString $ if c then 33 else 65+    case importPubKey ctx bs of+      Nothing -> fail "Could not decode public key"+      Just k -> return $ PublicKey k c++  marshalPut ctx pk =+    putByteString $ exportPubKey ctx pk.compress pk.point++-- | Wrap a public key from secp256k1 library adding information about compression.+wrapPubKey :: Bool -> PubKey -> PublicKey+wrapPubKey c p = PublicKey p c++-- | Derives a public key from a private key. This function will preserve+-- compression flag.+derivePublicKey :: Ctx -> PrivateKey -> PublicKey+derivePublicKey ctx (PrivateKey d c) = PublicKey (derivePubKey ctx d) c++-- | Tweak a public key.+tweakPubKey :: Ctx -> PubKey -> Hash256 -> Maybe PubKey+tweakPubKey ctx p =+  tweakAddPubKey ctx p <=< tweak . runPutS . serialize++-- | Elliptic curve private key type with expected public key compression+-- information. Compression information is stored in private key WIF formats and+-- needs to be preserved to generate the correct address from the corresponding+-- public key.+data PrivateKey = PrivateKey+  { key :: !SecKey,+    compress :: !Bool+  }+  deriving (Eq, Show, Read, Generic, NFData)++instance Serial PrivateKey where+  serialize p = do+    putByteString p.key.get+    serialize p.compress+  deserialize = do+    k <- getByteString 32+    c <- deserialize+    return PrivateKey {key = SecKey k, compress = c}++instance MarshalJSON Network PrivateKey where+  marshalValue net = String . toWif net+  marshalEncoding net = text . toWif net+  unmarshalValue net =+    withText "PrivateKey" $+      maybe (fail "Could not decode WIF") return . fromWif net++-- | Wrap private key with corresponding public key compression flag.+wrapSecKey :: Bool -> SecKey -> PrivateKey+wrapSecKey c d = PrivateKey d c++-- | Tweak a private key.+tweakSecKey :: Ctx -> SecKey -> Hash256 -> Maybe SecKey+tweakSecKey ctx k =+  tweakAddSecKey ctx k <=< tweak . runPutS . serialize++-- | Decode Casascius mini private keys (22 or 30 characters).+fromMiniKey :: ByteString -> Maybe PrivateKey+fromMiniKey bs = do+  guard checkShortKey+  wrapSecKey False <$> (secKey . runPutS . serialize . sha256) bs+  where+    checkHash = runPutS $ serialize $ sha256 $ bs `BS.append` "?"+    checkShortKey = BS.length bs `elem` [22, 30] && BS.head checkHash == 0x00++-- | Decode private key from WIF (wallet import format) string.+fromWif :: Network -> Base58 -> Maybe PrivateKey+fromWif net wif = do+  bs <- decodeBase58Check wif+  -- Check that this is a private key+  guard (BS.head bs == net.secretPrefix)+  case BS.length bs of+    -- Uncompressed format+    33 -> wrapSecKey False <$> (secKey . BS.tail) bs+    -- Compressed format+    34 -> do+      guard $ BS.last bs == 0x01+      wrapSecKey True <$> (secKey . BS.tail . BS.init) bs+    -- Bad length+    _ -> Nothing++-- | Encode private key into a WIF string.+toWif :: Network -> PrivateKey -> Base58+toWif net (PrivateKey k c) =+  encodeBase58Check . BS.cons net.secretPrefix $+    if c then k.get `BS.snoc` 0x01 else k.get
+ src/Haskoin/Crypto/Keys/Extended.hs view
@@ -0,0 +1,1057 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Keys.Extended+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- BIP-32 extended keys.+module Haskoin.Crypto.Keys.Extended+  ( -- * Extended Keys+    XPubKey (..),+    XPrvKey (..),+    ChainCode,+    KeyIndex,+    Fingerprint,+    fingerprintToText,+    textToFingerprint,+    DerivationException (..),+    makeXPrvKey,+    deriveXPubKey,+    prvSubKey,+    pubSubKey,+    hardSubKey,+    xPrvIsHard,+    xPubIsHard,+    xPrvChild,+    xPubChild,+    xPubID,+    xPrvID,+    xPubFP,+    xPrvFP,+    xPubAddr,+    xPubWitnessAddr,+    xPubCompatWitnessAddr,+    xPubExport,+    xPrvExport,+    xPubImport,+    xPrvImport,+    xPrvWif,++    -- ** Helper Functions+    prvSubKeys,+    pubSubKeys,+    hardSubKeys,+    deriveAddr,+    deriveWitnessAddr,+    deriveCompatWitnessAddr,+    deriveAddrs,+    deriveWitnessAddrs,+    deriveCompatWitnessAddrs,+    deriveMSAddr,+    deriveMSAddrs,+    cycleIndex,++    -- ** Derivation Paths+    DerivPathI (..),+    AnyDeriv,+    HardDeriv,+    SoftDeriv,+    HardOrAny,+    AnyOrSoft,+    DerivPath,+    HardPath,+    SoftPath,+    Bip32PathIndex (..),+    derivePath,+    derivePubPath,+    toHard,+    toSoft,+    toGeneric,+    (++/),+    pathToStr,+    listToPath,+    pathToList,++    -- *** Derivation Path Parser+    XKey (..),+    ParsedPath (..),+    parsePath,+    parseHard,+    parseSoft,+    applyPath,+    derivePathAddr,+    derivePathAddrs,+    derivePathMSAddr,+    derivePathMSAddrs,+    concatBip32Segments,+  )+where++import Control.Applicative+import Control.DeepSeq+import Control.Exception (Exception, throw)+import Control.Monad (guard, mzero, unless, (<=<))+import Crypto.Secp256k1+import Data.Aeson as Aeson+  ( FromJSON,+    ToJSON (..),+    Value (String),+    parseJSON,+    toJSON,+    withText,+  )+import Data.Aeson.Encoding (Encoding, string, text)+import Data.Aeson.Types (Parser)+import Data.Binary (Binary (get, put))+import Data.Bits (clearBit, setBit, testBit)+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Bytes.Get+  ( MonadGet+      ( getByteString,+        getWord32be,+        getWord8+      ),+    runGetS,+  )+import Data.Bytes.Put+  ( MonadPut+      ( putByteString,+        putWord32be,+        putWord8+      ),+    runPutS,+  )+import Data.Bytes.Serial (Serial (..))+import Data.Either (fromRight)+import Data.Hashable (Hashable)+import Data.List (foldl')+import Data.List.Split (splitOn)+import Data.Maybe (fromMaybe)+import Data.Serialize (Serialize (..))+import Data.Serialize qualified as S+import Data.String (IsString, fromString)+import Data.String.Conversions (cs)+import Data.Text qualified as Text+import Data.Typeable (Typeable)+import Data.Word (Word32, Word8)+import GHC.Generics (Generic)+import Haskoin.Address+import Haskoin.Crypto.Hash+import Haskoin.Crypto.Keys.Common+import Haskoin.Crypto.Keys.Extended.Internal+import Haskoin.Network.Data+import Haskoin.Script.Standard+import Haskoin.Util+import Text.Read as Read+  ( Lexeme (Ident, Number, String),+    Read (readPrec),+    lexP,+    parens,+    pfail,+  )+import Text.Read.Lex (numberToInteger)++-- | A derivation exception is thrown in the very unlikely event that a+-- derivation is invalid.+newtype DerivationException = DerivationException String+  deriving (Eq, Read, Show, Typeable, Generic)+  deriving newtype (NFData)++instance Exception DerivationException++-- | Chain code as specified in BIP-32.+type ChainCode = Hash256++-- | Index of key as specified in BIP-32.+type KeyIndex = Word32++-- | Data type representing an extended BIP32 private key. An extended key+-- is a node in a tree of key derivations. It has a depth in the tree, a+-- parent node and an index to differentiate it from other siblings.+data XPrvKey = XPrvKey+  { -- | depth in the tree+    depth :: !Word8,+    -- | fingerprint of parent+    parent :: !Fingerprint,+    -- | derivation index+    index :: !KeyIndex,+    -- | chain code+    chain :: !ChainCode,+    -- | private key of this node+    key :: !SecKey+  }+  deriving (Generic, Eq, Show, Read, NFData, Hashable)++instance Marshal Network XPrvKey where+  marshalGet net = do+    ver <- getWord32be+    unless (ver == net.xPrvPrefix) $+      fail "Get: Invalid version for extended private key"+    XPrvKey+      <$> getWord8+      <*> deserialize+      <*> getWord32be+      <*> deserialize+      <*> getPadPrvKey++  marshalPut net k = do+    putWord32be net.xPrvPrefix+    putWord8 k.depth+    serialize k.parent+    putWord32be k.index+    serialize $ k.chain+    putPadPrvKey k.key++instance MarshalJSON Network XPrvKey where+  marshalValue net = Aeson.String . xPrvExport net++  marshalEncoding net = text . xPrvExport net++  unmarshalValue net =+    withText "XPrvKey" $ \t ->+      case xPrvImport net t of+        Nothing -> fail "could not read xprv"+        Just x -> return x++-- | Data type representing an extended BIP32 public key.+data XPubKey = XPubKey+  { -- | depth in the tree+    depth :: !Word8,+    -- | fingerprint of parent+    parent :: !Fingerprint,+    -- | derivation index+    index :: !KeyIndex,+    -- | chain code+    chain :: !ChainCode,+    -- | public key of this node+    key :: !PubKey+  }+  deriving (Generic, Eq, Show, Read, Hashable, NFData)++instance Marshal (Network, Ctx) XPubKey where+  marshalGet (net, ctx) = do+    ver <- getWord32be+    unless (ver == net.xPubPrefix) $+      fail "Get: Invalid version for extended public key"+    XPubKey+      <$> getWord8+      <*> deserialize+      <*> getWord32be+      <*> deserialize+      <*> ((\PublicKey {point} -> point) <$> marshalGet ctx)++  marshalPut (net, ctx) k = do+    putWord32be net.xPubPrefix+    putWord8 k.depth+    serialize k.parent+    putWord32be k.index+    serialize k.chain+    marshalPut ctx $ wrapPubKey True k.key++instance MarshalJSON (Network, Ctx) XPubKey where+  unmarshalValue (net, ctx) =+    withText "XPubKey" $ \t ->+      case xPubImport net ctx t of+        Nothing -> fail "could not read xpub"+        Just x -> return x++  marshalValue (net, ctx) = Aeson.String . xPubExport net ctx++  marshalEncoding (net, ctx) = text . xPubExport net ctx++-- | Build a BIP32 compatible extended private key from a bytestring. This will+-- produce a root node (@depth=0@ and @parent=0@).+makeXPrvKey :: ByteString -> XPrvKey+makeXPrvKey bs =+  XPrvKey 0 (Fingerprint 0) 0 c k+  where+    (p, c) = split512 $ hmac512 "Bitcoin seed" bs+    k = fromMaybe err (secKey (runPutS (serialize p)))+    err = throw $ DerivationException "Invalid seed"++-- | Derive an extended public key from an extended private key. This function+-- will preserve the depth, parent, index and chaincode fields of the extended+-- private keys.+deriveXPubKey :: Ctx -> XPrvKey -> XPubKey+deriveXPubKey ctx (XPrvKey d p i c k) = XPubKey d p i c (derivePubKey ctx k)++-- | Compute a private, soft child key derivation. A private soft derivation+-- will allow the equivalent extended public key to derive the public key for+-- this child. Given a parent key /m/ and a derivation index /i/, this function+-- will compute /m\/i/.+--+-- Soft derivations allow for more flexibility such as read-only wallets.+-- However, care must be taken not the leak both the parent extended public key+-- and one of the extended child private keys as this would compromise the+-- extended parent private key.+prvSubKey ::+  Ctx ->+  -- | extended parent private key+  XPrvKey ->+  -- | child derivation index+  KeyIndex ->+  -- | extended child private key+  XPrvKey+prvSubKey ctx xkey child+  | child >= 0 && child < 0x80000000 =+      XPrvKey (xkey.depth + 1) (xPrvFP ctx xkey) child c k+  | otherwise = error "Invalid child derivation index"+  where+    pK = (deriveXPubKey ctx xkey).key+    m = B.append (exportPubKey ctx True pK) (runPutS (serialize child))+    (a, c) = split512 $ hmac512 (runPutS $ serialize xkey.chain) m+    k = fromMaybe err $ tweakSecKey ctx xkey.key a+    err = throw $ DerivationException "Invalid prvSubKey derivation"++-- | Compute a public, soft child key derivation. Given a parent key /M/+-- and a derivation index /i/, this function will compute /M\/i/.+pubSubKey ::+  Ctx ->+  -- | extended parent public key+  XPubKey ->+  -- | child derivation index+  KeyIndex ->+  -- | extended child public key+  XPubKey+pubSubKey ctx xKey child+  | child >= 0 && child < 0x80000000 =+      XPubKey (xKey.depth + 1) (xPubFP ctx xKey) child c pK+  | otherwise = error "Invalid child derivation index"+  where+    m = B.append (exportPubKey ctx True xKey.key) (runPutS $ serialize child)+    (a, c) = split512 $ hmac512 (runPutS $ serialize xKey.chain) m+    pK = fromMaybe err $ tweakPubKey ctx xKey.key a+    err = throw $ DerivationException "Invalid pubSubKey derivation"++-- | Compute a hard child key derivation. Hard derivations can only be computed+-- for private keys. Hard derivations do not allow the parent public key to+-- derive the child public keys. However, they are safer as a breach of the+-- parent public key and child private keys does not lead to a breach of the+-- parent private key. Given a parent key /m/ and a derivation index /i/, this+-- function will compute /m\/i'/.+hardSubKey ::+  Ctx ->+  -- | extended parent private key+  XPrvKey ->+  -- | child derivation index+  KeyIndex ->+  -- | extended child private key+  XPrvKey+hardSubKey ctx xkey child+  | child >= 0 && child < 0x80000000 =+      XPrvKey (xkey.depth + 1) (xPrvFP ctx xkey) i c k+  | otherwise = error "Invalid child derivation index"+  where+    i = setBit child 31+    m = B.append (bsPadPrvKey xkey.key) (runPutS $ serialize i)+    (a, c) = split512 $ hmac512 (runPutS $ serialize xkey.chain) m+    k = fromMaybe err $ tweakSecKey ctx xkey.key a+    err = throw $ DerivationException "Invalid hardSubKey derivation"++-- | Returns true if the extended private key was derived through a hard+-- derivation.+xPrvIsHard :: XPrvKey -> Bool+xPrvIsHard k = testBit k.index 31++-- | Returns true if the extended public key was derived through a hard+-- derivation.+xPubIsHard :: XPubKey -> Bool+xPubIsHard k = testBit k.index 31++-- | Returns the derivation index of this extended private key without the hard+-- bit set.+xPrvChild :: XPrvKey -> KeyIndex+xPrvChild k = clearBit k.index 31++-- | Returns the derivation index of this extended public key without the hard+-- bit set.+xPubChild :: XPubKey -> KeyIndex+xPubChild k = clearBit k.index 31++-- | Computes the key identifier of an extended private key.+xPrvID :: Ctx -> XPrvKey -> Hash160+xPrvID ctx = xPubID ctx . deriveXPubKey ctx++-- | Computes the key identifier of an extended public key.+xPubID :: Ctx -> XPubKey -> Hash160+xPubID ctx =+  ripemd160+    . runPutS+    . serialize+    . sha256+    . exportPubKey ctx True+    . (.key)++-- | Computes the key fingerprint of an extended private key.+xPrvFP :: Ctx -> XPrvKey -> Fingerprint+xPrvFP ctx =+  fromRight err+    . runGetS deserialize+    . B.take 4+    . runPutS+    . serialize+    . xPrvID ctx+  where+    err = error "Could not decode xPrvFP"++-- | Computes the key fingerprint of an extended public key.+xPubFP :: Ctx -> XPubKey -> Fingerprint+xPubFP ctx =+  fromRight err+    . runGetS deserialize+    . B.take 4+    . runPutS+    . serialize+    . xPubID ctx+  where+    err = error "Could not decode xPubFP"++-- | Compute a standard P2PKH address for an extended public key.+xPubAddr :: Ctx -> XPubKey -> Address+xPubAddr ctx xkey = pubKeyAddr ctx (wrapPubKey True xkey.key)++-- | Compute a SegWit P2WPKH address for an extended public key.+xPubWitnessAddr :: Ctx -> XPubKey -> Address+xPubWitnessAddr ctx xkey =+  pubKeyWitnessAddr ctx (wrapPubKey True xkey.key)++-- | Compute a backwards-compatible SegWit P2SH-P2WPKH address for an extended+-- public key.+xPubCompatWitnessAddr :: Ctx -> XPubKey -> Address+xPubCompatWitnessAddr ctx xkey =+  pubKeyCompatWitnessAddr ctx (wrapPubKey True xkey.key)++-- | Exports an extended private key to the BIP32 key export format ('Base58').+xPrvExport :: Network -> XPrvKey -> Base58+xPrvExport net = encodeBase58Check . marshal net++-- | Exports an extended public key to the BIP32 key export format ('Base58').+xPubExport :: Network -> Ctx -> XPubKey -> Base58+xPubExport net ctx = encodeBase58Check . marshal (net, ctx)++-- | Decodes a BIP32 encoded extended private key. This function will fail if+-- invalid base 58 characters are detected or if the checksum fails.+xPrvImport :: Network -> Base58 -> Maybe XPrvKey+xPrvImport net =+  eitherToMaybe . unmarshal net <=< decodeBase58Check++-- | Decodes a BIP32 encoded extended public key. This function will fail if+-- invalid base 58 characters are detected or if the checksum fails.+xPubImport :: Network -> Ctx -> Base58 -> Maybe XPubKey+xPubImport net ctx =+  eitherToMaybe . unmarshal (net, ctx) <=< decodeBase58Check++-- | Export an extended private key to WIF (Wallet Import Format).+xPrvWif :: Network -> XPrvKey -> Base58+xPrvWif net xkey = toWif net (wrapSecKey True xkey.key)++{- Derivation helpers -}++-- | Cyclic list of all private soft child key derivations of a parent key+-- starting from an offset index.+prvSubKeys :: Ctx -> XPrvKey -> KeyIndex -> [(XPrvKey, KeyIndex)]+prvSubKeys ctx k = map (\i -> (prvSubKey ctx k i, i)) . cycleIndex++-- | Cyclic list of all public soft child key derivations of a parent key+-- starting from an offset index.+pubSubKeys :: Ctx -> XPubKey -> KeyIndex -> [(XPubKey, KeyIndex)]+pubSubKeys ctx k = map (\i -> (pubSubKey ctx k i, i)) . cycleIndex++-- | Cyclic list of all hard child key derivations of a parent key starting+-- from an offset index.+hardSubKeys :: Ctx -> XPrvKey -> KeyIndex -> [(XPrvKey, KeyIndex)]+hardSubKeys ctx k = map (\i -> (hardSubKey ctx k i, i)) . cycleIndex++-- | Derive a standard address from an extended public key and an index.+deriveAddr :: Ctx -> XPubKey -> KeyIndex -> (Address, PubKey)+deriveAddr ctx k i =+  (xPubAddr ctx key, key.key)+  where+    key = pubSubKey ctx k i++-- | Derive a SegWit P2WPKH address from an extended public key and an index.+deriveWitnessAddr :: Ctx -> XPubKey -> KeyIndex -> (Address, PubKey)+deriveWitnessAddr ctx k i =+  (xPubWitnessAddr ctx key, key.key)+  where+    key = pubSubKey ctx k i++-- | Derive a backwards-compatible SegWit P2SH-P2WPKH address from an extended+-- public key and an index.+deriveCompatWitnessAddr :: Ctx -> XPubKey -> KeyIndex -> (Address, PubKey)+deriveCompatWitnessAddr ctx k i =+  (xPubCompatWitnessAddr ctx key, key.key)+  where+    key = pubSubKey ctx k i++-- | Cyclic list of all addresses derived from a public key starting from an+-- offset index.+deriveAddrs :: Ctx -> XPubKey -> KeyIndex -> [(Address, PubKey, KeyIndex)]+deriveAddrs ctx k =+  map f . cycleIndex+  where+    f i = let (a, key) = deriveAddr ctx k i in (a, key, i)++-- | Cyclic list of all SegWit P2WPKH addresses derived from a public key+-- starting from an offset index.+deriveWitnessAddrs ::+  Ctx -> XPubKey -> KeyIndex -> [(Address, PubKey, KeyIndex)]+deriveWitnessAddrs ctx k =+  map f . cycleIndex+  where+    f i = let (a, key) = deriveWitnessAddr ctx k i in (a, key, i)++-- | Cyclic list of all backwards-compatible SegWit P2SH-P2WPKH addresses+-- derived from a public key starting from an offset index.+deriveCompatWitnessAddrs ::+  Ctx -> XPubKey -> KeyIndex -> [(Address, PubKey, KeyIndex)]+deriveCompatWitnessAddrs ctx k =+  map f . cycleIndex+  where+    f i = let (a, key) = deriveCompatWitnessAddr ctx k i in (a, key, i)++-- | Derive a multisig address from a list of public keys, the number of+-- required signatures /m/ and a derivation index. The derivation type is a+-- public, soft derivation.+deriveMSAddr ::+  Ctx -> [XPubKey] -> Int -> KeyIndex -> (Address, RedeemScript)+deriveMSAddr ctx keys m i = (payToScriptAddress ctx rdm, rdm)+  where+    rdm = sortMulSig ctx $ PayMulSig k m+    k = map (wrapPubKey True . (.key) . flip (pubSubKey ctx) i) keys++-- | Cyclic list of all multisig addresses derived from a list of public keys,+-- a number of required signatures /m/ and starting from an offset index. The+-- derivation type is a public, soft derivation.+deriveMSAddrs ::+  Ctx ->+  [XPubKey] ->+  Int ->+  KeyIndex ->+  [(Address, RedeemScript, KeyIndex)]+deriveMSAddrs ctx keys m = map f . cycleIndex+  where+    f i =+      let (a, rdm) = deriveMSAddr ctx keys m i+       in (a, rdm, i)++-- | Helper function to go through derivation indices.+cycleIndex :: KeyIndex -> [KeyIndex]+cycleIndex i+  | i == 0 = cycle [0 .. 0x7fffffff]+  | i < 0x80000000 = cycle $ [i .. 0x7fffffff] ++ [0 .. (i - 1)]+  | otherwise = error $ "cycleIndex: invalid index " ++ show i++{- Derivation Paths -}++-- | Phantom type signaling a hardened derivation path that can only be computed+-- from private extended key.+data HardDeriv deriving (Generic, NFData)++-- | Phantom type signaling no knowledge about derivation path: can be hardened or not.+data AnyDeriv deriving (Generic, NFData)++-- | Phantom type signaling derivation path including only non-hardened paths+-- that can be computed from an extended public key.+data SoftDeriv deriving (Generic, NFData)++-- | Hardened derivation path. Can be computed from extended private key only.+type HardPath = DerivPathI HardDeriv++-- | Any derivation path.+type DerivPath = DerivPathI AnyDeriv++-- | Non-hardened derivation path can be computed from extended public key.+type SoftPath = DerivPathI SoftDeriv++-- | Helper class to perform validations on a hardened derivation path.+class HardOrAny a++instance HardOrAny HardDeriv++instance HardOrAny AnyDeriv++-- | Helper class to perform validations on a non-hardened derivation path.+class AnyOrSoft a++instance AnyOrSoft AnyDeriv++instance AnyOrSoft SoftDeriv++-- | Data type representing a derivation path. Two constructors are provided+-- for specifying soft or hard derivations. The path /\/0\/1'\/2/ for example can be+-- expressed as @'Deriv' :\/ 0 :| 1 :\/ 2@. The 'HardOrAny' and 'AnyOrSoft' type+-- classes are used to constrain the valid values for the phantom type /t/. If+-- you mix hard '(:|)' and soft '(:\/)' paths, the only valid type for /t/ is 'AnyDeriv'.+-- Otherwise, /t/ can be 'HardDeriv' if you only have hard derivation or 'SoftDeriv'+-- if you only have soft derivations.+--+-- Using this type is as easy as writing the required derivation like in these+-- example:+--+-- > Deriv :/ 0 :/ 1 :/ 2 :: SoftPath+-- > Deriv :| 0 :| 1 :| 2 :: HardPath+-- > Deriv :| 0 :/ 1 :/ 2 :: DerivPath+data DerivPathI t where+  (:|) :: (HardOrAny t) => !(DerivPathI t) -> !KeyIndex -> DerivPathI t+  (:/) :: (AnyOrSoft t) => !(DerivPathI t) -> !KeyIndex -> DerivPathI t+  Deriv :: DerivPathI t++instance NFData (DerivPathI t) where+  rnf (a :| b) = rnf a `seq` rnf b+  rnf (a :/ b) = rnf a `seq` rnf b+  rnf Deriv = ()++instance Eq (DerivPathI t) where+  (nextA :| iA) == (nextB :| iB) = iA == iB && nextA == nextB+  (nextA :/ iA) == (nextB :/ iB) = iA == iB && nextA == nextB+  Deriv == Deriv = True+  _ == _ = False++instance Ord (DerivPathI t) where+  -- Same hardness on each side+  (nextA :| iA) `compare` (nextB :| iB) =+    if nextA == nextB then iA `compare` iB else nextA `compare` nextB+  (nextA :/ iA) `compare` (nextB :/ iB) =+    if nextA == nextB then iA `compare` iB else nextA `compare` nextB+  -- Different hardness: hard paths are LT soft paths+  (nextA :/ _iA) `compare` (nextB :| _iB) =+    if nextA == nextB then LT else nextA `compare` nextB+  (nextA :| _iA) `compare` (nextB :/ _iB) =+    if nextA == nextB then GT else nextA `compare` nextB+  Deriv `compare` Deriv = EQ+  Deriv `compare` _ = LT+  _ `compare` Deriv = GT++instance Serial DerivPath where+  deserialize = listToPath <$> getList getWord32be+  serialize = putList putWord32be . pathToList++instance Serialize DerivPath where+  put = serialize+  get = deserialize++instance Binary DerivPath where+  put = serialize+  get = deserialize++instance Serial HardPath where+  deserialize =+    maybe+      (fail "Could not decode hard path")+      return+      . toHard+      . listToPath+      =<< getList getWord32be+  serialize = putList putWord32be . pathToList++instance Serialize HardPath where+  put = serialize+  get = deserialize++instance Binary HardPath where+  put = serialize+  get = deserialize++instance Serial SoftPath where+  deserialize =+    maybe+      (fail "Could not decode soft path")+      return+      . toSoft+      . listToPath+      =<< getList getWord32be+  serialize = putList putWord32be . pathToList++instance Serialize SoftPath where+  put = serialize+  get = deserialize++instance Binary SoftPath where+  put = serialize+  get = deserialize++-- | Get a list of derivation indices from a derivation path.+pathToList :: DerivPathI t -> [KeyIndex]+pathToList =+  reverse . go+  where+    go (next :| i) = setBit i 31 : go next+    go (next :/ i) = i : go next+    go _ = []++-- | Convert a list of derivation indices to a derivation path.+listToPath :: [KeyIndex] -> DerivPath+listToPath =+  go . reverse+  where+    go (i : is)+      | testBit i 31 = go is :| clearBit i 31+      | otherwise = go is :/ i+    go [] = Deriv++-- | Convert a derivation path to a human-readable string.+pathToStr :: DerivPathI t -> String+pathToStr p =+  case p of+    next :| i -> concat [pathToStr next, "/", show i, "'"]+    next :/ i -> concat [pathToStr next, "/", show i]+    Deriv -> ""++-- | Turn a derivation path into a hard derivation path. Will fail if the path+-- contains soft derivations.+toHard :: DerivPathI t -> Maybe HardPath+toHard p = case p of+  next :| i -> (:| i) <$> toHard next+  Deriv -> Just Deriv+  _ -> Nothing++-- | Turn a derivation path into a soft derivation path. Will fail if the path+-- has hard derivations.+toSoft :: DerivPathI t -> Maybe SoftPath+toSoft p = case p of+  next :/ i -> (:/ i) <$> toSoft next+  Deriv -> Just Deriv+  _ -> Nothing++-- | Make a derivation path generic.+toGeneric :: DerivPathI t -> DerivPath+toGeneric p = case p of+  next :/ i -> toGeneric next :/ i+  next :| i -> toGeneric next :| i+  Deriv -> Deriv++-- | Append two derivation paths together. The result will be a mixed+-- derivation path.+(++/) :: DerivPathI t1 -> DerivPathI t2 -> DerivPath+(++/) p1 p2 =+  go id (toGeneric p2) $ toGeneric p1+  where+    go f p = case p of+      next :/ i -> go (f . (:/ i)) $ toGeneric next+      next :| i -> go (f . (:| i)) $ toGeneric next+      _ -> f++-- | Derive a private key from a derivation path+derivePath :: Ctx -> DerivPathI t -> XPrvKey -> XPrvKey+derivePath ctx = go id+  where+    -- Build the full derivation function starting from the end+    go f p = case p of+      next :| i -> go (f . flip (hardSubKey ctx) i) next+      next :/ i -> go (f . flip (prvSubKey ctx) i) next+      _ -> f++-- | Derive a public key from a soft derivation path+derivePubPath :: Ctx -> SoftPath -> XPubKey -> XPubKey+derivePubPath ctx = go id+  where+    -- Build the full derivation function starting from the end+    go f p = case p of+      next :/ i -> go (f . flip (pubSubKey ctx) i) next+      _ -> f++instance Show DerivPath where+  showsPrec d p =+    showParen (d > 10) $+      showString "DerivPath " . shows (pathToStr p)++instance Read DerivPath where+  readPrec = parens $ do+    Ident "DerivPath" <- lexP+    Read.String str <- lexP+    maybe pfail (return . (.get)) (parsePath str)++instance Show HardPath where+  showsPrec d p =+    showParen (d > 10) $+      showString "HardPath " . shows (pathToStr p)++instance Read HardPath where+  readPrec = parens $ do+    Ident "HardPath" <- lexP+    Read.String str <- lexP+    maybe pfail return $ parseHard str++instance Show SoftPath where+  showsPrec d p =+    showParen (d > 10) $+      showString "SoftPath " . shows (pathToStr p)++instance Read SoftPath where+  readPrec = parens $ do+    Ident "SoftPath" <- lexP+    Read.String str <- lexP+    maybe pfail return $ parseSoft str++instance IsString ParsedPath where+  fromString =+    fromMaybe e . parsePath+    where+      e = error "Could not parse derivation path"++instance IsString DerivPath where+  fromString =+    (.get) . fromMaybe e . parsePath+    where+      e = error "Could not parse derivation path"++instance IsString HardPath where+  fromString =+    fromMaybe e . parseHard+    where+      e = error "Could not parse hard derivation path"++instance IsString SoftPath where+  fromString =+    fromMaybe e . parseSoft+    where+      e = error "Could not parse soft derivation path"++instance FromJSON ParsedPath where+  parseJSON = withText "ParsedPath" $ \str -> case parsePath $ cs str of+    Just p -> return p+    _ -> mzero++instance FromJSON DerivPath where+  parseJSON = withText "DerivPath" $ \str -> case parsePath $ cs str of+    Just p -> return p.get+    _ -> mzero++instance FromJSON HardPath where+  parseJSON = withText "HardPath" $ \str -> case parseHard $ cs str of+    Just p -> return p+    _ -> mzero++instance FromJSON SoftPath where+  parseJSON = withText "SoftPath" $ \str -> case parseSoft $ cs str of+    Just p -> return p+    _ -> mzero++instance ToJSON (DerivPathI t) where+  toJSON = Aeson.String . cs . pathToStr+  toEncoding = string . pathToStr++instance ToJSON ParsedPath where+  toJSON (ParsedPrv p) = Aeson.String . cs . ("m" ++) . pathToStr $ p+  toJSON (ParsedPub p) = Aeson.String . cs . ("M" ++) . pathToStr $ p+  toJSON (ParsedEmpty p) = Aeson.String . cs . ("" ++) . pathToStr $ p+  toEncoding (ParsedPrv p) = text . cs . ("m" ++) . pathToStr $ p+  toEncoding (ParsedPub p) = text . cs . ("M" ++) . pathToStr $ p+  toEncoding (ParsedEmpty p) = text . cs . ("" ++) . pathToStr $ p++{- Parsing derivation paths of the form m/1/2'/3 or M/1/2'/3 -}++-- | Type for parsing derivation paths of the form /m\/1\/2'\/3/ or+-- /M\/1\/2'\/3/.+data ParsedPath+  = ParsedPrv {get :: !DerivPath}+  | ParsedPub {get :: !DerivPath}+  | ParsedEmpty {get :: !DerivPath}+  deriving (Eq, Generic, NFData)++instance Show ParsedPath where+  showsPrec d p = showParen (d > 10) $ showString "ParsedPath " . shows f+    where+      f =+        case p of+          ParsedPrv d' -> "m" <> pathToStr d'+          ParsedPub d' -> "M" <> pathToStr d'+          ParsedEmpty d' -> pathToStr d'++instance Read ParsedPath where+  readPrec = parens $ do+    Ident "ParsedPath" <- lexP+    Read.String str <- lexP+    maybe pfail return $ parsePath str++-- | Parse derivation path string for extended key.+-- Forms: /m\/0'\/2/, /M\/2\/3\/4/.+parsePath :: String -> Maybe ParsedPath+parsePath str = do+  res <- concatBip32Segments <$> mapM parseBip32PathIndex xs+  case x of+    "m" -> Just $ ParsedPrv res+    "M" -> Just $ ParsedPub res+    "" -> Just $ ParsedEmpty res+    _ -> Nothing+  where+    (x : xs) = splitOn "/" str++-- | Concatenate derivation path indices into a derivation path.+concatBip32Segments :: [Bip32PathIndex] -> DerivPath+concatBip32Segments = foldl' appendBip32Segment Deriv++-- | Append an extra derivation path index element into an existing path.+appendBip32Segment :: DerivPath -> Bip32PathIndex -> DerivPath+appendBip32Segment d (Bip32SoftIndex i) = d :/ i+appendBip32Segment d (Bip32HardIndex i) = d :| i++-- | Parse a BIP32 derivation path index element from a string.+parseBip32PathIndex :: String -> Maybe Bip32PathIndex+parseBip32PathIndex segment = case reads segment of+  [(i, "")] -> guard (is31Bit i) >> return (Bip32SoftIndex i)+  [(i, "'")] -> guard (is31Bit i) >> return (Bip32HardIndex i)+  _ -> Nothing++-- | Type for BIP32 path index element.+data Bip32PathIndex+  = Bip32HardIndex KeyIndex+  | Bip32SoftIndex KeyIndex+  deriving (Eq, Generic, NFData)++instance Show Bip32PathIndex where+  showsPrec d (Bip32HardIndex i) =+    showParen (d > 10) $+      showString "Bip32HardIndex " . shows i+  showsPrec d (Bip32SoftIndex i) =+    showParen (d > 10) $+      showString "Bip32SoftIndex " . shows i++instance Read Bip32PathIndex where+  readPrec = h <|> s+    where+      h =+        parens $ do+          Ident "Bip32HardIndex" <- lexP+          Number n <- lexP+          maybe+            pfail+            (return . Bip32HardIndex . fromIntegral)+            (numberToInteger n)+      s =+        parens $ do+          Ident "Bip32SoftIndex" <- lexP+          Number n <- lexP+          maybe+            pfail+            (return . Bip32SoftIndex . fromIntegral)+            (numberToInteger n)++-- | Test whether the number could be a valid BIP32 derivation index.+is31Bit :: (Integral a) => a -> Bool+is31Bit i = i >= 0 && i < 0x80000000++-- | Helper function to parse a hard path.+parseHard :: String -> Maybe HardPath+parseHard = toHard . (.get) <=< parsePath++-- | Helper function to parse a soft path.+parseSoft :: String -> Maybe SoftPath+parseSoft = toSoft . (.get) <=< parsePath++-- | Data type representing a private or public key with its respective network.+data XKey+  = XPrv+      { xprv :: !XPrvKey,+        net :: !Network+      }+  | XPub+      { xpub :: !XPubKey,+        net :: !Network+      }+  deriving (Show, Read, Eq, Generic, NFData)++-- | Apply a parsed path to an extended key to derive the new key defined in the+-- path. If the path starts with /m/, a private key will be returned and if the+-- path starts with /M/, a public key will be returned. Private derivations on a+-- public key, and public derivations with a hard segment, return an error+-- value.+applyPath :: Ctx -> ParsedPath -> XKey -> Either String XKey+applyPath ctx path key =+  case (path, key) of+    (ParsedPrv _, XPrv k n) -> return $ XPrv (derivPrvF k) n+    (ParsedPrv _, XPub {}) -> Left "applyPath: Invalid public key"+    (ParsedPub _, XPrv k n) -> return $ XPub (deriveXPubKey ctx (derivPrvF k)) n+    (ParsedPub _, XPub k n) -> derivPubFE >>= \f -> return $ XPub (f k) n+    -- For empty parsed paths, we take a hint from the provided key+    (ParsedEmpty _, XPrv k n) -> return $ XPrv (derivPrvF k) n+    (ParsedEmpty _, XPub k n) -> derivPubFE >>= \f -> return $ XPub (f k) n+  where+    derivPrvF = goPrv id path.get+    derivPubFE = goPubE id path.get+    -- Build the full private derivation function starting from the end+    goPrv f p =+      case p of+        next :| i -> goPrv (f . flip (hardSubKey ctx) i) next+        next :/ i -> goPrv (f . flip (prvSubKey ctx) i) next+        Deriv -> f+    -- Build the full public derivation function starting from the end+    goPubE f p =+      case p of+        next :/ i -> goPubE (f . flip (pubSubKey ctx) i) next+        Deriv -> Right f+        _ -> Left "applyPath: Invalid hard derivation"++{- Helpers for derivation paths and addresses -}++-- | Derive an address from a given parent path.+derivePathAddr :: Ctx -> XPubKey -> SoftPath -> KeyIndex -> (Address, PubKey)+derivePathAddr ctx key path = deriveAddr ctx (derivePubPath ctx path key)++-- | Cyclic list of all addresses derived from a given parent path and starting+-- from the given offset index.+derivePathAddrs ::+  Ctx -> XPubKey -> SoftPath -> KeyIndex -> [(Address, PubKey, KeyIndex)]+derivePathAddrs ctx key path = deriveAddrs ctx (derivePubPath ctx path key)++-- | Derive a multisig address from a given parent path. The number of required+-- signatures (m in m of n) is also needed.+derivePathMSAddr ::+  Ctx ->+  [XPubKey] ->+  SoftPath ->+  Int ->+  KeyIndex ->+  (Address, RedeemScript)+derivePathMSAddr ctx keys path =+  deriveMSAddr ctx $ map (derivePubPath ctx path) keys++-- | Cyclic list of all multisig addresses derived from a given parent path and+-- starting from the given offset index. The number of required signatures+-- (m in m of n) is also needed.+derivePathMSAddrs ::+  Ctx ->+  [XPubKey] ->+  SoftPath ->+  Int ->+  KeyIndex ->+  [(Address, RedeemScript, KeyIndex)]+derivePathMSAddrs ctx keys path =+  deriveMSAddrs ctx $ map (derivePubPath ctx path) keys++{- Utilities for extended keys -}++-- | De-serialize HDW-specific private key.+getPadPrvKey :: (MonadGet m) => m SecKey+getPadPrvKey = do+  pad <- getWord8+  unless (pad == 0x00) $ fail "Private key must be padded with 0x00"+  bs <- getByteString 32+  case secKey bs of+    Nothing -> fail $ "Could not decode secret key: " ++ cs (encodeHex bs)+    Just x -> return x++-- | Serialize HDW-specific private key.+putPadPrvKey :: (MonadPut m) => SecKey -> m ()+putPadPrvKey p = putWord8 0x00 >> putByteString p.get++bsPadPrvKey :: SecKey -> ByteString+bsPadPrvKey = runPutS . putPadPrvKey
+ src/Haskoin/Crypto/Keys/Extended/Internal.hs view
@@ -0,0 +1,88 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE NoFieldSelectors #-}++module Haskoin.Crypto.Keys.Extended.Internal+  ( Fingerprint (..),+    fingerprintToText,+    textToFingerprint,+  )+where++import Control.DeepSeq (NFData)+import Control.Monad ((>=>))+import Data.Aeson+  ( FromJSON (parseJSON),+    ToJSON (toEncoding, toJSON),+    withText,+  )+import Data.Aeson.Encoding (text)+import Data.Binary (Binary (..))+import Data.Bytes.Get (getWord32be)+import Data.Bytes.Put (putWord32be)+import Data.Bytes.Serial (Serial (..))+import Data.Either (fromRight)+import Data.Hashable (Hashable)+import Data.Maybe (fromMaybe)+import Data.Serialize (Serialize (..))+import Data.Serialize qualified as S+import Data.String (IsString (..))+import Data.Text (Text)+import Data.Text qualified as Text+import Data.Typeable (Typeable)+import Data.Word (Word32)+import GHC.Generics (Generic)+import Haskoin.Util.Helpers (decodeHex, encodeHex)+import Text.Read (readEither, readPrec)++-- | Fingerprint of parent+newtype Fingerprint = Fingerprint {get :: Word32}+  deriving (Eq, Ord, Hashable, Typeable, Generic, NFData)++fingerprintToText :: Fingerprint -> Text+fingerprintToText = encodeHex . S.encode++textToFingerprint :: Text -> Either String Fingerprint+textToFingerprint =+  maybe (Left "Fingerprint: invalid hex") Right . decodeHex >=> S.decode++instance Show Fingerprint where+  show = show . Text.unpack . encodeHex . S.encode++instance Read Fingerprint where+  readPrec =+    readPrec+      >>= maybe (fail "Fingerprint: invalid hex") pure . decodeHex+      >>= either (fail . ("Fingerprint: " <>)) pure . S.decode++instance IsString Fingerprint where+  fromString =+    fromRight decodeError+      . S.decode+      . fromMaybe hexError+      . decodeHex+      . Text.pack+    where+      decodeError = error "Fingerprint literal: Unable to decode"+      hexError = error "Fingerprint literal: Invalid hex"++instance Serial Fingerprint where+  serialize = putWord32be . (.get)+  deserialize = Fingerprint <$> getWord32be++instance Binary Fingerprint where+  put = serialize+  get = deserialize++instance Serialize Fingerprint where+  put = serialize+  get = deserialize++instance FromJSON Fingerprint where+  parseJSON = withText "Fingerprint" $ either fail pure . textToFingerprint++instance ToJSON Fingerprint where+  toJSON = toJSON . fingerprintToText+  toEncoding = text . fingerprintToText
+ src/Haskoin/Crypto/Keys/Mnemonic.hs view
@@ -0,0 +1,2236 @@+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Keys.Mnemonic+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Mnemonic keys (BIP-39). Only English dictionary.+module Haskoin.Crypto.Keys.Mnemonic+  ( -- * Mnemonic Sentences+    Entropy,+    Mnemonic,+    Passphrase,+    Seed,+    toMnemonic,+    fromMnemonic,+    mnemonicToSeed,+    wordList,+    wordListMap,+  )+where++import Control.Monad (when)+import Crypto.Hash (SHA256 (..), hashWith)+import Crypto.KDF.PBKDF2 (Parameters (..), fastPBKDF2_SHA512)+import Data.Bits (shiftL, shiftR)+import Data.ByteArray qualified as BA+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.List+import Data.Map.Strict qualified as M+import Data.Maybe+import Data.String.Conversions (cs)+import Data.Text (Text)+import Data.Text qualified as T+import Data.Text.Encoding qualified as E+import Data.Vector (Vector, (!))+import Data.Vector qualified as V+import Haskoin.Util++-- | Random data used to create a mnemonic sentence. Use a good entropy source.+-- You will get your coins stolen if you don't. You have been warned.+type Entropy = ByteString++-- | Human-readable mnemonic sentence.+type Mnemonic = Text++-- | Optional passphrase for mnemnoic sentence.+type Passphrase = Text++-- | Seed for a private key from a mnemonic sentence.+type Seed = ByteString++-- | Mnemonic key checksum.+type Checksum = ByteString++-- | Paremeters for PBKDF2 function.+pbkdfParams :: Parameters+pbkdfParams = Parameters {iterCounts = 2048, outputLength = 64}++-- | Provide intial 'Entropy' as a 'ByteString' of length multiple of 4 bytes.+-- Output a 'Mnemonic' sentence.+toMnemonic :: Entropy -> Either String Mnemonic+toMnemonic ent = do+  when (B.null ent) $+    Left "toMnemonic: entropy can not be empty"+  when (remainder /= 0) $+    Left "toMnemonic: entropy must be multiples of 4 bytes"+  when (cs_len > 16) $+    Left "toMnemonic: maximum entropy is 64 bytes (512 bits)"+  return ms+  where+    (cs_len, remainder) = B.length ent `quotRem` 4+    c = calcCS cs_len ent+    indices = bsToIndices $ ent `B.append` c+    ms = T.unwords $ map (wordList !) indices++-- | Revert 'toMnemonic'. Do not use this to generate a 'Seed'. Instead use+-- 'mnemonicToSeed'. This outputs the original 'Entropy' used to generate a+-- 'Mnemonic' sentence.+fromMnemonic :: Mnemonic -> Either String Entropy+fromMnemonic ms = do+  when (T.null ms) $+    Left "fromMnemonic: empty mnemonic"+  when (word_count > 48) $+    Left $+      "fromMnemonic: too many words: " ++ show word_count+  when (word_count `mod` 3 /= 0) $+    Left $+      "fromMnemonic: wrong number of words:" ++ show word_count+  ms_bs <- indicesToBS =<< getIndices ms_words+  let (ms_ent, ms_cs) = B.splitAt (ent_len * 4) ms_bs+      ms_cs_num = numCS cs_len ms_cs+      ent_cs_num = numCS cs_len $ calcCS cs_len ms_ent+  when (ent_cs_num /= ms_cs_num) $+    Left $+      "fromMnemonic: checksum failed: " ++ sh ent_cs_num ms_cs_num+  return ms_ent+  where+    ms_words = T.words ms+    word_count = length ms_words+    (ent_len, cs_len) = (word_count * 11) `quotRem` 32+    sh cs_a cs_b = show cs_a ++ " /= " ++ show cs_b++-- | Compute 'Checksum'.+calcCS :: Int -> Entropy -> Checksum+calcCS len = getBits len . BA.convert . hashWith SHA256++numCS :: Int -> Entropy -> Integer+numCS len =+  shiftCS . bsToInteger+  where+    shiftCS = case 8 - len `mod` 8 of+      8 -> id+      x -> flip shiftR x++-- | Turn an arbitrary sequence of characters into a 512-bit 'Seed'. Use+-- 'mnemonicToSeed' to get a seed from a 'Mnemonic' sentence. Warning: Does not+-- perform NFKD normalization.+anyToSeed :: Passphrase -> Mnemonic -> Seed+anyToSeed pf ms =+  fastPBKDF2_SHA512+    pbkdfParams+    (E.encodeUtf8 ms)+    ("mnemonic" `mappend` E.encodeUtf8 pf)++-- | Get a 512-bit 'Seed' from a 'Mnemonic' sentence. Will validate checksum.+-- 'Passphrase' can be used to protect the 'Mnemonic'. Use an empty string as+-- 'Passphrase' if none is required.+mnemonicToSeed :: Passphrase -> Mnemonic -> Either String Seed+mnemonicToSeed pf ms = do+  ent <- fromMnemonic ms+  mnm <- toMnemonic ent+  return $ anyToSeed pf mnm++-- | Get indices of words in word list.+getIndices :: [Text] -> Either String [Int]+getIndices ws+  | null n = return $ catMaybes i+  | otherwise = Left $ "getIndices: words not found: " ++ cs w+  where+    i = map (`M.lookup` wordListMap) ws+    n = elemIndices Nothing i+    w = T.unwords $ map (ws !!) n++-- | Turn a list of 11-bit numbers into a 'ByteString'+indicesToBS :: [Int] -> Either String ByteString+indicesToBS is = do+  when lrg $ Left "indicesToBS: index larger or equal than 2048"+  return . pad . integerToBS $ foldl' f 0 is `shiftL` shift_width+  where+    lrg = isJust $ find (>= 2048) is+    (q, r) = (length is * 11) `quotRem` 8+    shift_width =+      if r == 0+        then 0+        else 8 - r+    bl =+      if r == 0+        then q+        else q + 1 -- length of resulting ByteString+    pad bs = B.append (B.replicate (bl - B.length bs) 0x00) bs+    f acc x = (acc `shiftL` 11) + fromIntegral x++-- | Turn a 'ByteString' into a list of 11-bit numbers.+bsToIndices :: ByteString -> [Int]+bsToIndices bs =+  reverse . go q $ bsToInteger bs `shiftR` r+  where+    (q, r) = (B.length bs * 8) `quotRem` 11+    go 0 _ = []+    go n i = fromIntegral (i `mod` 2048) : go (n - 1) (i `shiftR` 11)++-- | Map of words to their position (index) in the word list.+wordListMap :: M.Map Text Int+wordListMap = V.ifoldr' (flip M.insert) M.empty wordList++-- | Standard English dictionary from BIP-39 specification.+wordList :: Vector Text+wordList =+  V.fromListN+    2048+    [ "abandon",+      "ability",+      "able",+      "about",+      "above",+      "absent",+      "absorb",+      "abstract",+      "absurd",+      "abuse",+      "access",+      "accident",+      "account",+      "accuse",+      "achieve",+      "acid",+      "acoustic",+      "acquire",+      "across",+      "act",+      "action",+      "actor",+      "actress",+      "actual",+      "adapt",+      "add",+      "addict",+      "address",+      "adjust",+      "admit",+      "adult",+      "advance",+      "advice",+      "aerobic",+      "affair",+      "afford",+      "afraid",+      "again",+      "age",+      "agent",+      "agree",+      "ahead",+      "aim",+      "air",+      "airport",+      "aisle",+      "alarm",+      "album",+      "alcohol",+      "alert",+      "alien",+      "all",+      "alley",+      "allow",+      "almost",+      "alone",+      "alpha",+      "already",+      "also",+      "alter",+      "always",+      "amateur",+      "amazing",+      "among",+      "amount",+      "amused",+      "analyst",+      "anchor",+      "ancient",+      "anger",+      "angle",+      "angry",+      "animal",+      "ankle",+      "announce",+      "annual",+      "another",+      "answer",+      "antenna",+      "antique",+      "anxiety",+      "any",+      "apart",+      "apology",+      "appear",+      "apple",+      "approve",+      "april",+      "arch",+      "arctic",+      "area",+      "arena",+      "argue",+      "arm",+      "armed",+      "armor",+      "army",+      "around",+      "arrange",+      "arrest",+      "arrive",+      "arrow",+      "art",+      "artefact",+      "artist",+      "artwork",+      "ask",+      "aspect",+      "assault",+      "asset",+      "assist",+      "assume",+      "asthma",+      "athlete",+      "atom",+      "attack",+      "attend",+      "attitude",+      "attract",+      "auction",+      "audit",+      "august",+      "aunt",+      "author",+      "auto",+      "autumn",+      "average",+      "avocado",+      "avoid",+      "awake",+      "aware",+      "away",+      "awesome",+      "awful",+      "awkward",+      "axis",+      "baby",+      "bachelor",+      "bacon",+      "badge",+      "bag",+      "balance",+      "balcony",+      "ball",+      "bamboo",+      "banana",+      "banner",+      "bar",+      "barely",+      "bargain",+      "barrel",+      "base",+      "basic",+      "basket",+      "battle",+      "beach",+      "bean",+      "beauty",+      "because",+      "become",+      "beef",+      "before",+      "begin",+      "behave",+      "behind",+      "believe",+      "below",+      "belt",+      "bench",+      "benefit",+      "best",+      "betray",+      "better",+      "between",+      "beyond",+      "bicycle",+      "bid",+      "bike",+      "bind",+      "biology",+      "bird",+      "birth",+      "bitter",+      "black",+      "blade",+      "blame",+      "blanket",+      "blast",+      "bleak",+      "bless",+      "blind",+      "blood",+      "blossom",+      "blouse",+      "blue",+      "blur",+      "blush",+      "board",+      "boat",+      "body",+      "boil",+      "bomb",+      "bone",+      "bonus",+      "book",+      "boost",+      "border",+      "boring",+      "borrow",+      "boss",+      "bottom",+      "bounce",+      "box",+      "boy",+      "bracket",+      "brain",+      "brand",+      "brass",+      "brave",+      "bread",+      "breeze",+      "brick",+      "bridge",+      "brief",+      "bright",+      "bring",+      "brisk",+      "broccoli",+      "broken",+      "bronze",+      "broom",+      "brother",+      "brown",+      "brush",+      "bubble",+      "buddy",+      "budget",+      "buffalo",+      "build",+      "bulb",+      "bulk",+      "bullet",+      "bundle",+      "bunker",+      "burden",+      "burger",+      "burst",+      "bus",+      "business",+      "busy",+      "butter",+      "buyer",+      "buzz",+      "cabbage",+      "cabin",+      "cable",+      "cactus",+      "cage",+      "cake",+      "call",+      "calm",+      "camera",+      "camp",+      "can",+      "canal",+      "cancel",+      "candy",+      "cannon",+      "canoe",+      "canvas",+      "canyon",+      "capable",+      "capital",+      "captain",+      "car",+      "carbon",+      "card",+      "cargo",+      "carpet",+      "carry",+      "cart",+      "case",+      "cash",+      "casino",+      "castle",+      "casual",+      "cat",+      "catalog",+      "catch",+      "category",+      "cattle",+      "caught",+      "cause",+      "caution",+      "cave",+      "ceiling",+      "celery",+      "cement",+      "census",+      "century",+      "cereal",+      "certain",+      "chair",+      "chalk",+      "champion",+      "change",+      "chaos",+      "chapter",+      "charge",+      "chase",+      "chat",+      "cheap",+      "check",+      "cheese",+      "chef",+      "cherry",+      "chest",+      "chicken",+      "chief",+      "child",+      "chimney",+      "choice",+      "choose",+      "chronic",+      "chuckle",+      "chunk",+      "churn",+      "cigar",+      "cinnamon",+      "circle",+      "citizen",+      "city",+      "civil",+      "claim",+      "clap",+      "clarify",+      "claw",+      "clay",+      "clean",+      "clerk",+      "clever",+      "click",+      "client",+      "cliff",+      "climb",+      "clinic",+      "clip",+      "clock",+      "clog",+      "close",+      "cloth",+      "cloud",+      "clown",+      "club",+      "clump",+      "cluster",+      "clutch",+      "coach",+      "coast",+      "coconut",+      "code",+      "coffee",+      "coil",+      "coin",+      "collect",+      "color",+      "column",+      "combine",+      "come",+      "comfort",+      "comic",+      "common",+      "company",+      "concert",+      "conduct",+      "confirm",+      "congress",+      "connect",+      "consider",+      "control",+      "convince",+      "cook",+      "cool",+      "copper",+      "copy",+      "coral",+      "core",+      "corn",+      "correct",+      "cost",+      "cotton",+      "couch",+      "country",+      "couple",+      "course",+      "cousin",+      "cover",+      "coyote",+      "crack",+      "cradle",+      "craft",+      "cram",+      "crane",+      "crash",+      "crater",+      "crawl",+      "crazy",+      "cream",+      "credit",+      "creek",+      "crew",+      "cricket",+      "crime",+      "crisp",+      "critic",+      "crop",+      "cross",+      "crouch",+      "crowd",+      "crucial",+      "cruel",+      "cruise",+      "crumble",+      "crunch",+      "crush",+      "cry",+      "crystal",+      "cube",+      "culture",+      "cup",+      "cupboard",+      "curious",+      "current",+      "curtain",+      "curve",+      "cushion",+      "custom",+      "cute",+      "cycle",+      "dad",+      "damage",+      "damp",+      "dance",+      "danger",+      "daring",+      "dash",+      "daughter",+      "dawn",+      "day",+      "deal",+      "debate",+      "debris",+      "decade",+      "december",+      "decide",+      "decline",+      "decorate",+      "decrease",+      "deer",+      "defense",+      "define",+      "defy",+      "degree",+      "delay",+      "deliver",+      "demand",+      "demise",+      "denial",+      "dentist",+      "deny",+      "depart",+      "depend",+      "deposit",+      "depth",+      "deputy",+      "derive",+      "describe",+      "desert",+      "design",+      "desk",+      "despair",+      "destroy",+      "detail",+      "detect",+      "develop",+      "device",+      "devote",+      "diagram",+      "dial",+      "diamond",+      "diary",+      "dice",+      "diesel",+      "diet",+      "differ",+      "digital",+      "dignity",+      "dilemma",+      "dinner",+      "dinosaur",+      "direct",+      "dirt",+      "disagree",+      "discover",+      "disease",+      "dish",+      "dismiss",+      "disorder",+      "display",+      "distance",+      "divert",+      "divide",+      "divorce",+      "dizzy",+      "doctor",+      "document",+      "dog",+      "doll",+      "dolphin",+      "domain",+      "donate",+      "donkey",+      "donor",+      "door",+      "dose",+      "double",+      "dove",+      "draft",+      "dragon",+      "drama",+      "drastic",+      "draw",+      "dream",+      "dress",+      "drift",+      "drill",+      "drink",+      "drip",+      "drive",+      "drop",+      "drum",+      "dry",+      "duck",+      "dumb",+      "dune",+      "during",+      "dust",+      "dutch",+      "duty",+      "dwarf",+      "dynamic",+      "eager",+      "eagle",+      "early",+      "earn",+      "earth",+      "easily",+      "east",+      "easy",+      "echo",+      "ecology",+      "economy",+      "edge",+      "edit",+      "educate",+      "effort",+      "egg",+      "eight",+      "either",+      "elbow",+      "elder",+      "electric",+      "elegant",+      "element",+      "elephant",+      "elevator",+      "elite",+      "else",+      "embark",+      "embody",+      "embrace",+      "emerge",+      "emotion",+      "employ",+      "empower",+      "empty",+      "enable",+      "enact",+      "end",+      "endless",+      "endorse",+      "enemy",+      "energy",+      "enforce",+      "engage",+      "engine",+      "enhance",+      "enjoy",+      "enlist",+      "enough",+      "enrich",+      "enroll",+      "ensure",+      "enter",+      "entire",+      "entry",+      "envelope",+      "episode",+      "equal",+      "equip",+      "era",+      "erase",+      "erode",+      "erosion",+      "error",+      "erupt",+      "escape",+      "essay",+      "essence",+      "estate",+      "eternal",+      "ethics",+      "evidence",+      "evil",+      "evoke",+      "evolve",+      "exact",+      "example",+      "excess",+      "exchange",+      "excite",+      "exclude",+      "excuse",+      "execute",+      "exercise",+      "exhaust",+      "exhibit",+      "exile",+      "exist",+      "exit",+      "exotic",+      "expand",+      "expect",+      "expire",+      "explain",+      "expose",+      "express",+      "extend",+      "extra",+      "eye",+      "eyebrow",+      "fabric",+      "face",+      "faculty",+      "fade",+      "faint",+      "faith",+      "fall",+      "false",+      "fame",+      "family",+      "famous",+      "fan",+      "fancy",+      "fantasy",+      "farm",+      "fashion",+      "fat",+      "fatal",+      "father",+      "fatigue",+      "fault",+      "favorite",+      "feature",+      "february",+      "federal",+      "fee",+      "feed",+      "feel",+      "female",+      "fence",+      "festival",+      "fetch",+      "fever",+      "few",+      "fiber",+      "fiction",+      "field",+      "figure",+      "file",+      "film",+      "filter",+      "final",+      "find",+      "fine",+      "finger",+      "finish",+      "fire",+      "firm",+      "first",+      "fiscal",+      "fish",+      "fit",+      "fitness",+      "fix",+      "flag",+      "flame",+      "flash",+      "flat",+      "flavor",+      "flee",+      "flight",+      "flip",+      "float",+      "flock",+      "floor",+      "flower",+      "fluid",+      "flush",+      "fly",+      "foam",+      "focus",+      "fog",+      "foil",+      "fold",+      "follow",+      "food",+      "foot",+      "force",+      "forest",+      "forget",+      "fork",+      "fortune",+      "forum",+      "forward",+      "fossil",+      "foster",+      "found",+      "fox",+      "fragile",+      "frame",+      "frequent",+      "fresh",+      "friend",+      "fringe",+      "frog",+      "front",+      "frost",+      "frown",+      "frozen",+      "fruit",+      "fuel",+      "fun",+      "funny",+      "furnace",+      "fury",+      "future",+      "gadget",+      "gain",+      "galaxy",+      "gallery",+      "game",+      "gap",+      "garage",+      "garbage",+      "garden",+      "garlic",+      "garment",+      "gas",+      "gasp",+      "gate",+      "gather",+      "gauge",+      "gaze",+      "general",+      "genius",+      "genre",+      "gentle",+      "genuine",+      "gesture",+      "ghost",+      "giant",+      "gift",+      "giggle",+      "ginger",+      "giraffe",+      "girl",+      "give",+      "glad",+      "glance",+      "glare",+      "glass",+      "glide",+      "glimpse",+      "globe",+      "gloom",+      "glory",+      "glove",+      "glow",+      "glue",+      "goat",+      "goddess",+      "gold",+      "good",+      "goose",+      "gorilla",+      "gospel",+      "gossip",+      "govern",+      "gown",+      "grab",+      "grace",+      "grain",+      "grant",+      "grape",+      "grass",+      "gravity",+      "great",+      "green",+      "grid",+      "grief",+      "grit",+      "grocery",+      "group",+      "grow",+      "grunt",+      "guard",+      "guess",+      "guide",+      "guilt",+      "guitar",+      "gun",+      "gym",+      "habit",+      "hair",+      "half",+      "hammer",+      "hamster",+      "hand",+      "happy",+      "harbor",+      "hard",+      "harsh",+      "harvest",+      "hat",+      "have",+      "hawk",+      "hazard",+      "head",+      "health",+      "heart",+      "heavy",+      "hedgehog",+      "height",+      "hello",+      "helmet",+      "help",+      "hen",+      "hero",+      "hidden",+      "high",+      "hill",+      "hint",+      "hip",+      "hire",+      "history",+      "hobby",+      "hockey",+      "hold",+      "hole",+      "holiday",+      "hollow",+      "home",+      "honey",+      "hood",+      "hope",+      "horn",+      "horror",+      "horse",+      "hospital",+      "host",+      "hotel",+      "hour",+      "hover",+      "hub",+      "huge",+      "human",+      "humble",+      "humor",+      "hundred",+      "hungry",+      "hunt",+      "hurdle",+      "hurry",+      "hurt",+      "husband",+      "hybrid",+      "ice",+      "icon",+      "idea",+      "identify",+      "idle",+      "ignore",+      "ill",+      "illegal",+      "illness",+      "image",+      "imitate",+      "immense",+      "immune",+      "impact",+      "impose",+      "improve",+      "impulse",+      "inch",+      "include",+      "income",+      "increase",+      "index",+      "indicate",+      "indoor",+      "industry",+      "infant",+      "inflict",+      "inform",+      "inhale",+      "inherit",+      "initial",+      "inject",+      "injury",+      "inmate",+      "inner",+      "innocent",+      "input",+      "inquiry",+      "insane",+      "insect",+      "inside",+      "inspire",+      "install",+      "intact",+      "interest",+      "into",+      "invest",+      "invite",+      "involve",+      "iron",+      "island",+      "isolate",+      "issue",+      "item",+      "ivory",+      "jacket",+      "jaguar",+      "jar",+      "jazz",+      "jealous",+      "jeans",+      "jelly",+      "jewel",+      "job",+      "join",+      "joke",+      "journey",+      "joy",+      "judge",+      "juice",+      "jump",+      "jungle",+      "junior",+      "junk",+      "just",+      "kangaroo",+      "keen",+      "keep",+      "ketchup",+      "key",+      "kick",+      "kid",+      "kidney",+      "kind",+      "kingdom",+      "kiss",+      "kit",+      "kitchen",+      "kite",+      "kitten",+      "kiwi",+      "knee",+      "knife",+      "knock",+      "know",+      "lab",+      "label",+      "labor",+      "ladder",+      "lady",+      "lake",+      "lamp",+      "language",+      "laptop",+      "large",+      "later",+      "latin",+      "laugh",+      "laundry",+      "lava",+      "law",+      "lawn",+      "lawsuit",+      "layer",+      "lazy",+      "leader",+      "leaf",+      "learn",+      "leave",+      "lecture",+      "left",+      "leg",+      "legal",+      "legend",+      "leisure",+      "lemon",+      "lend",+      "length",+      "lens",+      "leopard",+      "lesson",+      "letter",+      "level",+      "liar",+      "liberty",+      "library",+      "license",+      "life",+      "lift",+      "light",+      "like",+      "limb",+      "limit",+      "link",+      "lion",+      "liquid",+      "list",+      "little",+      "live",+      "lizard",+      "load",+      "loan",+      "lobster",+      "local",+      "lock",+      "logic",+      "lonely",+      "long",+      "loop",+      "lottery",+      "loud",+      "lounge",+      "love",+      "loyal",+      "lucky",+      "luggage",+      "lumber",+      "lunar",+      "lunch",+      "luxury",+      "lyrics",+      "machine",+      "mad",+      "magic",+      "magnet",+      "maid",+      "mail",+      "main",+      "major",+      "make",+      "mammal",+      "man",+      "manage",+      "mandate",+      "mango",+      "mansion",+      "manual",+      "maple",+      "marble",+      "march",+      "margin",+      "marine",+      "market",+      "marriage",+      "mask",+      "mass",+      "master",+      "match",+      "material",+      "math",+      "matrix",+      "matter",+      "maximum",+      "maze",+      "meadow",+      "mean",+      "measure",+      "meat",+      "mechanic",+      "medal",+      "media",+      "melody",+      "melt",+      "member",+      "memory",+      "mention",+      "menu",+      "mercy",+      "merge",+      "merit",+      "merry",+      "mesh",+      "message",+      "metal",+      "method",+      "middle",+      "midnight",+      "milk",+      "million",+      "mimic",+      "mind",+      "minimum",+      "minor",+      "minute",+      "miracle",+      "mirror",+      "misery",+      "miss",+      "mistake",+      "mix",+      "mixed",+      "mixture",+      "mobile",+      "model",+      "modify",+      "mom",+      "moment",+      "monitor",+      "monkey",+      "monster",+      "month",+      "moon",+      "moral",+      "more",+      "morning",+      "mosquito",+      "mother",+      "motion",+      "motor",+      "mountain",+      "mouse",+      "move",+      "movie",+      "much",+      "muffin",+      "mule",+      "multiply",+      "muscle",+      "museum",+      "mushroom",+      "music",+      "must",+      "mutual",+      "myself",+      "mystery",+      "myth",+      "naive",+      "name",+      "napkin",+      "narrow",+      "nasty",+      "nation",+      "nature",+      "near",+      "neck",+      "need",+      "negative",+      "neglect",+      "neither",+      "nephew",+      "nerve",+      "nest",+      "net",+      "network",+      "neutral",+      "never",+      "news",+      "next",+      "nice",+      "night",+      "noble",+      "noise",+      "nominee",+      "noodle",+      "normal",+      "north",+      "nose",+      "notable",+      "note",+      "nothing",+      "notice",+      "novel",+      "now",+      "nuclear",+      "number",+      "nurse",+      "nut",+      "oak",+      "obey",+      "object",+      "oblige",+      "obscure",+      "observe",+      "obtain",+      "obvious",+      "occur",+      "ocean",+      "october",+      "odor",+      "off",+      "offer",+      "office",+      "often",+      "oil",+      "okay",+      "old",+      "olive",+      "olympic",+      "omit",+      "once",+      "one",+      "onion",+      "online",+      "only",+      "open",+      "opera",+      "opinion",+      "oppose",+      "option",+      "orange",+      "orbit",+      "orchard",+      "order",+      "ordinary",+      "organ",+      "orient",+      "original",+      "orphan",+      "ostrich",+      "other",+      "outdoor",+      "outer",+      "output",+      "outside",+      "oval",+      "oven",+      "over",+      "own",+      "owner",+      "oxygen",+      "oyster",+      "ozone",+      "pact",+      "paddle",+      "page",+      "pair",+      "palace",+      "palm",+      "panda",+      "panel",+      "panic",+      "panther",+      "paper",+      "parade",+      "parent",+      "park",+      "parrot",+      "party",+      "pass",+      "patch",+      "path",+      "patient",+      "patrol",+      "pattern",+      "pause",+      "pave",+      "payment",+      "peace",+      "peanut",+      "pear",+      "peasant",+      "pelican",+      "pen",+      "penalty",+      "pencil",+      "people",+      "pepper",+      "perfect",+      "permit",+      "person",+      "pet",+      "phone",+      "photo",+      "phrase",+      "physical",+      "piano",+      "picnic",+      "picture",+      "piece",+      "pig",+      "pigeon",+      "pill",+      "pilot",+      "pink",+      "pioneer",+      "pipe",+      "pistol",+      "pitch",+      "pizza",+      "place",+      "planet",+      "plastic",+      "plate",+      "play",+      "please",+      "pledge",+      "pluck",+      "plug",+      "plunge",+      "poem",+      "poet",+      "point",+      "polar",+      "pole",+      "police",+      "pond",+      "pony",+      "pool",+      "popular",+      "portion",+      "position",+      "possible",+      "post",+      "potato",+      "pottery",+      "poverty",+      "powder",+      "power",+      "practice",+      "praise",+      "predict",+      "prefer",+      "prepare",+      "present",+      "pretty",+      "prevent",+      "price",+      "pride",+      "primary",+      "print",+      "priority",+      "prison",+      "private",+      "prize",+      "problem",+      "process",+      "produce",+      "profit",+      "program",+      "project",+      "promote",+      "proof",+      "property",+      "prosper",+      "protect",+      "proud",+      "provide",+      "public",+      "pudding",+      "pull",+      "pulp",+      "pulse",+      "pumpkin",+      "punch",+      "pupil",+      "puppy",+      "purchase",+      "purity",+      "purpose",+      "purse",+      "push",+      "put",+      "puzzle",+      "pyramid",+      "quality",+      "quantum",+      "quarter",+      "question",+      "quick",+      "quit",+      "quiz",+      "quote",+      "rabbit",+      "raccoon",+      "race",+      "rack",+      "radar",+      "radio",+      "rail",+      "rain",+      "raise",+      "rally",+      "ramp",+      "ranch",+      "random",+      "range",+      "rapid",+      "rare",+      "rate",+      "rather",+      "raven",+      "raw",+      "razor",+      "ready",+      "real",+      "reason",+      "rebel",+      "rebuild",+      "recall",+      "receive",+      "recipe",+      "record",+      "recycle",+      "reduce",+      "reflect",+      "reform",+      "refuse",+      "region",+      "regret",+      "regular",+      "reject",+      "relax",+      "release",+      "relief",+      "rely",+      "remain",+      "remember",+      "remind",+      "remove",+      "render",+      "renew",+      "rent",+      "reopen",+      "repair",+      "repeat",+      "replace",+      "report",+      "require",+      "rescue",+      "resemble",+      "resist",+      "resource",+      "response",+      "result",+      "retire",+      "retreat",+      "return",+      "reunion",+      "reveal",+      "review",+      "reward",+      "rhythm",+      "rib",+      "ribbon",+      "rice",+      "rich",+      "ride",+      "ridge",+      "rifle",+      "right",+      "rigid",+      "ring",+      "riot",+      "ripple",+      "risk",+      "ritual",+      "rival",+      "river",+      "road",+      "roast",+      "robot",+      "robust",+      "rocket",+      "romance",+      "roof",+      "rookie",+      "room",+      "rose",+      "rotate",+      "rough",+      "round",+      "route",+      "royal",+      "rubber",+      "rude",+      "rug",+      "rule",+      "run",+      "runway",+      "rural",+      "sad",+      "saddle",+      "sadness",+      "safe",+      "sail",+      "salad",+      "salmon",+      "salon",+      "salt",+      "salute",+      "same",+      "sample",+      "sand",+      "satisfy",+      "satoshi",+      "sauce",+      "sausage",+      "save",+      "say",+      "scale",+      "scan",+      "scare",+      "scatter",+      "scene",+      "scheme",+      "school",+      "science",+      "scissors",+      "scorpion",+      "scout",+      "scrap",+      "screen",+      "script",+      "scrub",+      "sea",+      "search",+      "season",+      "seat",+      "second",+      "secret",+      "section",+      "security",+      "seed",+      "seek",+      "segment",+      "select",+      "sell",+      "seminar",+      "senior",+      "sense",+      "sentence",+      "series",+      "service",+      "session",+      "settle",+      "setup",+      "seven",+      "shadow",+      "shaft",+      "shallow",+      "share",+      "shed",+      "shell",+      "sheriff",+      "shield",+      "shift",+      "shine",+      "ship",+      "shiver",+      "shock",+      "shoe",+      "shoot",+      "shop",+      "short",+      "shoulder",+      "shove",+      "shrimp",+      "shrug",+      "shuffle",+      "shy",+      "sibling",+      "sick",+      "side",+      "siege",+      "sight",+      "sign",+      "silent",+      "silk",+      "silly",+      "silver",+      "similar",+      "simple",+      "since",+      "sing",+      "siren",+      "sister",+      "situate",+      "six",+      "size",+      "skate",+      "sketch",+      "ski",+      "skill",+      "skin",+      "skirt",+      "skull",+      "slab",+      "slam",+      "sleep",+      "slender",+      "slice",+      "slide",+      "slight",+      "slim",+      "slogan",+      "slot",+      "slow",+      "slush",+      "small",+      "smart",+      "smile",+      "smoke",+      "smooth",+      "snack",+      "snake",+      "snap",+      "sniff",+      "snow",+      "soap",+      "soccer",+      "social",+      "sock",+      "soda",+      "soft",+      "solar",+      "soldier",+      "solid",+      "solution",+      "solve",+      "someone",+      "song",+      "soon",+      "sorry",+      "sort",+      "soul",+      "sound",+      "soup",+      "source",+      "south",+      "space",+      "spare",+      "spatial",+      "spawn",+      "speak",+      "special",+      "speed",+      "spell",+      "spend",+      "sphere",+      "spice",+      "spider",+      "spike",+      "spin",+      "spirit",+      "split",+      "spoil",+      "sponsor",+      "spoon",+      "sport",+      "spot",+      "spray",+      "spread",+      "spring",+      "spy",+      "square",+      "squeeze",+      "squirrel",+      "stable",+      "stadium",+      "staff",+      "stage",+      "stairs",+      "stamp",+      "stand",+      "start",+      "state",+      "stay",+      "steak",+      "steel",+      "stem",+      "step",+      "stereo",+      "stick",+      "still",+      "sting",+      "stock",+      "stomach",+      "stone",+      "stool",+      "story",+      "stove",+      "strategy",+      "street",+      "strike",+      "strong",+      "struggle",+      "student",+      "stuff",+      "stumble",+      "style",+      "subject",+      "submit",+      "subway",+      "success",+      "such",+      "sudden",+      "suffer",+      "sugar",+      "suggest",+      "suit",+      "summer",+      "sun",+      "sunny",+      "sunset",+      "super",+      "supply",+      "supreme",+      "sure",+      "surface",+      "surge",+      "surprise",+      "surround",+      "survey",+      "suspect",+      "sustain",+      "swallow",+      "swamp",+      "swap",+      "swarm",+      "swear",+      "sweet",+      "swift",+      "swim",+      "swing",+      "switch",+      "sword",+      "symbol",+      "symptom",+      "syrup",+      "system",+      "table",+      "tackle",+      "tag",+      "tail",+      "talent",+      "talk",+      "tank",+      "tape",+      "target",+      "task",+      "taste",+      "tattoo",+      "taxi",+      "teach",+      "team",+      "tell",+      "ten",+      "tenant",+      "tennis",+      "tent",+      "term",+      "test",+      "text",+      "thank",+      "that",+      "theme",+      "then",+      "theory",+      "there",+      "they",+      "thing",+      "this",+      "thought",+      "three",+      "thrive",+      "throw",+      "thumb",+      "thunder",+      "ticket",+      "tide",+      "tiger",+      "tilt",+      "timber",+      "time",+      "tiny",+      "tip",+      "tired",+      "tissue",+      "title",+      "toast",+      "tobacco",+      "today",+      "toddler",+      "toe",+      "together",+      "toilet",+      "token",+      "tomato",+      "tomorrow",+      "tone",+      "tongue",+      "tonight",+      "tool",+      "tooth",+      "top",+      "topic",+      "topple",+      "torch",+      "tornado",+      "tortoise",+      "toss",+      "total",+      "tourist",+      "toward",+      "tower",+      "town",+      "toy",+      "track",+      "trade",+      "traffic",+      "tragic",+      "train",+      "transfer",+      "trap",+      "trash",+      "travel",+      "tray",+      "treat",+      "tree",+      "trend",+      "trial",+      "tribe",+      "trick",+      "trigger",+      "trim",+      "trip",+      "trophy",+      "trouble",+      "truck",+      "true",+      "truly",+      "trumpet",+      "trust",+      "truth",+      "try",+      "tube",+      "tuition",+      "tumble",+      "tuna",+      "tunnel",+      "turkey",+      "turn",+      "turtle",+      "twelve",+      "twenty",+      "twice",+      "twin",+      "twist",+      "two",+      "type",+      "typical",+      "ugly",+      "umbrella",+      "unable",+      "unaware",+      "uncle",+      "uncover",+      "under",+      "undo",+      "unfair",+      "unfold",+      "unhappy",+      "uniform",+      "unique",+      "unit",+      "universe",+      "unknown",+      "unlock",+      "until",+      "unusual",+      "unveil",+      "update",+      "upgrade",+      "uphold",+      "upon",+      "upper",+      "upset",+      "urban",+      "urge",+      "usage",+      "use",+      "used",+      "useful",+      "useless",+      "usual",+      "utility",+      "vacant",+      "vacuum",+      "vague",+      "valid",+      "valley",+      "valve",+      "van",+      "vanish",+      "vapor",+      "various",+      "vast",+      "vault",+      "vehicle",+      "velvet",+      "vendor",+      "venture",+      "venue",+      "verb",+      "verify",+      "version",+      "very",+      "vessel",+      "veteran",+      "viable",+      "vibrant",+      "vicious",+      "victory",+      "video",+      "view",+      "village",+      "vintage",+      "violin",+      "virtual",+      "virus",+      "visa",+      "visit",+      "visual",+      "vital",+      "vivid",+      "vocal",+      "voice",+      "void",+      "volcano",+      "volume",+      "vote",+      "voyage",+      "wage",+      "wagon",+      "wait",+      "walk",+      "wall",+      "walnut",+      "want",+      "warfare",+      "warm",+      "warrior",+      "wash",+      "wasp",+      "waste",+      "water",+      "wave",+      "way",+      "wealth",+      "weapon",+      "wear",+      "weasel",+      "weather",+      "web",+      "wedding",+      "weekend",+      "weird",+      "welcome",+      "west",+      "wet",+      "whale",+      "what",+      "wheat",+      "wheel",+      "when",+      "where",+      "whip",+      "whisper",+      "wide",+      "width",+      "wife",+      "wild",+      "will",+      "win",+      "window",+      "wine",+      "wing",+      "wink",+      "winner",+      "winter",+      "wire",+      "wisdom",+      "wise",+      "wish",+      "witness",+      "wolf",+      "woman",+      "wonder",+      "wood",+      "wool",+      "word",+      "work",+      "world",+      "worry",+      "worth",+      "wrap",+      "wreck",+      "wrestle",+      "wrist",+      "write",+      "wrong",+      "yard",+      "year",+      "yellow",+      "you",+      "young",+      "youth",+      "zebra",+      "zero",+      "zone",+      "zoo"+    ]
+ src/Haskoin/Crypto/Signature.hs view
@@ -0,0 +1,106 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module      : Haskoin.Crypto.Signature+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- ECDSA signatures using secp256k1 curve. Uses functions from upstream secp256k1+-- library.+module Haskoin.Crypto.Signature+  ( -- * Signatures+    signHash,+    verifyHashSig,+    isCanonicalHalfOrder,+    decodeStrictSig,+    exportSig,+  )+where++import Control.Monad (guard, unless, when)+import Crypto.Secp256k1+import Data.Aeson+import Data.Aeson.Encoding+import Data.Binary (Binary (..))+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.ByteString.Lazy qualified as L+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Maybe (fromMaybe, isNothing)+import Data.Serialize (Serialize (..))+import Data.Text qualified as T+import Haskoin.Crypto.Hash+import Haskoin.Util.Helpers+import Haskoin.Util.Marshal+import Numeric (showHex)++-- | Convert 256-bit hash into a 'Msg' for signing or verification.+hashToMsg :: Hash256 -> Msg+hashToMsg =+  fromMaybe e . msg . runPutS . serialize+  where+    e = error "Could not convert 32-byte hash to secp256k1 message"++-- | Sign a 256-bit hash using secp256k1 elliptic curve.+signHash :: Ctx -> SecKey -> Hash256 -> Sig+signHash ctx k = signMsg ctx k . hashToMsg++-- | Verify an ECDSA signature for a 256-bit hash.+verifyHashSig :: Ctx -> Hash256 -> Sig -> PubKey -> Bool+verifyHashSig ctx h s p = verifySig ctx p norm (hashToMsg h)+  where+    norm = fromMaybe s (normalizeSig ctx s)++instance Marshal Ctx Sig where+  marshalGet ctx = do+    l <- lookAhead $ do+      t <- getWord8+      -- 0x30 is DER sequence type+      unless (t == 0x30) $+        fail $+          "Bad DER identifier byte 0x" ++ showHex t ". Expecting 0x30"+      l <- getWord8+      when (l == 0x00) $ fail "Indeterminate form unsupported"+      when (l >= 0x80) $ fail "Multi-octect length not supported"+      return $ fromIntegral l+    bs <- getByteString $ l + 2+    case decodeStrictSig ctx bs of+      Just s -> return s+      Nothing -> fail "Invalid signature"++  marshalPut ctx s = putByteString $ exportSig ctx s++instance MarshalJSON Ctx Sig where+  marshalValue ctx = String . encodeHex . exportSig ctx+  marshalEncoding ctx = hexEncoding . L.fromStrict . exportSig ctx+  unmarshalValue ctx =+    withText "Sig" $ \t ->+      case decodeHex t >>= importSig ctx of+        Nothing -> fail $ "Could not decode signature: " <> T.unpack t+        Just s -> return s++-- | Is canonical half order.+isCanonicalHalfOrder :: Ctx -> Sig -> Bool+isCanonicalHalfOrder ctx = isNothing . normalizeSig ctx++-- | Decode signature strictly.+decodeStrictSig :: Ctx -> ByteString -> Maybe Sig+decodeStrictSig ctx bs = do+  g <- importSig ctx bs+  -- <http://www.secg.org/sec1-v2.pdf Section 4.1.4>+  -- 4.1.4.1 (r and s can not be zero)+  let compact = exportCompactSig ctx g+  let zero = B.replicate 32 0+  guard $ B.take 32 compact.get /= zero+  guard $ (B.take 32 . B.drop 32) compact.get /= zero+  guard $ isCanonicalHalfOrder ctx g+  return g
+ src/Haskoin/Network.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE DuplicateRecordFields #-}++-- |+-- Module      : Haskoin.Network+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- This module provides basic types used for the Bitcoin networking protocol+-- together with 'Data.Serialize' instances for efficiently serializing and+-- de-serializing them.+module Haskoin.Network+  ( module Data,+    module Constants,+    module Common,+    module Message,+    module Bloom,+  )+where++import Haskoin.Network.Bloom as Bloom+import Haskoin.Network.Common as Common+import Haskoin.Network.Constants as Constants+import Haskoin.Network.Data as Data+import Haskoin.Network.Message as Message
+ src/Haskoin/Network/Bloom.hs view
@@ -0,0 +1,345 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Network.Bloom+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Bloom filters are used to reduce data transfer when synchronizing thin cients.+-- When bloom filters are used a client will obtain filtered blocks that only+-- contain transactions that pass the bloom filter. Transactions announced via inv+-- messages also pass the filter.+module Haskoin.Network.Bloom+  ( -- * Bloom Filters+    BloomFlags (..),+    BloomFilter (..),+    FilterLoad (..),+    FilterAdd (..),+    bloomCreate,+    bloomInsert,+    bloomContains,+    isBloomValid,+    isBloomEmpty,+    isBloomFull,+    acceptsFilters,+    bloomRelevantUpdate,+  )+where++import Control.DeepSeq+import Control.Monad (forM_, replicateM)+import Crypto.Secp256k1 (Ctx)+import Data.Binary (Binary (..))+import Data.Bits+import Data.ByteString (ByteString)+import Data.ByteString qualified as BS+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Foldable qualified as F+import Data.Hash.Murmur (murmur3)+import Data.List (foldl')+import Data.Sequence qualified as S+import Data.Serialize (Serialize (..))+import Data.Word+import GHC.Generics (Generic)+import Haskoin.Network.Common+import Haskoin.Script.Standard+import Haskoin.Transaction.Common+import Haskoin.Util.Marshal++-- | 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001%+maxBloomSize :: Int+maxBloomSize = 36000++maxHashFuncs :: Word32+maxHashFuncs = 50++ln2Squared :: Double+ln2Squared = 0.4804530139182014246671025263266649717305529515945455++ln2 :: Double+ln2 = 0.6931471805599453094172321214581765680755001343602552++bitMask :: [Word8]+bitMask = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]++-- | The bloom flags are used to tell the remote peer how to auto-update+-- the provided bloom filter.+data BloomFlags+  = -- | never update+    BloomUpdateNone+  | -- | auto-update on all outputs+    BloomUpdateAll+  | -- | auto-update on pay-to-pubkey or pay-to-multisig (default)+    BloomUpdateP2PubKeyOnly+  deriving (Eq, Show, Read, Generic, NFData)++instance Serial BloomFlags where+  deserialize = go =<< getWord8+    where+      go 0 = return BloomUpdateNone+      go 1 = return BloomUpdateAll+      go 2 = return BloomUpdateP2PubKeyOnly+      go _ = fail "BloomFlags get: Invalid bloom flag"++  serialize f = putWord8 $ case f of+    BloomUpdateNone -> 0+    BloomUpdateAll -> 1+    BloomUpdateP2PubKeyOnly -> 2++instance Binary BloomFlags where+  get = deserialize+  put = serialize++instance Serialize BloomFlags where+  get = deserialize+  put = serialize++-- | A bloom filter is a probabilistic data structure that SPV clients send to+-- other peers to filter the set of transactions received from them. Bloom+-- filters can have false positives but not false negatives. Some transactions+-- that pass the filter may not be relevant to the receiving peer. By+-- controlling the false positive rate, SPV nodes can trade off bandwidth+-- versus privacy.+data BloomFilter = BloomFilter+  { -- | bloom filter data+    array :: !(S.Seq Word8),+    -- | number of hash functions for this filter+    functions :: !Word32,+    -- | hash function random nonce+    tweak :: !Word32,+    -- | bloom filter auto-update flags+    flags :: !BloomFlags+  }+  deriving (Eq, Show, Read, Generic, NFData)++instance Serial BloomFilter where+  deserialize =+    BloomFilter+      <$> (S.fromList <$> (readDat =<< deserialize))+      <*> getWord32le+      <*> getWord32le+      <*> deserialize+    where+      readDat (VarInt len) = replicateM (fromIntegral len) getWord8++  serialize BloomFilter {..} = do+    putVarInt $ S.length array+    mapM_ putWord8 (F.toList array)+    putWord32le functions+    putWord32le tweak+    serialize flags++instance Binary BloomFilter where+  put = serialize+  get = deserialize++instance Serialize BloomFilter where+  put = serialize+  get = deserialize++-- | Set a new bloom filter on the peer connection.+newtype FilterLoad = FilterLoad {filter :: BloomFilter}+  deriving (Eq, Show, Read, Generic)+  deriving newtype (NFData)++instance Serial FilterLoad where+  deserialize = FilterLoad <$> deserialize+  serialize (FilterLoad f) = serialize f++instance Binary FilterLoad where+  put = serialize+  get = deserialize++instance Serialize FilterLoad where+  put = serialize+  get = deserialize++-- | Add the given data element to the connections current filter without+-- requiring a completely new one to be set.+newtype FilterAdd = FilterAdd {get :: ByteString}+  deriving (Eq, Show, Read, Generic)+  deriving newtype (NFData)++instance Serial FilterAdd where+  deserialize = do+    (VarInt len) <- deserialize+    dat <- getByteString $ fromIntegral len+    return $ FilterAdd dat++  serialize (FilterAdd bs) = do+    putVarInt $ BS.length bs+    putByteString bs++instance Binary FilterAdd where+  put = serialize+  get = deserialize++instance Serialize FilterAdd where+  put = serialize+  get = deserialize++-- | Build a bloom filter that will provide the given false positive rate when+-- the given number of elements have been inserted.+bloomCreate ::+  -- | number of elements+  Int ->+  -- | false positive rate+  Double ->+  -- | random nonce (tweak) for the hash function+  Word32 ->+  -- | bloom filter flags+  BloomFlags ->+  -- | bloom filter+  BloomFilter+bloomCreate numElem fpRate =+  BloomFilter (S.replicate bloomSize 0) numHashF+  where+    -- Bloom filter size in bytes+    bloomSize = truncate $ min a b / 8+    -- Suggested size in bits+    a = -1 / ln2Squared * fromIntegral numElem * log fpRate+    -- Maximum size in bits+    b = fromIntegral $ maxBloomSize * 8+    numHashF = truncate $ min c (fromIntegral maxHashFuncs)+    -- Suggested number of hash functions+    c = fromIntegral bloomSize * 8 / fromIntegral numElem * ln2++bloomHash :: BloomFilter -> Word32 -> ByteString -> Word32+bloomHash b hashNum bs =+  murmur3 seed bs `mod` (fromIntegral (S.length (b.array)) * 8)+  where+    seed = hashNum * 0xfba4c795 + b.tweak++-- | Insert arbitrary data into a bloom filter. Returns the new bloom filter+-- containing the new data.+bloomInsert ::+  -- | Original bloom filter+  BloomFilter ->+  -- | New data to insert+  ByteString ->+  -- | Bloom filter containing the new data+  BloomFilter+bloomInsert b bs+  | isBloomFull b = b+  | otherwise = b {array = dat}+  where+    idxs = map (\i -> bloomHash b i bs) [0 .. b.functions - 1]+    upd s i =+      S.adjust+        (.|. bitMask !! fromIntegral (7 .&. i))+        (fromIntegral $ i `shiftR` 3)+        s+    dat = foldl upd b.array idxs++-- | Tests if some arbitrary data matches the filter. This can be either because+-- the data was inserted into the filter or because it is a false positive.+bloomContains ::+  -- | Bloom filter+  BloomFilter ->+  -- | Data that will be checked against the given bloom filter+  ByteString ->+  -- | Returns True if the data matches the filter+  Bool+bloomContains b bs+  | isBloomFull b = True+  | isBloomEmpty b = False+  | otherwise = all isSet idxs+  where+    s = b.array+    idxs = map (\i -> bloomHash b i bs) [0 .. b.functions - 1]+    isSet i =+      S.index s (fromIntegral $ i `shiftR` 3)+        .&. (bitMask !! fromIntegral (7 .&. i))+        /= 0++-- | Checks if any of the outputs of a tx is in the current bloom filter.+-- If it is, add the txid and vout as an outpoint (i.e. so that+-- a future tx that spends the output won't be missed).+bloomRelevantUpdate ::+  Ctx ->+  -- | Bloom filter+  BloomFilter ->+  -- | Tx that may (or may not) have relevant outputs+  Tx ->+  -- | Returns an updated bloom filter adding relevant output+  Maybe BloomFilter+bloomRelevantUpdate ctx b tx+  | isBloomFull b || isBloomEmpty b = Nothing+  | b.flags == BloomUpdateNone = Nothing+  | not (null matchOuts) = Just $ foldl' addRelevant b matchOuts+  | otherwise = Nothing+  where+    -- TxHash if we end up inserting an outpoint++    h = txHash tx+    -- Decode the scriptOutpus and add vOuts in case we make them outpoints+    decodedOutputScripts = traverse (unmarshal ctx . (.script)) tx.outputs+    err = error "Error Decoding output script"+    idxOutputScripts = either (const err) (zip [0 ..]) decodedOutputScripts+    -- Check if any txOuts were contained in the bloom filter+    matchFilter =+      filter (\(_, op) -> any (bloomContains b) (encodeScriptOut op))+    matchOuts = matchFilter idxOutputScripts+    addRelevant :: BloomFilter -> (Word32, ScriptOutput) -> BloomFilter+    addRelevant bf (id', scriptOut) =+      case (b.flags, scriptType) of+        -- We filtered out BloomUpdateNone so we insert any PayPk or PayMulSig++        (_, True) -> bloomInsert bf outpoint+        (BloomUpdateAll, _) -> bloomInsert bf outpoint+        _ -> error "Error Updating Bloom Filter with relevant outpoint"+      where+        outpoint = runPutS $ serialize $ OutPoint {hash = h, index = id'}+        scriptType = (\s -> isPayPK s || isPayMulSig s) scriptOut+    -- Encodes a scriptOutput so it can be checked agains the Bloom Filter+    encodeScriptOut :: ScriptOutput -> [ByteString]+    encodeScriptOut (PayPK pk) =+      return $ marshal ctx pk+    encodeScriptOut (PayPKHash ph) =+      return . runPutS $ serialize ph+    encodeScriptOut (PayMulSig outputMuSig _) =+      map (marshal ctx) outputMuSig+    encodeScriptOut (PayScriptHash sh) =+      return . runPutS $ serialize sh+    encodeScriptOut (PayWitnessPKHash ph) =+      return . runPutS $ serialize ph+    encodeScriptOut (PayWitnessScriptHash sh) =+      return . runPutS $ serialize sh+    encodeScriptOut (PayWitness _ wd) =+      return wd+    encodeScriptOut (DataCarrier dat) =+      return dat++-- | Returns True if the filter is empty (all bytes set to 0x00)+isBloomEmpty :: BloomFilter -> Bool+isBloomEmpty b = all (== 0x00) $ F.toList b.array++-- | Returns True if the filter is full (all bytes set to 0xff)+isBloomFull :: BloomFilter -> Bool+isBloomFull b = all (== 0xff) $ F.toList b.array++-- | Tests if a given bloom filter is valid.+isBloomValid ::+  -- | Bloom filter to test+  BloomFilter ->+  -- | True if the given filter is valid+  Bool+isBloomValid BloomFilter {..} =+  S.length array <= maxBloomSize && functions <= maxHashFuncs++-- | Does the peer with these version services accept bloom filters?+acceptsFilters :: Word64 -> Bool+acceptsFilters srv = srv .&. (1 `shiftL` 2) /= 0
+ src/Haskoin/Network/Common.hs view
@@ -0,0 +1,779 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Network.Common+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Common functions and data types related to peer-to-peer network.+module Haskoin.Network.Common+  ( -- * Network Data Types+    Addr (..),+    NetworkAddressTime,+    Alert (..),+    GetData (..),+    Inv (..),+    InvVector (..),+    InvType (..),+    HostAddress,+    hostToSockAddr,+    sockToHostAddress,+    NetworkAddress (..),+    NotFound (..),+    Ping (..),+    Pong (..),+    Reject (..),+    RejectCode (..),+    VarInt (..),+    VarString (..),+    Version (..),+    MessageCommand (..),+    reject,+    nodeNone,+    nodeNetwork,+    nodeGetUTXO,+    nodeBloom,+    nodeWitness,+    nodeXThin,+    commandToString,+    stringToCommand,+    putVarInt,+  )+where++import Control.DeepSeq+import Control.Monad (forM_, liftM2, replicateM, unless)+import Data.Binary (Binary (..))+import Data.Bits (shiftL)+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.ByteString.Char8 as C (replicate)+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Serialize (Serialize (..))+import Data.String+import Data.String.Conversions (cs)+import Data.Word (Word32, Word64)+import GHC.Generics (Generic)+import Haskoin.Crypto.Hash+import Network.Socket (SockAddr (..))+import Text.Read as R++-- | Network address with a timestamp.+type NetworkAddressTime = (Word32, NetworkAddress)++-- | Provides information about known nodes in the bitcoin network. An 'Addr'+-- type is sent inside a 'Message' as a response to a 'GetAddr' message.+newtype Addr = Addr+  { -- List of addresses of other nodes on the network with timestamps.+    list :: [NetworkAddressTime]+  }+  deriving (Eq, Show, Generic)+  deriving newtype (NFData)++instance Serial Addr where+  deserialize = Addr <$> (repList =<< deserialize)+    where+      repList (VarInt c) = replicateM (fromIntegral c) action+      action = liftM2 (,) getWord32le deserialize++  serialize (Addr xs) = do+    putVarInt $ length xs+    forM_ xs $ \(a, b) -> putWord32le a >> serialize b++instance Binary Addr where+  get = deserialize+  put = serialize++instance Serialize Addr where+  get = deserialize+  put = serialize++-- | Data type describing signed messages that can be sent between bitcoin+-- nodes to display important notifications to end users about the health of+-- the network.+data Alert = Alert+  { -- | Alert payload.+    payload :: !VarString,+    -- | ECDSA signature of the payload+    signature :: !VarString+  }+  deriving (Eq, Show, Read, Generic, NFData)++instance Serial Alert where+  deserialize = Alert <$> deserialize <*> deserialize+  serialize (Alert p s) = serialize p >> serialize s++instance Binary Alert where+  put = serialize+  get = deserialize++instance Serialize Alert where+  put = serialize+  get = deserialize++-- | The 'GetData' type is used to retrieve information on a specific object+-- ('Block' or 'Tx') identified by the objects hash. The payload of a 'GetData'+-- request is a list of 'InvVector' which represent all the hashes of objects+-- that a node wants. The response to a 'GetBlock' message will be either a+-- 'Block' or a 'Tx' message depending on the type of the object referenced by+-- the hash. Usually, 'GetData' messages are sent after a node receives an 'Inv'+-- message that contains unknown object hashes.+newtype GetData = GetData+  { -- | list of object hashes+    list :: [InvVector]+  }+  deriving (Eq, Show, Generic)+  deriving newtype (NFData)++instance Serial GetData where+  deserialize = GetData <$> (repList =<< deserialize)+    where+      repList (VarInt c) = replicateM (fromIntegral c) deserialize++  serialize (GetData xs) = do+    putVarInt $ length xs+    forM_ xs serialize++instance Binary GetData where+  get = deserialize+  put = serialize++instance Serialize GetData where+  get = deserialize+  put = serialize++-- | 'Inv' messages are used by nodes to advertise their knowledge of new+-- objects by publishing a list of hashes to a peer. 'Inv' messages can be sent+-- unsolicited or in response to a 'GetBlocks' message.+newtype Inv = Inv+  { -- | inventory+    list :: [InvVector]+  }+  deriving (Eq, Show, Generic)+  deriving newtype (NFData)++instance Serial Inv where+  deserialize = Inv <$> (repList =<< deserialize)+    where+      repList (VarInt c) = replicateM (fromIntegral c) deserialize++  serialize (Inv xs) = do+    putVarInt $ length xs+    forM_ xs serialize++instance Binary Inv where+  get = deserialize+  put = serialize++instance Serialize Inv where+  get = deserialize+  put = serialize++-- | Data type identifying the type of an inventory vector. SegWit types are+-- only used in 'GetData' messages, not 'Inv'.+data InvType+  = -- | error+    InvError+  | -- | transaction+    InvTx+  | -- | block+    InvBlock+  | -- | filtered block+    InvMerkleBlock+  | -- | segwit transaction+    InvWitnessTx+  | -- | segwit block+    InvWitnessBlock+  | -- | segwit filtered block+    InvWitnessMerkleBlock+  | -- | unknown inv type+    InvType Word32+  deriving (Eq, Show, Read, Generic, NFData)++instance Serial InvType where+  deserialize = go =<< getWord32le+    where+      go x =+        case x of+          0 -> return InvError+          1 -> return InvTx+          2 -> return InvBlock+          3 -> return InvMerkleBlock+          _+            | x == 1 `shiftL` 30 + 1 -> return InvWitnessTx+            | x == 1 `shiftL` 30 + 2 -> return InvWitnessBlock+            | x == 1 `shiftL` 30 + 3 -> return InvWitnessMerkleBlock+            | otherwise -> return (InvType x)+  serialize x =+    putWord32le $+      case x of+        InvError -> 0+        InvTx -> 1+        InvBlock -> 2+        InvMerkleBlock -> 3+        InvWitnessTx -> 1 `shiftL` 30 + 1+        InvWitnessBlock -> 1 `shiftL` 30 + 2+        InvWitnessMerkleBlock -> 1 `shiftL` 30 + 3+        InvType w -> w++instance Binary InvType where+  get = deserialize+  put = serialize++instance Serialize InvType where+  get = deserialize+  put = serialize++-- | Invectory vectors represent hashes identifying objects such as a 'Block' or+-- a 'Tx'. They notify other peers about new data or data they have otherwise+-- requested.+data InvVector = InvVector+  { -- | type of object+    invType :: !InvType,+    -- | 256-bit hash of object+    invHash :: !Hash256+  }+  deriving (Eq, Show, Generic, NFData)++instance Serial InvVector where+  deserialize = InvVector <$> deserialize <*> deserialize+  serialize (InvVector t h) = serialize t >> serialize h++instance Binary InvVector where+  get = deserialize+  put = serialize++instance Serialize InvVector where+  get = deserialize+  put = serialize++newtype HostAddress+  = HostAddress ByteString+  deriving (Eq, Show, Ord, Generic)+  deriving newtype (NFData)++instance Serial HostAddress where+  serialize (HostAddress bs) = putByteString bs+  deserialize = HostAddress <$> getByteString 18++instance Binary HostAddress where+  get = deserialize+  put = serialize++instance Serialize HostAddress where+  get = deserialize+  put = serialize++-- | Data type describing a bitcoin network address. Addresses are stored in+-- IPv6 format. IPv4 addresses are mapped to IPv6 using IPv4 mapped IPv6+-- addresses: <http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses>.+data NetworkAddress = NetworkAddress+  { -- | bitmask of services available for this address+    services :: !Word64,+    -- | address and port information+    address :: !HostAddress+  }+  deriving (Eq, Show, Generic, NFData)++hostToSockAddr :: HostAddress -> SockAddr+hostToSockAddr (HostAddress bs) =+  case runGetS getSockAddr bs of+    Left e -> error e+    Right x -> x++sockToHostAddress :: SockAddr -> HostAddress+sockToHostAddress = HostAddress . runPutS . putSockAddr++putSockAddr :: (MonadPut m) => SockAddr -> m ()+putSockAddr (SockAddrInet6 p _ (a, b, c, d) _) = do+  putWord32be a+  putWord32be b+  putWord32be c+  putWord32be d+  putWord16be (fromIntegral p)+putSockAddr (SockAddrInet p a) = do+  putWord32be 0x00000000+  putWord32be 0x00000000+  putWord32be 0x0000ffff+  putWord32host a+  putWord16be (fromIntegral p)+putSockAddr _ = error "Invalid address type"++getSockAddr :: (MonadGet m) => m SockAddr+getSockAddr = do+  a <- getWord32be+  b <- getWord32be+  c <- getWord32be+  if a == 0x00000000 && b == 0x00000000 && c == 0x0000ffff+    then do+      d <- getWord32host+      p <- getWord16be+      return $ SockAddrInet (fromIntegral p) d+    else do+      d <- getWord32be+      p <- getWord16be+      return $ SockAddrInet6 (fromIntegral p) 0 (a, b, c, d) 0++instance Serial NetworkAddress where+  deserialize = NetworkAddress <$> getWord64le <*> deserialize+  serialize (NetworkAddress s a) = putWord64le s >> serialize a++instance Binary NetworkAddress where+  get = deserialize+  put = serialize++instance Serialize NetworkAddress where+  get = deserialize+  put = serialize++-- | A 'NotFound' message is returned as a response to a 'GetData' message+-- whe one of the requested objects could not be retrieved. This could happen,+-- for example, if a tranasaction was requested and was not available in the+-- memory pool of the receiving node.+newtype NotFound = NotFound+  { -- | Inventory vectors related to this request+    list :: [InvVector]+  }+  deriving (Eq, Show, Generic)+  deriving newtype (NFData)++instance Serial NotFound where+  deserialize = NotFound <$> (repList =<< deserialize)+    where+      repList (VarInt c) = replicateM (fromIntegral c) deserialize++  serialize (NotFound xs) = do+    putVarInt $ length xs+    forM_ xs serialize++instance Binary NotFound where+  get = deserialize+  put = serialize++instance Serialize NotFound where+  get = deserialize+  put = serialize++-- | A 'Ping' message is sent to bitcoin peers to check if a connection is still+-- open.+newtype Ping = Ping+  { -- | A random nonce used to identify the recipient of the ping+    -- request once a Pong response is received.+    nonce :: Word64+  }+  deriving (Eq, Show, Read, Generic)+  deriving newtype (NFData)++-- | A Pong message is sent as a response to a ping message.+newtype Pong = Pong+  { -- | nonce from corresponding 'Ping'+    nonce :: Word64+  }+  deriving (Eq, Show, Read, Generic)+  deriving newtype (NFData)++instance Serial Ping where+  deserialize = Ping <$> getWord64le+  serialize (Ping n) = putWord64le n++instance Serial Pong where+  deserialize = Pong <$> getWord64le+  serialize (Pong n) = putWord64le n++instance Binary Ping where+  get = deserialize+  put = serialize++instance Binary Pong where+  get = deserialize+  put = serialize++instance Serialize Ping where+  get = deserialize+  put = serialize++instance Serialize Pong where+  get = deserialize+  put = serialize++-- | The 'Reject' message is sent when messages are rejected by a peer.+data Reject = Reject+  { -- | type of message rejected+    message :: !MessageCommand,+    -- | rejection code+    code :: !RejectCode,+    -- | text reason for rejection+    reason :: !VarString,+    -- | extra data such as block or tx hash+    extra :: !ByteString+  }+  deriving (Eq, Show, Read, Generic, NFData)++-- | Rejection code associated to the 'Reject' message.+data RejectCode+  = RejectMalformed+  | RejectInvalid+  | RejectObsolete+  | RejectDuplicate+  | RejectNonStandard+  | RejectDust+  | RejectInsufficientFee+  | RejectCheckpoint+  deriving (Eq, Show, Read, Generic, NFData)++instance Serial RejectCode where+  deserialize =+    getWord8 >>= \code -> case code of+      0x01 -> return RejectMalformed+      0x10 -> return RejectInvalid+      0x11 -> return RejectObsolete+      0x12 -> return RejectDuplicate+      0x40 -> return RejectNonStandard+      0x41 -> return RejectDust+      0x42 -> return RejectInsufficientFee+      0x43 -> return RejectCheckpoint+      _ ->+        fail $+          unwords+            [ "Reject get: Invalid code",+              show code+            ]++  serialize code = putWord8 $ case code of+    RejectMalformed -> 0x01+    RejectInvalid -> 0x10+    RejectObsolete -> 0x11+    RejectDuplicate -> 0x12+    RejectNonStandard -> 0x40+    RejectDust -> 0x41+    RejectInsufficientFee -> 0x42+    RejectCheckpoint -> 0x43++instance Binary RejectCode where+  put = serialize+  get = deserialize++instance Serialize RejectCode where+  put = serialize+  get = deserialize++-- | Convenience function to build a 'Reject' message.+reject :: MessageCommand -> RejectCode -> ByteString -> Reject+reject cmd code reason =+  Reject cmd code (VarString reason) B.empty++instance Serial Reject where+  deserialize =+    deserialize >>= \(VarString bs) ->+      Reject (stringToCommand bs)+        <$> deserialize+        <*> deserialize+        <*> maybeData+    where+      maybeData =+        isEmpty >>= \done ->+          if done+            then return B.empty+            else getByteString 32+  serialize (Reject cmd code reason dat) = do+    serialize $ VarString $ commandToString cmd+    serialize code+    serialize reason+    unless (B.null dat) $ putByteString dat++instance Binary Reject where+  put = serialize+  get = deserialize++instance Serialize Reject where+  put = serialize+  get = deserialize++-- | Data type representing a variable-length integer. The 'VarInt' type+-- usually precedes an array or a string that can vary in length.+newtype VarInt = VarInt {get :: Word64}+  deriving (Eq, Show, Read, Generic)+  deriving newtype (NFData)++instance Serial VarInt where+  deserialize = VarInt <$> (getWord8 >>= go)+    where+      go 0xff = getWord64le+      go 0xfe = fromIntegral <$> getWord32le+      go 0xfd = fromIntegral <$> getWord16le+      go x = return $ fromIntegral x++  serialize (VarInt x)+    | x < 0xfd =+        putWord8 $ fromIntegral x+    | x <= 0xffff = do+        putWord8 0xfd+        putWord16le $ fromIntegral x+    | x <= 0xffffffff = do+        putWord8 0xfe+        putWord32le $ fromIntegral x+    | otherwise = do+        putWord8 0xff+        putWord64le x++instance Binary VarInt where+  put = serialize+  get = deserialize++instance Serialize VarInt where+  put = serialize+  get = deserialize++putVarInt :: (MonadPut m, Integral a) => a -> m ()+putVarInt = serialize . VarInt . fromIntegral++-- | Data type for serialization of variable-length strings.+newtype VarString = VarString {get :: ByteString}+  deriving (Eq, Show, Read, Generic)+  deriving newtype (NFData)++instance Serial VarString where+  deserialize = VarString <$> (readBS =<< deserialize)+    where+      readBS (VarInt len) = getByteString (fromIntegral len)++  serialize (VarString bs) = do+    putVarInt $ B.length bs+    putByteString bs++instance Binary VarString where+  put = serialize+  get = deserialize++instance Serialize VarString where+  put = serialize+  get = deserialize++-- | When a bitcoin node creates an outgoing connection to another node,+-- the first message it will send is a 'Version' message. The other node+-- will similarly respond with it's own 'Version' message.+data Version = Version+  { -- | protocol version+    version :: !Word32,+    -- | features supported by this connection+    services :: !Word64,+    -- | unix timestamp+    timestamp :: !Word64,+    -- | network address of remote node+    addrRecv :: !NetworkAddress,+    -- | network address of sending node+    addrSend :: !NetworkAddress,+    -- | random nonce to detect connection to self+    nonce :: !Word64,+    -- | user agent string+    userAgent :: !VarString,+    -- | height of the last block in sending node+    startHeight :: !Word32,+    -- | relay transactions flag (BIP-37)+    relay :: !Bool+  }+  deriving (Eq, Show, Generic, NFData)++instance Serial Version where+  deserialize =+    Version+      <$> getWord32le+      <*> getWord64le+      <*> getWord64le+      <*> deserialize+      <*> deserialize+      <*> getWord64le+      <*> deserialize+      <*> getWord32le+      <*> (go =<< isEmpty)+    where+      go True = return True+      go False = getBool++  serialize (Version v s t ar as n ua sh r) = do+    putWord32le v+    putWord64le s+    putWord64le t+    serialize ar+    serialize as+    putWord64le n+    serialize ua+    putWord32le sh+    putBool r++instance Binary Version where+  put = serialize+  get = deserialize++instance Serialize Version where+  put = serialize+  get = deserialize++-- | 0x00 is 'False', anything else is 'True'.+getBool :: (MonadGet m) => m Bool+getBool = go =<< getWord8+  where+    go 0 = return False+    go _ = return True++putBool :: (MonadPut m) => Bool -> m ()+putBool True = putWord8 1+putBool False = putWord8 0++-- | A 'MessageCommand' is included in a 'MessageHeader' in order to identify+-- the type of message present in the payload. This allows the message+-- de-serialization code to know how to decode a particular message payload.+-- Every valid 'Message' constructor has a corresponding 'MessageCommand'+-- constructor.+data MessageCommand+  = MCVersion+  | MCVerAck+  | MCAddr+  | MCInv+  | MCGetData+  | MCNotFound+  | MCGetBlocks+  | MCGetHeaders+  | MCTx+  | MCBlock+  | MCMerkleBlock+  | MCHeaders+  | MCGetAddr+  | MCFilterLoad+  | MCFilterAdd+  | MCFilterClear+  | MCPing+  | MCPong+  | MCAlert+  | MCMempool+  | MCReject+  | MCSendHeaders+  | MCOther ByteString+  deriving (Eq, Generic, NFData)++instance Show MessageCommand where+  showsPrec _ = shows . commandToString++instance Read MessageCommand where+  readPrec = do+    String str <- lexP+    return (stringToCommand (cs str))++instance Serial MessageCommand where+  deserialize = go <$> getByteString 12+    where+      go bs =+        let str = unpackCommand bs+         in stringToCommand str+  serialize mc = putByteString $ packCommand $ commandToString mc++instance Binary MessageCommand where+  put = serialize+  get = deserialize++instance Serialize MessageCommand where+  put = serialize+  get = deserialize++instance IsString MessageCommand where+  fromString str = stringToCommand (cs str)++-- | Read a 'MessageCommand' from its string representation.+stringToCommand :: ByteString -> MessageCommand+stringToCommand str = case str of+  "version" -> MCVersion+  "verack" -> MCVerAck+  "addr" -> MCAddr+  "inv" -> MCInv+  "getdata" -> MCGetData+  "notfound" -> MCNotFound+  "getblocks" -> MCGetBlocks+  "getheaders" -> MCGetHeaders+  "tx" -> MCTx+  "block" -> MCBlock+  "merkleblock" -> MCMerkleBlock+  "headers" -> MCHeaders+  "getaddr" -> MCGetAddr+  "filterload" -> MCFilterLoad+  "filteradd" -> MCFilterAdd+  "filterclear" -> MCFilterClear+  "ping" -> MCPing+  "pong" -> MCPong+  "alert" -> MCAlert+  "mempool" -> MCMempool+  "reject" -> MCReject+  "sendheaders" -> MCSendHeaders+  _ -> MCOther str++-- | Convert a 'MessageCommand' to its string representation.+commandToString :: MessageCommand -> ByteString+commandToString mc = case mc of+  MCVersion -> "version"+  MCVerAck -> "verack"+  MCAddr -> "addr"+  MCInv -> "inv"+  MCGetData -> "getdata"+  MCNotFound -> "notfound"+  MCGetBlocks -> "getblocks"+  MCGetHeaders -> "getheaders"+  MCTx -> "tx"+  MCBlock -> "block"+  MCMerkleBlock -> "merkleblock"+  MCHeaders -> "headers"+  MCGetAddr -> "getaddr"+  MCFilterLoad -> "filterload"+  MCFilterAdd -> "filteradd"+  MCFilterClear -> "filterclear"+  MCPing -> "ping"+  MCPong -> "pong"+  MCAlert -> "alert"+  MCMempool -> "mempool"+  MCReject -> "reject"+  MCSendHeaders -> "sendheaders"+  MCOther c -> c++-- | Pack a string 'MessageCommand' so that it is exactly 12-bytes long.+packCommand :: ByteString -> ByteString+packCommand s =+  B.take 12 $+    s `mappend` C.replicate 12 '\NUL'++-- | Undo packing done by 'packCommand'.+unpackCommand :: ByteString -> ByteString+unpackCommand = B.takeWhile (/= 0)++-- | Node offers no services.+nodeNone :: Word64+nodeNone = 0++-- | Services indicate node is a full node that can serve full blocks.+nodeNetwork :: Word64+nodeNetwork = 1++-- | Services indicate node allows to request 'UTXO' set.+nodeGetUTXO :: Word64+nodeGetUTXO = 1 `shiftL` 1++-- | Services indicate node accepts bloom filters.+nodeBloom :: Word64+nodeBloom = 1 `shiftL` 2++-- | Services indicate SegWit-capable node.+nodeWitness :: Word64+nodeWitness = 1 `shiftL` 3++-- | Services indicate Xtreme Thinblocks compatibility.+nodeXThin :: Word64+nodeXThin = 1 `shiftL` 4
+ src/Haskoin/Network/Constants.hs view
@@ -0,0 +1,582 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Constants+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Network constants for various networks, including Bitcoin SegWit (BTC), Bitcoin+-- Cash (BCH), and corresponding public test and private regression test networks.+module Haskoin.Network.Constants+  ( -- * Constants+    btc,+    btcTest,+    btcRegTest,+    bch,+    bchTest,+    bchTest4,+    bchRegTest,+    allNets,+    netByName,+  )+where++import Control.DeepSeq+import Data.Binary (Binary (..))+import Data.ByteString (ByteString)+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.List+import Data.Maybe+import Data.Serialize (Serialize (..))+import Data.String+import Data.Text (Text)+import Data.Word (Word32, Word64, Word8)+import GHC.Generics (Generic)+import Haskoin.Block+import Haskoin.Network.Common+import Haskoin.Network.Data+import Haskoin.Transaction+import Text.Read++-- | Version of Haskoin Core package.+versionString :: (IsString a) => a++#ifdef CURRENT_PACKAGE_VERSION+versionString = CURRENT_PACKAGE_VERSION+#else+versionString = "Unavailable"+#endif++-- | Query known networks by name.+netByName :: String -> Maybe Network+netByName str = find ((== str) . (.name)) allNets++-- | Bitcoin SegWit network. Symbol: BTC.+btc :: Network+btc =+  Network+    { name = "btc",+      addrPrefix = 0,+      scriptPrefix = 5,+      secretPrefix = 128,+      xPubPrefix = 0x0488b21e,+      xPrvPrefix = 0x0488ade4,+      magic = 0xf9beb4d9,+      genesisHeader =+        BlockHeader+          0x01+          "0000000000000000000000000000000000000000000000000000000000000000"+          "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"+          1231006505+          0x1d00ffff+          2083236893,+      -- Hash 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f+      maxBlockSize = 1000000,+      maxSatoshi = 2100000000000000,+      userAgent =+        "/haskoin-btc:" <> versionString <> "/",+      defaultPort = 8333,+      minDiffBlocks = False,+      powNoRetarget = False,+      powLimit =+        0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff,+      bip34Block =+        ( 227931,+          "000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"+        ),+      bip65Height = 388381,+      bip66Height = 363725,+      targetTimespan = 14 * 24 * 60 * 60,+      targetSpacing = 10 * 60,+      checkpoints =+        [ ( 11111,+            "0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d"+          ),+          ( 33333,+            "000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6"+          ),+          ( 74000,+            "0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20"+          ),+          ( 105000,+            "00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97"+          ),+          ( 134444,+            "00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe"+          ),+          ( 168000,+            "000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763"+          ),+          ( 193000,+            "000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317"+          ),+          ( 210000,+            "000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e"+          ),+          ( 216116,+            "00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e"+          ),+          ( 225430,+            "00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932"+          ),+          ( 250000,+            "000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214"+          ),+          ( 279000,+            "0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40"+          ),+          ( 295000,+            "00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983"+          )+        ],+      seeds =+        [ "seed.bitcoin.sipa.be", -- Pieter Wuille+          "dnsseed.bluematt.me", -- Matt Corallo+          "dnsseed.bitcoin.dashjr.org", -- Luke Dashjr+          "seed.bitcoinstats.com", -- Chris Decker+          "seed.bitcoin.jonasschnelli.ch", -- Jonas Schnelli+          "seed.btc.petertodd.org", -- Peter Todd+          "seed.bitcoin.sprovoost.nl", -- Sjors Provoost+          "dnsseed.emzy.de", -- Stephan Oeste+          "seed.bitcoin.wiz.biz" -- Jason Maurice+        ],+      bip44Coin = 0,+      sigHashForkId = Nothing,+      edaHeight = Nothing,+      daaHeight = Nothing,+      asertActivationTime = Nothing,+      asertHalfLife = 0,+      segWit = True,+      cashAddrPrefix = Nothing,+      bech32Prefix = Just "bc",+      replaceByFee = True,+      halvingInterval = 210000+    }++-- | Testnet for Bitcoin SegWit network.+btcTest :: Network+btcTest =+  Network+    { name = "btctest",+      addrPrefix = 111,+      scriptPrefix = 196,+      secretPrefix = 239,+      xPubPrefix = 0x043587cf,+      xPrvPrefix = 0x04358394,+      magic = 0x0b110907,+      genesisHeader =+        BlockHeader+          0x01+          "0000000000000000000000000000000000000000000000000000000000000000"+          "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"+          1296688602+          486604799+          414098458,+      -- Hash 000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943+      maxBlockSize = 1000000,+      maxSatoshi = 2100000000000000,+      userAgent = "/haskoin-btc-test:" <> versionString <> "/",+      defaultPort = 18333,+      minDiffBlocks = True,+      powNoRetarget = False,+      powLimit =+        0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff,+      bip34Block =+        ( 21111,+          "0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8"+        ),+      bip65Height = 581885,+      bip66Height = 330776,+      targetTimespan = 14 * 24 * 60 * 60,+      targetSpacing = 10 * 60,+      checkpoints =+        [ ( 546,+            "000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70"+          )+        ],+      seeds =+        [ "testnet-seed.bitcoin.jonasschnelli.ch",+          "seed.tbtc.petertodd.org",+          "seed.testnet.bitcoin.sprovoost.nl",+          "testnet-seed.bluematt.me"+        ],+      bip44Coin = 1,+      sigHashForkId = Nothing,+      edaHeight = Nothing,+      daaHeight = Nothing,+      asertActivationTime = Nothing,+      asertHalfLife = 0,+      segWit = True,+      cashAddrPrefix = Nothing,+      bech32Prefix = Just "tb",+      replaceByFee = True,+      halvingInterval = 210000+    }++-- | RegTest for Bitcoin SegWit network.+btcRegTest :: Network+btcRegTest =+  Network+    { name = "btcreg",+      addrPrefix = 111,+      scriptPrefix = 196,+      secretPrefix = 239,+      xPubPrefix = 0x043587cf,+      xPrvPrefix = 0x04358394,+      magic = 0xfabfb5da,+      genesisHeader =+        BlockHeader+          -- 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206+          0x01+          "0000000000000000000000000000000000000000000000000000000000000000"+          "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"+          1296688602+          0x207fffff+          2,+      maxBlockSize = 1000000,+      maxSatoshi = 2100000000000000,+      userAgent = "/haskoin-btc-regtest:" <> versionString <> "/",+      defaultPort = 18444,+      minDiffBlocks = True,+      powNoRetarget = True,+      powLimit =+        0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,+      bip34Block =+        ( 100000000,+          "0000000000000000000000000000000000000000000000000000000000000000"+        ),+      bip65Height = 1351,+      bip66Height = 1251,+      targetTimespan = 14 * 24 * 60 * 60,+      targetSpacing = 10 * 60,+      checkpoints = [],+      seeds = ["localhost"],+      bip44Coin = 1,+      sigHashForkId = Nothing,+      edaHeight = Nothing,+      daaHeight = Nothing,+      asertActivationTime = Nothing,+      asertHalfLife = 0,+      segWit = True,+      cashAddrPrefix = Nothing,+      bech32Prefix = Just "bcrt",+      replaceByFee = True,+      halvingInterval = 150+    }++-- | Bitcoin Cash network. Symbol: BCH.+bch :: Network+bch =+  Network+    { name = "bch",+      addrPrefix = 0,+      scriptPrefix = 5,+      secretPrefix = 128,+      xPubPrefix = 0x0488b21e,+      xPrvPrefix = 0x0488ade4,+      magic = 0xe3e1f3e8,+      genesisHeader =+        BlockHeader+          0x01+          "0000000000000000000000000000000000000000000000000000000000000000"+          "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"+          1231006505+          0x1d00ffff+          2083236893,+      -- Hash 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f+      maxBlockSize = 32000000,+      maxSatoshi = 2100000000000000,+      userAgent = "/haskoin-bch:" <> versionString <> "/",+      defaultPort = 8333,+      minDiffBlocks = False,+      powNoRetarget = False,+      powLimit =+        0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff,+      bip34Block =+        ( 227931,+          "000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"+        ),+      bip65Height = 388381,+      bip66Height = 363725,+      targetTimespan = 14 * 24 * 60 * 60,+      targetSpacing = 10 * 60,+      checkpoints =+        [ ( 11111,+            "0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d"+          ),+          ( 33333,+            "000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6"+          ),+          ( 74000,+            "0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20"+          ),+          ( 105000,+            "00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97"+          ),+          ( 134444,+            "00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe"+          ),+          ( 168000,+            "000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763"+          ),+          ( 193000,+            "000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317"+          ),+          ( 210000,+            "000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e"+          ),+          ( 216116,+            "00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e"+          ),+          ( 225430,+            "00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932"+          ),+          ( 250000,+            "000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214"+          ),+          ( 279000,+            "0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40"+          ),+          ( 295000,+            "00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983"+          ),+          -- UAHF fork block.++          ( 478559,+            "000000000000000000651ef99cb9fcbe0dadde1d424bd9f15ff20136191a5eec"+          ),+          -- Nov, 13 DAA activation block.++          ( 504031,+            "0000000000000000011ebf65b60d0a3de80b8175be709d653b4c1a1beeb6ab9c"+          )+        ],+      seeds =+        [ "seed.bitcoinabc.org",+          "seed-bch.bitcoinforks.org",+          "btccash-seeder.bitcoinunlimited.info",+          "seed.bchd.cash",+          "seed.bch.loping.net",+          "dnsseed.electroncash.de"+        ],+      bip44Coin = 145,+      sigHashForkId = Just 0,+      edaHeight = Just 478559,+      daaHeight = Just 404031,+      asertActivationTime = Just 1605441600,+      asertHalfLife = 60 * 60 * 10,+      segWit = False,+      cashAddrPrefix = Just "bitcoincash",+      bech32Prefix = Nothing,+      replaceByFee = False,+      halvingInterval = 210000+    }++-- | Testnet for Bitcoin Cash network.+bchTest4 :: Network+bchTest4 =+  Network+    { name = "bchtest4",+      addrPrefix = 111,+      scriptPrefix = 196,+      secretPrefix = 239,+      xPubPrefix = 0x043587cf,+      xPrvPrefix = 0x04358394,+      magic = 0xe2b7daaf,+      genesisHeader =+        BlockHeader+          0x01+          "0000000000000000000000000000000000000000000000000000000000000000"+          "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"+          1597811185+          0x1d00ffff+          114152193,+      -- Hash 000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943+      maxBlockSize = 2000000,+      maxSatoshi = 2100000000000000,+      userAgent = "/haskoin-bch-test4:" <> versionString <> "/",+      defaultPort = 28333,+      minDiffBlocks = True,+      powNoRetarget = False,+      powLimit =+        0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff,+      bip34Block =+        ( 2,+          "00000000b0c65b1e03baace7d5c093db0d6aac224df01484985ffd5e86a1a20c"+        ),+      bip65Height = 3,+      bip66Height = 4,+      targetTimespan = 14 * 24 * 60 * 60,+      targetSpacing = 10 * 60,+      checkpoints =+        [ ( 5000,+            "000000009f092d074574a216faec682040a853c4f079c33dfd2c3ef1fd8108c4"+          ),+          -- Axion activation++          ( 16845,+            "00000000fb325b8f34fe80c96a5f708a08699a68bbab82dba4474d86bd743077"+          ),+          ( 38000,+            "000000000015197537e59f339e3b1bbf81a66f691bd3d7aa08560fc7bf5113fb"+          ),+          ( 54700,+            "00000000009af4379d87f17d0f172ee4769b48839a5a3a3e81d69da4322518b8"+          )+        ],+      seeds =+        [ "testnet4-seed-bch.bitcoinforks.org",+          "testnet4-seed-bch.toom.im",+          "seed.tbch4.loping.net",+          "testnet4-seed.flowee.cash"+        ],+      bip44Coin = 1,+      sigHashForkId = Just 0,+      edaHeight = Just 7,+      daaHeight = Just 3000,+      asertActivationTime = Just 1605441600,+      asertHalfLife = 60 * 60,+      segWit = False,+      cashAddrPrefix = Just "bchtest",+      bech32Prefix = Nothing,+      replaceByFee = False,+      halvingInterval = 210000+    }++-- | Testnet for Bitcoin Cash network.+bchTest :: Network+bchTest =+  Network+    { name = "bchtest",+      addrPrefix = 111,+      scriptPrefix = 196,+      secretPrefix = 239,+      xPubPrefix = 0x043587cf,+      xPrvPrefix = 0x04358394,+      magic = 0xf4e5f3f4,+      genesisHeader =+        BlockHeader+          0x01+          "0000000000000000000000000000000000000000000000000000000000000000"+          "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"+          1296688602+          486604799+          414098458,+      -- Hash 000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943+      maxBlockSize = 32000000,+      maxSatoshi = 2100000000000000,+      userAgent = "/haskoin-bch-test:" <> versionString <> "/",+      defaultPort = 18333,+      minDiffBlocks = True,+      powNoRetarget = False,+      powLimit =+        0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff,+      bip34Block =+        ( 21111,+          "0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8"+        ),+      bip65Height = 581885,+      bip66Height = 330776,+      targetTimespan = 14 * 24 * 60 * 60,+      targetSpacing = 10 * 60,+      checkpoints =+        [ ( 546,+            "000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70"+          ),+          -- UAHF fork block.++          ( 1155876,+            "00000000000e38fef93ed9582a7df43815d5c2ba9fd37ef70c9a0ea4a285b8f5"+          ),+          -- Nov, 13. DAA activation block.++          ( 1188697,+            "0000000000170ed0918077bde7b4d36cc4c91be69fa09211f748240dabe047fb"+          )+        ],+      seeds =+        [ "testnet-seed.bitcoinabc.org",+          "testnet-seed-bch.bitcoinforks.org",+          "testnet-seed.bchd.cash",+          "seed.tbch.loping.net"+        ],+      bip44Coin = 1,+      sigHashForkId = Just 0,+      edaHeight = Just 1155876,+      daaHeight = Just 1188697,+      asertActivationTime = Just 1605441600,+      asertHalfLife = 60 * 60,+      segWit = False,+      cashAddrPrefix = Just "bchtest",+      bech32Prefix = Nothing,+      replaceByFee = False,+      halvingInterval = 210000+    }++-- | RegTest for Bitcoin Cash network.+bchRegTest :: Network+bchRegTest =+  Network+    { name = "bchreg",+      addrPrefix = 111,+      scriptPrefix = 196,+      secretPrefix = 239,+      xPubPrefix = 0x043587cf,+      xPrvPrefix = 0x04358394,+      magic = 0xdab5bffa,+      genesisHeader =+        BlockHeader+          -- 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206+          0x01+          "0000000000000000000000000000000000000000000000000000000000000000"+          "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"+          1296688602+          0x207fffff+          2,+      maxBlockSize = 1000000,+      maxSatoshi = 2100000000000000,+      userAgent = "/haskoin-bch-regtest:" <> versionString <> "/",+      defaultPort = 18444,+      minDiffBlocks = True,+      powNoRetarget = True,+      powLimit =+        0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,+      bip34Block =+        ( 100000000,+          "0000000000000000000000000000000000000000000000000000000000000000"+        ),+      bip65Height = 1351,+      bip66Height = 1251,+      targetTimespan = 14 * 24 * 60 * 60,+      targetSpacing = 10 * 60,+      checkpoints =+        [ ( 0,+            "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"+          )+        ],+      seeds = ["localhost"],+      bip44Coin = 1,+      sigHashForkId = Just 0,+      edaHeight = Nothing,+      daaHeight = Just 0,+      asertActivationTime = Just 1605441600,+      asertHalfLife = 2 * 24 * 60 * 60,+      segWit = False,+      cashAddrPrefix = Just "bchreg",+      bech32Prefix = Nothing,+      replaceByFee = False,+      halvingInterval = 150+    }++-- | List of all networks supported by this library.+allNets :: [Network]+allNets = [btc, bch, btcTest, bchTest4, bchTest, btcRegTest, bchRegTest]
+ src/Haskoin/Network/Data.hs view
@@ -0,0 +1,89 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE NoFieldSelectors #-}++module Haskoin.Network.Data+  ( Network (..),+  )+where++import Control.DeepSeq+import Data.Binary (Binary (..))+import Data.ByteString (ByteString)+import Data.Serialize (Serialize (..))+import Data.Text (Text)+import Data.Word (Word32, Word64, Word8)+import GHC.Generics (Generic)+import Haskoin.Block.Common++-- | Network definition.+data Network = Network+  { -- | lowercase alphanumeric and dashes+    name :: !String,+    -- | prefix for 'Base58' P2PKH addresses+    addrPrefix :: !Word8,+    -- | prefix for 'Base58' P2SH addresses+    scriptPrefix :: !Word8,+    -- | prefix for WIF private key+    secretPrefix :: !Word8,+    -- | prefix for extended public key+    xPubPrefix :: !Word32,+    -- | prefix for extended private key+    xPrvPrefix :: !Word32,+    -- | network magic+    magic :: !Word32,+    -- | genesis block header+    genesisHeader :: !BlockHeader,+    -- | maximum block size in bytes+    maxBlockSize :: !Int,+    -- | maximum amount of satoshi+    maxSatoshi :: !Word64,+    -- | user agent string+    userAgent :: !ByteString,+    -- | default port for P2P connections+    defaultPort :: !Int,+    -- | allow min difficulty blocks (testnet)+    minDiffBlocks :: !Bool,+    -- | do not retarget difficulty (regtest)+    powNoRetarget :: !Bool,+    -- | proof-of-work target higest possible value+    powLimit :: !Integer,+    -- | block at which BIP34 activates+    bip34Block :: !(BlockHeight, BlockHash),+    -- | block at which BIP65 activates+    bip65Height :: !BlockHeight,+    -- | block at which BIP66 activates+    bip66Height :: !BlockHeight,+    -- | time between difficulty retargets+    targetTimespan :: !Word32,+    -- | time between blocks+    targetSpacing :: !Word32,+    -- | checkpoints+    checkpoints :: ![(BlockHeight, BlockHash)],+    -- | BIP44 derivation path root+    bip44Coin :: !Word32,+    -- | peer-to-peer network seeds+    seeds :: ![String],+    -- | fork id for replay protection+    sigHashForkId :: !(Maybe Word32),+    -- | EDA start block height+    edaHeight :: !(Maybe Word32),+    -- | DAA start block height+    daaHeight :: !(Maybe Word32),+    -- | asert3-2d algorithm activation time+    -- TODO: Replace with block height after fork+    asertActivationTime :: !(Maybe Word32),+    -- | asert3-2d algorithm halflife (not used for non-BCH networks)+    asertHalfLife :: !Integer,+    -- | segregated witness active+    segWit :: !Bool,+    -- | 'CashAddr' prefix (for Bitcoin Cash)+    cashAddrPrefix :: !(Maybe Text),+    -- | 'Bech32' prefix (for SegWit network)+    bech32Prefix :: !(Maybe Text),+    -- | Replace-By-Fee (BIP-125)+    replaceByFee :: !Bool,+    -- | Subsidy halving interval+    halvingInterval :: !Word32+  }+  deriving (Eq, Show, Read, Generic, NFData)
+ src/Haskoin/Network/Message.hs view
@@ -0,0 +1,224 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Network.Message+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Peer-to-peer network message serialization.+module Haskoin.Network.Message+  ( -- * Network Message+    Message (..),+    MessageHeader (..),+    msgType,+    putMessage,+    getMessage,+  )+where++import Control.DeepSeq+import Control.Monad (unless)+import Data.Binary (Binary (..))+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Serialize (Serialize (..))+import Data.Word (Word32)+import GHC.Generics (Generic)+import Haskoin.Block.Common+import Haskoin.Block.Merkle+import Haskoin.Crypto.Hash+import Haskoin.Network.Bloom+import Haskoin.Network.Common+import Haskoin.Network.Data+import Haskoin.Transaction.Common++-- | Data type representing the header of a 'Message'. All messages sent between+-- nodes contain a message header.+data MessageHeader = MessageHeader+  { -- | magic bytes identify network+    magic :: !Word32,+    -- | message type+    cmd :: !MessageCommand,+    -- | length of payload+    size :: !Word32,+    -- | checksum of payload+    checksum :: !CheckSum32+  }+  deriving (Eq, Show, Generic, NFData)++instance Serial MessageHeader where+  deserialize =+    MessageHeader+      <$> getWord32be+      <*> deserialize+      <*> getWord32le+      <*> deserialize++  serialize (MessageHeader m c l chk) = do+    putWord32be m+    serialize c+    putWord32le l+    serialize chk++instance Binary MessageHeader where+  put = serialize+  get = deserialize++instance Serialize MessageHeader where+  put = serialize+  get = deserialize++-- | The 'Message' type is used to identify all the valid messages that can be+-- sent between bitcoin peers. Only values of type 'Message' will be accepted+-- by other bitcoin peers as bitcoin protocol messages need to be correctly+-- serialized with message headers. Serializing a 'Message' value will+-- include the 'MessageHeader' with the correct checksum value automatically.+-- No need to add the 'MessageHeader' separately.+data Message+  = MVersion !Version+  | MVerAck+  | MAddr !Addr+  | MInv !Inv+  | MGetData !GetData+  | MNotFound !NotFound+  | MGetBlocks !GetBlocks+  | MGetHeaders !GetHeaders+  | MTx !Tx+  | MBlock !Block+  | MMerkleBlock !MerkleBlock+  | MHeaders !Headers+  | MGetAddr+  | MFilterLoad !FilterLoad+  | MFilterAdd !FilterAdd+  | MFilterClear+  | MPing !Ping+  | MPong !Pong+  | MAlert !Alert+  | MMempool+  | MReject !Reject+  | MSendHeaders+  | MOther !ByteString !ByteString+  deriving (Eq, Show, Generic, NFData)++-- | Get 'MessageCommand' assocated with a message.+msgType :: Message -> MessageCommand+msgType (MVersion _) = MCVersion+msgType MVerAck = MCVerAck+msgType (MAddr _) = MCAddr+msgType (MInv _) = MCInv+msgType (MGetData _) = MCGetData+msgType (MNotFound _) = MCNotFound+msgType (MGetBlocks _) = MCGetBlocks+msgType (MGetHeaders _) = MCGetHeaders+msgType (MTx _) = MCTx+msgType (MBlock _) = MCBlock+msgType (MMerkleBlock _) = MCMerkleBlock+msgType (MHeaders _) = MCHeaders+msgType (MFilterLoad _) = MCFilterLoad+msgType (MFilterAdd _) = MCFilterAdd+msgType MFilterClear = MCFilterClear+msgType (MPing _) = MCPing+msgType (MPong _) = MCPong+msgType (MAlert _) = MCAlert+msgType MMempool = MCMempool+msgType (MReject _) = MCReject+msgType MSendHeaders = MCSendHeaders+msgType MGetAddr = MCGetAddr+msgType (MOther c _) = MCOther c++-- | Deserializer for network messages.+getMessage :: (MonadGet m) => Network -> m Message+getMessage net = do+  (MessageHeader mgc cmd len chk) <- deserialize+  bs <- lookAhead $ getByteString $ fromIntegral len+  unless+    (mgc == net.magic)+    (fail $ "get: Invalid network magic bytes: " ++ show mgc)+  unless+    (checkSum32 bs == chk)+    (fail $ "get: Invalid message checksum: " ++ show chk)+  if len > 0+    then do+      bs <- ensure (fromIntegral len)+      let f = case cmd of+            MCVersion -> MVersion <$> deserialize+            MCAddr -> MAddr <$> deserialize+            MCInv -> MInv <$> deserialize+            MCGetData -> MGetData <$> deserialize+            MCNotFound -> MNotFound <$> deserialize+            MCGetBlocks -> MGetBlocks <$> deserialize+            MCGetHeaders -> MGetHeaders <$> deserialize+            MCTx -> MTx <$> deserialize+            MCBlock -> MBlock <$> deserialize+            MCMerkleBlock -> MMerkleBlock <$> deserialize+            MCHeaders -> MHeaders <$> deserialize+            MCFilterLoad -> MFilterLoad <$> deserialize+            MCFilterAdd -> MFilterAdd <$> deserialize+            MCPing -> MPing <$> deserialize+            MCPong -> MPong <$> deserialize+            MCAlert -> MAlert <$> deserialize+            MCReject -> MReject <$> deserialize+            MCOther c -> MOther c <$> getByteString (fromIntegral len)+            _ ->+              fail $+                "get: command "+                  ++ show cmd+                  ++ " should not carry a payload"+      either fail return (runGetS f bs)+    else case cmd of+      MCGetAddr -> return MGetAddr+      MCVerAck -> return MVerAck+      MCFilterClear -> return MFilterClear+      MCMempool -> return MMempool+      MCSendHeaders -> return MSendHeaders+      MCOther c -> return (MOther c B.empty)+      _ ->+        fail $+          "get: command "+            ++ show cmd+            ++ " is expected to carry a payload"++-- | Serializer for network messages.+putMessage :: (MonadPut m) => Network -> Message -> m ()+putMessage net msg = do+  let (cmd, payload) =+        case msg of+          MVersion m -> (MCVersion, runPutS $ serialize m)+          MVerAck -> (MCVerAck, B.empty)+          MAddr m -> (MCAddr, runPutS $ serialize m)+          MInv m -> (MCInv, runPutS $ serialize m)+          MGetData m -> (MCGetData, runPutS $ serialize m)+          MNotFound m -> (MCNotFound, runPutS $ serialize m)+          MGetBlocks m -> (MCGetBlocks, runPutS $ serialize m)+          MGetHeaders m -> (MCGetHeaders, runPutS $ serialize m)+          MTx m -> (MCTx, runPutS $ serialize m)+          MBlock m -> (MCBlock, runPutS $ serialize m)+          MMerkleBlock m -> (MCMerkleBlock, runPutS $ serialize m)+          MHeaders m -> (MCHeaders, runPutS $ serialize m)+          MGetAddr -> (MCGetAddr, B.empty)+          MFilterLoad m -> (MCFilterLoad, runPutS $ serialize m)+          MFilterAdd m -> (MCFilterAdd, runPutS $ serialize m)+          MFilterClear -> (MCFilterClear, B.empty)+          MPing m -> (MCPing, runPutS $ serialize m)+          MPong m -> (MCPong, runPutS $ serialize m)+          MAlert m -> (MCAlert, runPutS $ serialize m)+          MMempool -> (MCMempool, B.empty)+          MReject m -> (MCReject, runPutS $ serialize m)+          MSendHeaders -> (MCSendHeaders, B.empty)+          MOther c p -> (MCOther c, p)+      chk = checkSum32 payload+      len = fromIntegral $ B.length payload+      header = MessageHeader net.magic cmd len chk+  serialize header+  putByteString payload
+ src/Haskoin/Script.hs view
@@ -0,0 +1,21 @@+-- |+-- Module      : Haskoin.Script+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- This module provides functions for parsing and evaluating bitcoin+-- transaction scripts. Data types are provided for building and+-- deconstructing all of the standard input and output script types.+module Haskoin.Script+  ( module Common,+    module Standard,+    module SigHash,+  )+where++import Haskoin.Script.Common as Common+import Haskoin.Script.SigHash as SigHash+import Haskoin.Script.Standard as Standard
+ src/Haskoin/Script/Common.hs view
@@ -0,0 +1,598 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Script.Common+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Common script-related functions and data types.+module Haskoin.Script.Common+  ( -- * Scripts+    ScriptOp (..),+    Script (..),+    PushDataType (..),+    isPushOp,+    opPushData,+    intToScriptOp,+    scriptOpToInt,+  )+where++import Control.DeepSeq+import Control.Monad+import Data.Aeson+import Data.Aeson.Encoding+import Data.Binary (Binary (..))+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import Data.ByteString.Builder (char7)+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Either (fromRight)+import Data.Hashable+import Data.Serialize (Serialize (..))+import Data.Word (Word8)+import GHC.Generics (Generic)+import Haskoin.Util (hexBuilder)+import Haskoin.Util.Helpers (decodeHex, encodeHex, hexEncoding)++-- | Data type representing a transaction script. Scripts are defined as lists+-- of script operators 'ScriptOp'. Scripts are used to:+--+-- * Define the spending conditions in the output of a transaction.+-- * Provide signatures in the input of a transaction (except SegWit).+--+-- SigWit only: the segregated witness data structure, and not the input script,+-- contains signatures and redeem script for pay-to-witness-script and+-- pay-to-witness-public-key-hash transactions.+newtype Script = Script+  { -- | script operators defining this script+    ops :: [ScriptOp]+  }+  deriving (Eq, Show, Read, Generic)+  deriving newtype (Hashable, NFData)++instance FromJSON Script where+  parseJSON = withText "script" $ \t -> do+    bs <- maybe mzero return (decodeHex t)+    either fail return (runGetS deserialize bs)++instance ToJSON Script where+  toJSON = String . encodeHex . runPutS . serialize+  toEncoding = hexEncoding . runPutL . serialize++instance Serial Script where+  deserialize =+    Script <$> getScriptOps+    where+      getScriptOps = do+        empty <- isEmpty+        if empty+          then return []+          else (:) <$> deserialize <*> getScriptOps++  serialize (Script ops) = forM_ ops serialize++instance Binary Script where+  put = serialize+  get = deserialize++instance Serialize Script where+  put = serialize+  get = deserialize++-- | Data type representing the type of an OP_PUSHDATA opcode.+data PushDataType+  = -- | next opcode bytes is data to be pushed+    OPCODE+  | -- | next byte contains number of bytes of data to be pushed+    OPDATA1+  | -- | next two bytes contains number of bytes to be pushed+    OPDATA2+  | -- | next four bytes contains the number of bytes to be pushed+    OPDATA4+  deriving (Show, Read, Eq, Generic, Hashable, NFData)++-- | Data type representing an operator allowed inside a 'Script'.+data ScriptOp+  = -- Pushing Data+    OP_PUSHDATA+      !ByteString+      !PushDataType+  | OP_0+  | OP_1NEGATE+  | OP_RESERVED+  | OP_1+  | OP_2+  | OP_3+  | OP_4+  | OP_5+  | OP_6+  | OP_7+  | OP_8+  | OP_9+  | OP_10+  | OP_11+  | OP_12+  | OP_13+  | OP_14+  | OP_15+  | OP_16+  | -- Flow control+    OP_NOP+  | OP_VER -- reserved+  | OP_IF+  | OP_NOTIF+  | OP_VERIF -- resreved+  | OP_VERNOTIF -- reserved+  | OP_ELSE+  | OP_ENDIF+  | OP_VERIFY+  | OP_RETURN+  | -- Stack operations+    OP_TOALTSTACK+  | OP_FROMALTSTACK+  | OP_IFDUP+  | OP_DEPTH+  | OP_DROP+  | OP_DUP+  | OP_NIP+  | OP_OVER+  | OP_PICK+  | OP_ROLL+  | OP_ROT+  | OP_SWAP+  | OP_TUCK+  | OP_2DROP+  | OP_2DUP+  | OP_3DUP+  | OP_2OVER+  | OP_2ROT+  | OP_2SWAP+  | -- Splice+    OP_CAT+  | OP_SUBSTR+  | OP_LEFT+  | OP_RIGHT+  | OP_SIZE+  | -- Bitwise logic+    OP_INVERT+  | OP_AND+  | OP_OR+  | OP_XOR+  | OP_EQUAL+  | OP_EQUALVERIFY+  | OP_RESERVED1+  | OP_RESERVED2+  | -- Arithmetic+    OP_1ADD+  | OP_1SUB+  | OP_2MUL+  | OP_2DIV+  | OP_NEGATE+  | OP_ABS+  | OP_NOT+  | OP_0NOTEQUAL+  | OP_ADD+  | OP_SUB+  | OP_MUL+  | OP_DIV+  | OP_MOD+  | OP_LSHIFT+  | OP_RSHIFT+  | OP_BOOLAND+  | OP_BOOLOR+  | OP_NUMEQUAL+  | OP_NUMEQUALVERIFY+  | OP_NUMNOTEQUAL+  | OP_LESSTHAN+  | OP_GREATERTHAN+  | OP_LESSTHANOREQUAL+  | OP_GREATERTHANOREQUAL+  | OP_MIN+  | OP_MAX+  | OP_WITHIN+  | -- Crypto+    OP_RIPEMD160+  | OP_SHA1+  | OP_SHA256+  | OP_HASH160+  | OP_HASH256+  | OP_CODESEPARATOR+  | OP_CHECKSIG+  | OP_CHECKSIGVERIFY+  | OP_CHECKMULTISIG+  | OP_CHECKMULTISIGVERIFY+  | -- Expansion+    OP_NOP1+  | OP_CHECKLOCKTIMEVERIFY+  | OP_CHECKSEQUENCEVERIFY+  | OP_NOP4+  | OP_NOP5+  | OP_NOP6+  | OP_NOP7+  | OP_NOP8+  | OP_NOP9+  | OP_NOP10+  | -- Bitcoin Cash Nov 2018 hard fork+    OP_CHECKDATASIG+  | OP_CHECKDATASIGVERIFY+  | -- Bitcoin Cash May 2020 hard fork+    OP_REVERSEBYTES+  | -- Other+    OP_PUBKEYHASH+  | OP_PUBKEY+  | OP_INVALIDOPCODE !Word8+  deriving (Show, Read, Eq, Generic, Hashable, NFData)++instance Serial ScriptOp where+  deserialize = go . fromIntegral =<< getWord8+    where+      go op+        | op == 0x00 = return OP_0+        | op <= 0x4b = do+            payload <- getByteString (fromIntegral op)+            return $ OP_PUSHDATA payload OPCODE+        | op == 0x4c = do+            len <- getWord8+            payload <- getByteString (fromIntegral len)+            return $ OP_PUSHDATA payload OPDATA1+        | op == 0x4d = do+            len <- getWord16le+            payload <- getByteString (fromIntegral len)+            return $ OP_PUSHDATA payload OPDATA2+        | op == 0x4e = do+            len <- getWord32le+            payload <- getByteString (fromIntegral len)+            return $ OP_PUSHDATA payload OPDATA4+        | op == 0x4f = return OP_1NEGATE+        | op == 0x50 = return OP_RESERVED+        | op == 0x51 = return OP_1+        | op == 0x52 = return OP_2+        | op == 0x53 = return OP_3+        | op == 0x54 = return OP_4+        | op == 0x55 = return OP_5+        | op == 0x56 = return OP_6+        | op == 0x57 = return OP_7+        | op == 0x58 = return OP_8+        | op == 0x59 = return OP_9+        | op == 0x5a = return OP_10+        | op == 0x5b = return OP_11+        | op == 0x5c = return OP_12+        | op == 0x5d = return OP_13+        | op == 0x5e = return OP_14+        | op == 0x5f = return OP_15+        | op == 0x60 = return OP_16+        -- Flow control+        | op == 0x61 = return OP_NOP+        | op == 0x62 = return OP_VER -- reserved+        | op == 0x63 = return OP_IF+        | op == 0x64 = return OP_NOTIF+        | op == 0x65 = return OP_VERIF -- reserved+        | op == 0x66 = return OP_VERNOTIF -- reserved+        | op == 0x67 = return OP_ELSE+        | op == 0x68 = return OP_ENDIF+        | op == 0x69 = return OP_VERIFY+        | op == 0x6a = return OP_RETURN+        -- Stack+        | op == 0x6b = return OP_TOALTSTACK+        | op == 0x6c = return OP_FROMALTSTACK+        | op == 0x6d = return OP_2DROP+        | op == 0x6e = return OP_2DUP+        | op == 0x6f = return OP_3DUP+        | op == 0x70 = return OP_2OVER+        | op == 0x71 = return OP_2ROT+        | op == 0x72 = return OP_2SWAP+        | op == 0x73 = return OP_IFDUP+        | op == 0x74 = return OP_DEPTH+        | op == 0x75 = return OP_DROP+        | op == 0x76 = return OP_DUP+        | op == 0x77 = return OP_NIP+        | op == 0x78 = return OP_OVER+        | op == 0x79 = return OP_PICK+        | op == 0x7a = return OP_ROLL+        | op == 0x7b = return OP_ROT+        | op == 0x7c = return OP_SWAP+        | op == 0x7d = return OP_TUCK+        -- Splice+        | op == 0x7e = return OP_CAT+        | op == 0x7f = return OP_SUBSTR+        | op == 0x80 = return OP_LEFT+        | op == 0x81 = return OP_RIGHT+        | op == 0x82 = return OP_SIZE+        -- Bitwise logic+        | op == 0x83 = return OP_INVERT+        | op == 0x84 = return OP_AND+        | op == 0x85 = return OP_OR+        | op == 0x86 = return OP_XOR+        | op == 0x87 = return OP_EQUAL+        | op == 0x88 = return OP_EQUALVERIFY+        | op == 0x89 = return OP_RESERVED1+        | op == 0x8a = return OP_RESERVED2+        -- Arithmetic+        | op == 0x8b = return OP_1ADD+        | op == 0x8c = return OP_1SUB+        | op == 0x8d = return OP_2MUL+        | op == 0x8e = return OP_2DIV+        | op == 0x8f = return OP_NEGATE+        | op == 0x90 = return OP_ABS+        | op == 0x91 = return OP_NOT+        | op == 0x92 = return OP_0NOTEQUAL+        | op == 0x93 = return OP_ADD+        | op == 0x94 = return OP_SUB+        | op == 0x95 = return OP_MUL+        | op == 0x96 = return OP_DIV+        | op == 0x97 = return OP_MOD+        | op == 0x98 = return OP_LSHIFT+        | op == 0x99 = return OP_RSHIFT+        | op == 0x9a = return OP_BOOLAND+        | op == 0x9b = return OP_BOOLOR+        | op == 0x9c = return OP_NUMEQUAL+        | op == 0x9d = return OP_NUMEQUALVERIFY+        | op == 0x9e = return OP_NUMNOTEQUAL+        | op == 0x9f = return OP_LESSTHAN+        | op == 0xa0 = return OP_GREATERTHAN+        | op == 0xa1 = return OP_LESSTHANOREQUAL+        | op == 0xa2 = return OP_GREATERTHANOREQUAL+        | op == 0xa3 = return OP_MIN+        | op == 0xa4 = return OP_MAX+        | op == 0xa5 = return OP_WITHIN+        -- Crypto+        | op == 0xa6 = return OP_RIPEMD160+        | op == 0xa7 = return OP_SHA1+        | op == 0xa8 = return OP_SHA256+        | op == 0xa9 = return OP_HASH160+        | op == 0xaa = return OP_HASH256+        | op == 0xab = return OP_CODESEPARATOR+        | op == 0xac = return OP_CHECKSIG+        | op == 0xad = return OP_CHECKSIGVERIFY+        | op == 0xae = return OP_CHECKMULTISIG+        | op == 0xaf = return OP_CHECKMULTISIGVERIFY+        -- More NOPs+        | op == 0xb0 = return OP_NOP1+        | op == 0xb1 = return OP_CHECKLOCKTIMEVERIFY+        | op == 0xb2 = return OP_CHECKSEQUENCEVERIFY+        | op == 0xb3 = return OP_NOP4+        | op == 0xb4 = return OP_NOP5+        | op == 0xb5 = return OP_NOP6+        | op == 0xb6 = return OP_NOP7+        | op == 0xb7 = return OP_NOP8+        | op == 0xb8 = return OP_NOP9+        | op == 0xb9 = return OP_NOP10+        -- Bitcoin Cash Nov 2018 hard fork+        | op == 0xba = return OP_CHECKDATASIG+        | op == 0xbb = return OP_CHECKDATASIGVERIFY+        -- Bitcoin Cash May 2020 hard fork+        | op == 0xbc = return OP_REVERSEBYTES+        -- Constants+        | op == 0xfd = return OP_PUBKEYHASH+        | op == 0xfe = return OP_PUBKEY+        | otherwise = return $ OP_INVALIDOPCODE op++  serialize op = case op of+    (OP_PUSHDATA payload optype) -> do+      let len = B.length payload+      case optype of+        OPCODE -> do+          unless (len <= 0x4b) $+            error "OP_PUSHDATA OPCODE: Payload size too big"+          putWord8 $ fromIntegral len+        OPDATA1 -> do+          unless (len <= 0xff) $+            error "OP_PUSHDATA OPDATA1: Payload size too big"+          putWord8 0x4c+          putWord8 $ fromIntegral len+        OPDATA2 -> do+          unless (len <= 0xffff) $+            error "OP_PUSHDATA OPDATA2: Payload size too big"+          putWord8 0x4d+          putWord16le $ fromIntegral len+        OPDATA4 -> do+          unless (len <= 0x7fffffff) $+            error "OP_PUSHDATA OPDATA4: Payload size too big"+          putWord8 0x4e+          putWord32le $ fromIntegral len+      putByteString payload++    -- Constants+    OP_0 -> putWord8 0x00+    OP_1NEGATE -> putWord8 0x4f+    OP_RESERVED -> putWord8 0x50+    OP_1 -> putWord8 0x51+    OP_2 -> putWord8 0x52+    OP_3 -> putWord8 0x53+    OP_4 -> putWord8 0x54+    OP_5 -> putWord8 0x55+    OP_6 -> putWord8 0x56+    OP_7 -> putWord8 0x57+    OP_8 -> putWord8 0x58+    OP_9 -> putWord8 0x59+    OP_10 -> putWord8 0x5a+    OP_11 -> putWord8 0x5b+    OP_12 -> putWord8 0x5c+    OP_13 -> putWord8 0x5d+    OP_14 -> putWord8 0x5e+    OP_15 -> putWord8 0x5f+    OP_16 -> putWord8 0x60+    -- Crypto Constants+    OP_PUBKEY -> putWord8 0xfe+    OP_PUBKEYHASH -> putWord8 0xfd+    -- Invalid Opcodes+    (OP_INVALIDOPCODE x) -> putWord8 x+    -- Flow Control+    OP_NOP -> putWord8 0x61+    OP_VER -> putWord8 0x62+    OP_IF -> putWord8 0x63+    OP_NOTIF -> putWord8 0x64+    OP_VERIF -> putWord8 0x65+    OP_VERNOTIF -> putWord8 0x66+    OP_ELSE -> putWord8 0x67+    OP_ENDIF -> putWord8 0x68+    OP_VERIFY -> putWord8 0x69+    OP_RETURN -> putWord8 0x6a+    -- Stack Operations+    OP_TOALTSTACK -> putWord8 0x6b+    OP_FROMALTSTACK -> putWord8 0x6c+    OP_2DROP -> putWord8 0x6d+    OP_2DUP -> putWord8 0x6e+    OP_3DUP -> putWord8 0x6f+    OP_2OVER -> putWord8 0x70+    OP_2ROT -> putWord8 0x71+    OP_2SWAP -> putWord8 0x72+    OP_IFDUP -> putWord8 0x73+    OP_DEPTH -> putWord8 0x74+    OP_DROP -> putWord8 0x75+    OP_DUP -> putWord8 0x76+    OP_NIP -> putWord8 0x77+    OP_OVER -> putWord8 0x78+    OP_PICK -> putWord8 0x79+    OP_ROLL -> putWord8 0x7a+    OP_ROT -> putWord8 0x7b+    OP_SWAP -> putWord8 0x7c+    OP_TUCK -> putWord8 0x7d+    -- Splice+    OP_CAT -> putWord8 0x7e+    OP_SUBSTR -> putWord8 0x7f+    OP_LEFT -> putWord8 0x80+    OP_RIGHT -> putWord8 0x81+    OP_SIZE -> putWord8 0x82+    -- Bitwise Logic+    OP_INVERT -> putWord8 0x83+    OP_AND -> putWord8 0x84+    OP_OR -> putWord8 0x85+    OP_XOR -> putWord8 0x86+    OP_EQUAL -> putWord8 0x87+    OP_EQUALVERIFY -> putWord8 0x88+    OP_RESERVED1 -> putWord8 0x89+    OP_RESERVED2 -> putWord8 0x8a+    -- Arithmetic+    OP_1ADD -> putWord8 0x8b+    OP_1SUB -> putWord8 0x8c+    OP_2MUL -> putWord8 0x8d+    OP_2DIV -> putWord8 0x8e+    OP_NEGATE -> putWord8 0x8f+    OP_ABS -> putWord8 0x90+    OP_NOT -> putWord8 0x91+    OP_0NOTEQUAL -> putWord8 0x92+    OP_ADD -> putWord8 0x93+    OP_SUB -> putWord8 0x94+    OP_MUL -> putWord8 0x95+    OP_DIV -> putWord8 0x96+    OP_MOD -> putWord8 0x97+    OP_LSHIFT -> putWord8 0x98+    OP_RSHIFT -> putWord8 0x99+    OP_BOOLAND -> putWord8 0x9a+    OP_BOOLOR -> putWord8 0x9b+    OP_NUMEQUAL -> putWord8 0x9c+    OP_NUMEQUALVERIFY -> putWord8 0x9d+    OP_NUMNOTEQUAL -> putWord8 0x9e+    OP_LESSTHAN -> putWord8 0x9f+    OP_GREATERTHAN -> putWord8 0xa0+    OP_LESSTHANOREQUAL -> putWord8 0xa1+    OP_GREATERTHANOREQUAL -> putWord8 0xa2+    OP_MIN -> putWord8 0xa3+    OP_MAX -> putWord8 0xa4+    OP_WITHIN -> putWord8 0xa5+    -- Crypto+    OP_RIPEMD160 -> putWord8 0xa6+    OP_SHA1 -> putWord8 0xa7+    OP_SHA256 -> putWord8 0xa8+    OP_HASH160 -> putWord8 0xa9+    OP_HASH256 -> putWord8 0xaa+    OP_CODESEPARATOR -> putWord8 0xab+    OP_CHECKSIG -> putWord8 0xac+    OP_CHECKSIGVERIFY -> putWord8 0xad+    OP_CHECKMULTISIG -> putWord8 0xae+    OP_CHECKMULTISIGVERIFY -> putWord8 0xaf+    -- More NOPs+    OP_NOP1 -> putWord8 0xb0+    OP_CHECKLOCKTIMEVERIFY -> putWord8 0xb1+    OP_CHECKSEQUENCEVERIFY -> putWord8 0xb2+    OP_NOP4 -> putWord8 0xb3+    OP_NOP5 -> putWord8 0xb4+    OP_NOP6 -> putWord8 0xb5+    OP_NOP7 -> putWord8 0xb6+    OP_NOP8 -> putWord8 0xb7+    OP_NOP9 -> putWord8 0xb8+    OP_NOP10 -> putWord8 0xb9+    -- Bitcoin Cash Nov 2018 hard fork+    OP_CHECKDATASIG -> putWord8 0xba+    OP_CHECKDATASIGVERIFY -> putWord8 0xbb+    -- Bitcoin Cash May 2020 hard fork+    OP_REVERSEBYTES -> putWord8 0xbc++instance Binary ScriptOp where+  put = serialize+  get = deserialize++instance Serialize ScriptOp where+  put = serialize+  get = deserialize++-- | Check whether opcode is only data.+isPushOp :: ScriptOp -> Bool+isPushOp op = case op of+  OP_PUSHDATA _ _ -> True+  OP_0 -> True+  OP_1NEGATE -> True+  OP_1 -> True+  OP_2 -> True+  OP_3 -> True+  OP_4 -> True+  OP_5 -> True+  OP_6 -> True+  OP_7 -> True+  OP_8 -> True+  OP_9 -> True+  OP_10 -> True+  OP_11 -> True+  OP_12 -> True+  OP_13 -> True+  OP_14 -> True+  OP_15 -> True+  OP_16 -> True+  _ -> False++-- | Optimally encode data using one of the 4 types of data pushing opcodes.+opPushData :: ByteString -> ScriptOp+opPushData bs+  | len <= 0x4b = OP_PUSHDATA bs OPCODE+  | len <= 0xff = OP_PUSHDATA bs OPDATA1+  | len <= 0xffff = OP_PUSHDATA bs OPDATA2+  | len <= 0xffffffff = OP_PUSHDATA bs OPDATA4+  | otherwise = error "opPushData: payload size too big"+  where+    len = B.length bs++-- | Transforms integers @[1 .. 16]@ to 'ScriptOp' @[OP_1 .. OP_16]@.+intToScriptOp :: Int -> ScriptOp+intToScriptOp i+  | i `elem` [1 .. 16] = op+  | otherwise = err+  where+    op =+      fromRight err+        . runGetS deserialize+        . B.singleton+        . fromIntegral+        $ i + 0x50+    err = error $ "intToScriptOp: Invalid integer " ++ show i++-- | Decode 'ScriptOp' @[OP_1 .. OP_16]@ to integers @[1 .. 16]@. This functions+-- fails for other values of 'ScriptOp'+scriptOpToInt :: ScriptOp -> Either String Int+scriptOpToInt s+  | res `elem` [1 .. 16] = return res+  | otherwise = Left $ "scriptOpToInt: invalid opcode " ++ show s+  where+    res = fromIntegral (B.head $ runPutS $ serialize s) - 0x50
+ src/Haskoin/Script/SigHash.hs view
@@ -0,0 +1,350 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Script.SigHash+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Transaction signatures and related functions.+module Haskoin.Script.SigHash+  ( -- * Script Signatures+    SigHash (..),+    SigHashFlag (..),+    sigHashAll,+    sigHashNone,+    sigHashSingle,+    anyoneCanPay,+    hasForkIdFlag,+    setAnyoneCanPay,+    setForkIdFlag,+    isSigHashAll,+    isSigHashNone,+    isSigHashSingle,+    isSigHashUnknown,+    sigHashAddForkId,+    sigHashGetForkId,+    sigHashAddNetworkId,+    txSigHash,+    txSigHashForkId,+    TxSignature (..),+    decodeTxSig,+    encodeTxSig,+  )+where++import Control.DeepSeq+import Control.Monad+import Crypto.Secp256k1+import Data.Aeson+import Data.Bits+import Data.Bool (bool)+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Hashable+import Data.Maybe+import Data.Scientific+import Data.Word+import GHC.Generics (Generic)+import Haskoin.Crypto.Hash+import Haskoin.Crypto.Signature+import Haskoin.Network.Common+import Haskoin.Network.Data+import Haskoin.Script.Common+import Haskoin.Transaction.Common+import Haskoin.Util++-- | Constant representing a SIGHASH flag that controls what is being signed.+data SigHashFlag+  = -- | sign all outputs+    SIGHASH_ALL+  | -- | sign no outputs+    SIGHASH_NONE+  | -- | sign the output index corresponding to the input+    SIGHASH_SINGLE+  | -- | replay protection for Bitcoin Cash transactions+    SIGHASH_FORKID+  | -- | new inputs can be added+    SIGHASH_ANYONECANPAY+  deriving (Eq, Ord, Show, Read, Generic)++instance NFData SigHashFlag++instance Hashable SigHashFlag++instance Enum SigHashFlag where+  fromEnum SIGHASH_ALL = 0x01+  fromEnum SIGHASH_NONE = 0x02+  fromEnum SIGHASH_SINGLE = 0x03+  fromEnum SIGHASH_FORKID = 0x40+  fromEnum SIGHASH_ANYONECANPAY = 0x80+  toEnum 0x01 = SIGHASH_ALL+  toEnum 0x02 = SIGHASH_NONE+  toEnum 0x03 = SIGHASH_SINGLE+  toEnum 0x40 = SIGHASH_FORKID+  toEnum 0x80 = SIGHASH_ANYONECANPAY+  toEnum _ = error "Not a valid sighash flag"++-- | Data type representing the different ways a transaction can be signed.+-- When producing a signature, a hash of the transaction is used as the message+-- to be signed. The 'SigHash' parameter controls which parts of the+-- transaction are used or ignored to produce the transaction hash. The idea is+-- that if some part of a transaction is not used to produce the transaction+-- hash, then you can change that part of the transaction after producing a+-- signature without invalidating that signature.+--+-- If the 'SIGHASH_ANYONECANPAY' flag is set (true), then only the current input+-- is signed. Otherwise, all of the inputs of a transaction are signed. The+-- default value for 'SIGHASH_ANYONECANPAY' is unset (false).+newtype SigHash+  = SigHash Word32+  deriving (Eq, Ord, Enum, Show, Read, Generic)+  deriving newtype (Bits, Integral, Num, Real, Hashable, NFData)++instance FromJSON SigHash where+  parseJSON =+    withScientific "sighash" $+      maybe mzero (return . SigHash) . toBoundedInteger++instance ToJSON SigHash where+  toJSON = Number . fromIntegral+  toEncoding (SigHash n) = toEncoding n++-- | SIGHASH_NONE as a byte.+sigHashNone :: SigHash+sigHashNone = fromIntegral $ fromEnum SIGHASH_NONE++-- | SIGHASH_ALL as a byte.+sigHashAll :: SigHash+sigHashAll = fromIntegral $ fromEnum SIGHASH_ALL++-- | SIGHASH_SINGLE as a byte.+sigHashSingle :: SigHash+sigHashSingle = fromIntegral $ fromEnum SIGHASH_SINGLE++-- | SIGHASH_FORKID as a byte.+sigHashForkId :: SigHash+sigHashForkId = fromIntegral $ fromEnum SIGHASH_FORKID++-- | SIGHASH_ANYONECANPAY as a byte.+sigHashAnyoneCanPay :: SigHash+sigHashAnyoneCanPay = fromIntegral $ fromEnum SIGHASH_ANYONECANPAY++-- | Set SIGHASH_FORKID flag.+setForkIdFlag :: SigHash -> SigHash+setForkIdFlag = (.|. sigHashForkId)++-- | Set SIGHASH_ANYONECANPAY flag.+setAnyoneCanPay :: SigHash -> SigHash+setAnyoneCanPay = (.|. sigHashAnyoneCanPay)++-- | Is the SIGHASH_FORKID flag set?+hasForkIdFlag :: SigHash -> Bool+hasForkIdFlag = (/= 0) . (.&. sigHashForkId)++-- | Is the SIGHASH_ANYONECANPAY flag set?+anyoneCanPay :: SigHash -> Bool+anyoneCanPay = (/= 0) . (.&. sigHashAnyoneCanPay)++-- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_ALL'.+isSigHashAll :: SigHash -> Bool+isSigHashAll = (== sigHashAll) . (.&. 0x1f)++-- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_NONE'.+isSigHashNone :: SigHash -> Bool+isSigHashNone = (== sigHashNone) . (.&. 0x1f)++-- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_SINGLE'.+isSigHashSingle :: SigHash -> Bool+isSigHashSingle = (== sigHashSingle) . (.&. 0x1f)++-- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_UNKNOWN'.+isSigHashUnknown :: SigHash -> Bool+isSigHashUnknown =+  (`notElem` [sigHashAll, sigHashNone, sigHashSingle]) . (.&. 0x1f)++-- | Add a fork id to a 'SigHash'.+sigHashAddForkId :: SigHash -> Word32 -> SigHash+sigHashAddForkId sh w = (fromIntegral w `shiftL` 8) .|. (sh .&. 0x000000ff)++-- | Add fork id of a particular network to a 'SigHash'.+sigHashAddNetworkId :: Network -> SigHash -> SigHash+sigHashAddNetworkId net =+  (`sigHashAddForkId` fromMaybe 0 net.sigHashForkId)++-- | Get fork id from 'SigHash'.+sigHashGetForkId :: SigHash -> Word32+sigHashGetForkId (SigHash n) = fromIntegral $ n `shiftR` 8++-- | Computes the hash that will be used for signing a transaction.+txSigHash ::+  Network ->+  -- | transaction to sign+  Tx ->+  -- | script from output being spent+  Script ->+  -- | value of output being spent+  Word64 ->+  -- | index of input being signed+  Int ->+  -- | what to sign+  SigHash ->+  -- | hash to be signed+  Hash256+txSigHash net tx out v i sh+  | hasForkIdFlag sh && isJust net.sigHashForkId =+      txSigHashForkId net tx out v i sh+  | otherwise = do+      let newIn = buildInputs tx.inputs fout i sh+      -- When SigSingle and input index > outputs, then sign integer 1+      fromMaybe one $ do+        newOut <- buildOutputs tx.outputs i sh+        let newTx = Tx tx.version newIn newOut [] tx.locktime+        return . doubleSHA256 . runPutS $ do+          serialize newTx+          putWord32le $ fromIntegral sh+  where+    fout = Script $ filter (/= OP_CODESEPARATOR) out.ops+    one = "0100000000000000000000000000000000000000000000000000000000000000"++-- | Build transaction inputs for computing sighashes.+buildInputs :: [TxIn] -> Script -> Int -> SigHash -> [TxIn]+buildInputs txins out i sh+  | anyoneCanPay sh = [serialOut (txins !! i)]+  | isSigHashAll sh || isSigHashUnknown sh = single+  | otherwise = zipWith noSeq single [0 ..]+  where+    serialOut TxIn {..} = TxIn {script = runPutS $ serialize out, ..}+    emptyIn TxIn {..} = TxIn {script = B.empty, ..}+    emptyIns = map emptyIn txins+    single = updateIndex i emptyIns serialOut+    noSeq TxIn {..} j = TxIn {sequence = if i == j then sequence else 0, ..}++-- | Build transaction outputs for computing sighashes.+buildOutputs :: [TxOut] -> Int -> SigHash -> Maybe [TxOut]+buildOutputs txos i sh+  | isSigHashAll sh || isSigHashUnknown sh = return txos+  | isSigHashNone sh = return []+  | i >= length txos = Nothing+  | otherwise = return $ buffer ++ [txos !! i]+  where+    buffer = replicate i $ TxOut maxBound B.empty++-- | Compute the hash that will be used for signing a transaction. This+-- function is used when the 'SIGHASH_FORKID' flag is set.+txSigHashForkId ::+  Network ->+  -- | transaction to sign+  Tx ->+  -- | script from output being spent+  Script ->+  -- | value of output being spent+  Word64 ->+  -- | index of input being signed+  Int ->+  -- | what to sign+  SigHash ->+  -- | hash to be signed+  Hash256+txSigHashForkId net tx out v i sh =+  doubleSHA256 . runPutS $ do+    putWord32le tx.version+    serialize hashPrevouts+    serialize hashSequence+    serialize (tx.inputs !! i).outpoint+    putScript out+    putWord64le v+    putWord32le (tx.inputs !! i).sequence+    serialize hashOutputs+    putWord32le tx.locktime+    putWord32le $ fromIntegral $ sigHashAddNetworkId net sh+  where+    hashPrevouts+      | not (anyoneCanPay sh) =+          doubleSHA256 . runPutS $ mapM_ (serialize . (.outpoint)) tx.inputs+      | otherwise = zeros+    hashSequence+      | not (anyoneCanPay sh || isSigHashSingle sh || isSigHashNone sh) =+          doubleSHA256 . runPutS $ mapM_ (putWord32le . (.sequence)) tx.inputs+      | otherwise = zeros+    hashOutputs+      | not (isSigHashSingle sh || isSigHashNone sh) =+          doubleSHA256 . runPutS $ mapM_ serialize tx.outputs+      | isSigHashSingle sh && i < length tx.outputs =+          doubleSHA256 . runPutS $ serialize $ tx.outputs !! i+      | otherwise = zeros+    putScript s = do+      let encodedScript = runPutS $ serialize s+      putVarInt $ B.length encodedScript+      putByteString encodedScript+    zeros :: Hash256+    zeros = "0000000000000000000000000000000000000000000000000000000000000000"++-- | Data type representing a signature together with a 'SigHash'. The 'SigHash'+-- is serialized as one byte at the end of an ECDSA 'Sig'. All signatures in+-- transaction inputs are of type 'TxSignature'.+data TxSignature+  = TxSignature+      { sig :: !Sig,+        hash :: !SigHash+      }+  | TxSignatureEmpty+  deriving (Eq, Show, Read, Generic, NFData)++instance Marshal (Network, Ctx) TxSignature where+  marshalPut (net, ctx) TxSignatureEmpty = return ()+  marshalPut (net, ctx) (TxSignature sig (SigHash n)) = do+    marshalPut ctx sig+    putWord8 (fromIntegral n)++  marshalGet (net, ctx) =+    bool decode empty =<< isEmpty+    where+      empty = return TxSignatureEmpty+      decode = do+        sig <- marshalGet ctx+        sh <- fromIntegral <$> getWord8+        when (isSigHashUnknown sh) $+          fail "Non-canonical signature: unknown hashtype byte"+        when (isNothing net.sigHashForkId && hasForkIdFlag sh) $+          fail "Non-canonical signature: invalid network for forkId"+        return $ TxSignature sig sh++instance MarshalJSON (Network, Ctx) TxSignature where+  marshalValue (net, ctx) = String . encodeHex . encodeTxSig net ctx+  marshalEncoding s = hexEncoding . runPutL . marshalPut s+  unmarshalValue (net, ctx) =+    withText "TxSignature" $ \t ->+      case decodeHex t of+        Nothing -> fail "Cannot decode hex signature"+        Just b -> case decodeTxSig net ctx b of+          Left e -> fail e+          Right s -> return s++encodeTxSig :: Network -> Ctx -> TxSignature -> ByteString+encodeTxSig net ctx = runPutS . marshalPut (net, ctx)++decodeTxSig :: Network -> Ctx -> ByteString -> Either String TxSignature+decodeTxSig net ctx =+  runGetS $ do+    sig <- marshalGet (net, ctx)+    e <- isEmpty+    unless e $+      fail "Non-canonical signature: multiple hashtype bytes"+    return sig
+ src/Haskoin/Script/Standard.hs view
@@ -0,0 +1,435 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Script.Standard+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Standard scripts like pay-to-public-key, pay-to-public-key-hash,+-- pay-to-script-hash, pay-to-multisig and corresponding SegWit variants.+module Haskoin.Script.Standard+  ( -- * Standard Script Outputs+    ScriptOutput (..),+    RedeemScript,+    isPayPK,+    isPayPKHash,+    isPayMulSig,+    isPayScriptHash,+    isPayWitness,+    isPayWitnessPKHash,+    isPayWitnessScriptHash,+    isDataCarrier,+    encodeOutput,+    decodeOutput,+    toP2SH,+    toP2WSH,+    sortMulSig,++    -- * Standard Script Inputs+    ScriptInput (..),+    SimpleInput (..),+    encodeInput,+    decodeInput,+    isSpendPK,+    isSpendPKHash,+    isSpendMulSig,+    isScriptHashInput,+  )+where++import Control.Applicative ((<|>))+import Control.DeepSeq+import Control.Monad (guard, liftM2, (<=<))+import Crypto.Secp256k1+import Data.Aeson (ToJSON (..), Value (..), withText)+import Data.Aeson.Encoding (Encoding, text)+import Data.Aeson.Types (Parser)+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Function (on)+import Data.Hashable+import Data.List (sortBy)+import Data.Maybe (fromJust, isJust)+import Data.Word (Word8)+import GHC.Generics (Generic)+import Haskoin.Crypto.Hash+import Haskoin.Crypto.Keys.Common+import Haskoin.Network.Data+import Haskoin.Script.Common+import Haskoin.Script.SigHash+import Haskoin.Util++-- | Data type describing standard transaction output scripts. Output scripts+-- provide the conditions that must be fulfilled for someone to spend the funds+-- in a transaction output.+data ScriptOutput+  = -- | pay to public key+    PayPK {key :: !PublicKey}+  | -- | pay to public key hash+    PayPKHash {hash160 :: !Hash160}+  | -- | multisig+    PayMulSig+      { keys :: ![PublicKey],+        required :: !Int+      }+  | -- | pay to a script hash+    PayScriptHash {hash160 :: !Hash160}+  | -- | pay to witness public key hash+    PayWitnessPKHash {hash160 :: !Hash160}+  | -- | pay to witness script hash+    PayWitnessScriptHash {hash256 :: !Hash256}+  | -- | another pay to witness address+    PayWitness+      { version :: !Word8,+        bytes :: !ByteString+      }+  | -- | provably unspendable data carrier+    DataCarrier {bytes :: !ByteString}+  deriving (Eq, Show, Read, Generic, NFData)++instance MarshalJSON Ctx ScriptOutput where+  unmarshalValue ctx =+    withText "ScriptOutput" $ \t ->+      case decodeHex t of+        Nothing -> fail "Could not decode hex script"+        Just bs -> either fail return $ unmarshal ctx bs++  marshalValue ctx = String . encodeHex . marshal ctx++  marshalEncoding ctx = hexEncoding . runPutL . marshalPut ctx++-- | Is script a pay-to-public-key output?+isPayPK :: ScriptOutput -> Bool+isPayPK (PayPK _) = True+isPayPK _ = False++-- | Is script a pay-to-pub-key-hash output?+isPayPKHash :: ScriptOutput -> Bool+isPayPKHash (PayPKHash _) = True+isPayPKHash _ = False++-- | Is script a pay-to-multi-sig output?+isPayMulSig :: ScriptOutput -> Bool+isPayMulSig (PayMulSig _ _) = True+isPayMulSig _ = False++-- | Is script a pay-to-script-hash output?+isPayScriptHash :: ScriptOutput -> Bool+isPayScriptHash (PayScriptHash _) = True+isPayScriptHash _ = False++-- | Is script a pay-to-witness-pub-key-hash output?+isPayWitnessPKHash :: ScriptOutput -> Bool+isPayWitnessPKHash (PayWitnessPKHash _) = True+isPayWitnessPKHash _ = False++-- | Is script a pay-to-witness-script-hash output?+isPayWitnessScriptHash :: ScriptOutput -> Bool+isPayWitnessScriptHash (PayWitnessScriptHash _) = True+isPayWitnessScriptHash _ = False++-- | Is script paying to a different type of witness address?+isPayWitness :: ScriptOutput -> Bool+isPayWitness (PayWitness _ _) = True+isPayWitness _ = False++-- | Is script a data carrier output?+isDataCarrier :: ScriptOutput -> Bool+isDataCarrier (DataCarrier _) = True+isDataCarrier _ = False++-- | Tries to decode a 'ScriptOutput' from a 'Script'. This can fail if the+-- script is not recognized as any of the standard output types.+decodeOutput :: Ctx -> Script -> Either String ScriptOutput+decodeOutput ctx s = case s.ops of+  -- Pay to PubKey+  [OP_PUSHDATA bs _, OP_CHECKSIG] ->+    PayPK <$> unmarshal ctx bs+  -- Pay to PubKey Hash+  [OP_DUP, OP_HASH160, OP_PUSHDATA bs _, OP_EQUALVERIFY, OP_CHECKSIG] ->+    PayPKHash <$> runGetS deserialize bs+  -- Pay to Script Hash+  [OP_HASH160, OP_PUSHDATA bs _, OP_EQUAL] ->+    PayScriptHash <$> runGetS deserialize bs+  -- Pay to Witness+  [OP_0, OP_PUSHDATA bs OPCODE]+    | B.length bs == 20 ->+        PayWitnessPKHash <$> runGetS deserialize bs+    | B.length bs == 32 ->+        PayWitnessScriptHash <$> runGetS deserialize bs+    | B.length bs /= 20 && B.length bs /= 32 ->+        Left+          "decodeOutput: invalid version 0 segwit \+          \(must be 20 or 32 bytes)"+  -- Other Witness+  [ver, OP_PUSHDATA bs _]+    | Just wv <- opWitnessVersion ver,+      B.length bs >= 2,+      B.length bs <= 40 ->+        Right $ PayWitness wv bs+  -- Provably unspendable data carrier output+  [OP_RETURN, OP_PUSHDATA bs _] -> Right $ DataCarrier bs+  -- Pay to MultiSig Keys+  _ -> case matchPayMulSig ctx s of+    Right x -> return x+    Left _ -> Left "decodeOutput: Non-standard output"++witnessVersionOp :: Word8 -> Maybe ScriptOp+witnessVersionOp 0 = Just OP_0+witnessVersionOp 1 = Just OP_1+witnessVersionOp 2 = Just OP_2+witnessVersionOp 3 = Just OP_3+witnessVersionOp 4 = Just OP_4+witnessVersionOp 5 = Just OP_5+witnessVersionOp 6 = Just OP_6+witnessVersionOp 7 = Just OP_7+witnessVersionOp 8 = Just OP_8+witnessVersionOp 9 = Just OP_9+witnessVersionOp 10 = Just OP_10+witnessVersionOp 11 = Just OP_11+witnessVersionOp 12 = Just OP_12+witnessVersionOp 13 = Just OP_13+witnessVersionOp 14 = Just OP_14+witnessVersionOp 15 = Just OP_15+witnessVersionOp 16 = Just OP_16+witnessVersionOp _ = Nothing++opWitnessVersion :: ScriptOp -> Maybe Word8+opWitnessVersion OP_0 = Just 0+opWitnessVersion OP_1 = Just 1+opWitnessVersion OP_2 = Just 2+opWitnessVersion OP_3 = Just 3+opWitnessVersion OP_4 = Just 4+opWitnessVersion OP_5 = Just 5+opWitnessVersion OP_6 = Just 6+opWitnessVersion OP_7 = Just 7+opWitnessVersion OP_8 = Just 8+opWitnessVersion OP_9 = Just 9+opWitnessVersion OP_10 = Just 10+opWitnessVersion OP_11 = Just 11+opWitnessVersion OP_12 = Just 12+opWitnessVersion OP_13 = Just 13+opWitnessVersion OP_14 = Just 14+opWitnessVersion OP_15 = Just 15+opWitnessVersion OP_16 = Just 16+opWitnessVersion _ = Nothing++-- | Computes a 'Script' from a standard 'ScriptOutput'.+encodeOutput :: Ctx -> ScriptOutput -> Script+encodeOutput ctx s = Script $ case s of+  -- Pay to PubKey+  (PayPK k) -> [opPushData $ marshal ctx k, OP_CHECKSIG]+  -- Pay to PubKey Hash Address+  (PayPKHash h) ->+    [ OP_DUP,+      OP_HASH160,+      opPushData $ runPutS $ serialize h,+      OP_EQUALVERIFY,+      OP_CHECKSIG+    ]+  -- Pay to MultiSig Keys+  (PayMulSig ps r)+    | r <= length ps ->+        let opM = intToScriptOp r+            opN = intToScriptOp $ length ps+            keys = map (opPushData . marshal ctx) ps+         in opM : keys ++ [opN, OP_CHECKMULTISIG]+    | otherwise -> error "encodeOutput: PayMulSig r must be <= than pkeys"+  -- Pay to Script Hash Address+  (PayScriptHash h) ->+    [OP_HASH160, opPushData $ runPutS $ serialize h, OP_EQUAL]+  -- Pay to Witness PubKey Hash Address+  (PayWitnessPKHash h) ->+    [OP_0, opPushData $ runPutS $ serialize h]+  (PayWitnessScriptHash h) ->+    [OP_0, opPushData $ runPutS $ serialize h]+  (PayWitness v h) ->+    [ case witnessVersionOp v of+        Nothing -> error "encodeOutput: invalid witness version"+        Just c -> c,+      opPushData h+    ]+  -- Provably unspendable output+  (DataCarrier d) -> [OP_RETURN, opPushData d]++instance Marshal Ctx ScriptOutput where+  marshalGet ctx = do+    script <- deserialize+    case decodeOutput ctx script of+      Left e -> fail e+      Right o -> return o+  marshalPut ctx = serialize . encodeOutput ctx++-- | Encode script as pay-to-script-hash script+toP2SH :: Script -> ScriptOutput+toP2SH = PayScriptHash . addressHash . runPutS . serialize++-- | Encode script as a pay-to-witness-script-hash script+toP2WSH :: Script -> ScriptOutput+toP2WSH = PayWitnessScriptHash . sha256 . runPutS . serialize++-- | Match @[OP_N, PubKey1, ..., PubKeyM, OP_M, OP_CHECKMULTISIG]@+matchPayMulSig :: Ctx -> Script -> Either String ScriptOutput+matchPayMulSig ctx (Script ops) = case splitAt (length ops - 2) ops of+  (m : xs, [n, OP_CHECKMULTISIG]) -> do+    (intM, intN) <- liftM2 (,) (scriptOpToInt m) (scriptOpToInt n)+    if intM <= intN && length xs == intN+      then liftM2 PayMulSig (go xs) (return intM)+      else Left "matchPayMulSig: Invalid M or N parameters"+  _ -> Left "matchPayMulSig: script did not match output template"+  where+    go (OP_PUSHDATA bs _ : xs) =+      liftM2 (:) (unmarshal ctx bs) (go xs)+    go [] =+      Right []+    go _ =+      Left "matchPayMulSig: invalid multisig opcode"++-- | Sort the public keys of a multisig output in ascending order by comparing+-- their compressed serialized representations. Refer to BIP-67.+sortMulSig :: Ctx -> ScriptOutput -> ScriptOutput+sortMulSig ctx out = case out of+  PayMulSig keys r ->+    PayMulSig+      (sortBy (compare `on` marshal ctx) keys)+      r+  _ -> error "Can only call orderMulSig on PayMulSig scripts"++-- | Data type describing standard transaction input scripts. Input scripts+-- provide the signing data required to unlock the coins of the output they are+-- trying to spend, except in pay-to-witness-public-key-hash and+-- pay-to-script-hash transactions.+data SimpleInput+  = SpendPK+      { -- | transaction signature+        signature :: !TxSignature+      }+  | SpendPKHash+      { -- | embedded signature+        signature :: !TxSignature,+        -- | public key+        key :: !PublicKey+      }+  | SpendMulSig+      { -- | list of signatures+        signatures :: ![TxSignature]+      }+  deriving (Eq, Show, Read, Generic, NFData)++-- | Returns true if the input script is spending from a pay-to-public-key+-- output.+isSpendPK :: ScriptInput -> Bool+isSpendPK (RegularInput (SpendPK _)) = True+isSpendPK _ = False++-- | Returns true if the input script is spending from a pay-to-public-key-hash+-- output.+isSpendPKHash :: ScriptInput -> Bool+isSpendPKHash (RegularInput (SpendPKHash _ _)) = True+isSpendPKHash _ = False++-- | Returns true if the input script is spending a multisig output.+isSpendMulSig :: ScriptInput -> Bool+isSpendMulSig (RegularInput (SpendMulSig _)) = True+isSpendMulSig _ = False++-- | Returns true if the input script is spending a pay-to-script-hash output.+isScriptHashInput :: ScriptInput -> Bool+isScriptHashInput (ScriptHashInput _ _) = True+isScriptHashInput _ = False++-- | A redeem script is the output script serialized into the spending input+-- script. It must be included in inputs that spend pay-to-script-hash outputs.+type RedeemScript = ScriptOutput++-- | Standard input script high-level representation.+data ScriptInput+  = RegularInput+      { -- | get wrapped simple input+        get :: !SimpleInput+      }+  | ScriptHashInput+      { -- | get simple input associated with redeem script+        get :: !SimpleInput,+        -- | redeem script+        redeem :: !RedeemScript+      }+  deriving (Show, Read, Eq, Generic, NFData)++-- | Heuristic to decode an input script into one of the standard types.+decodeSimpleInput :: Network -> Ctx -> Script -> Either String SimpleInput+decodeSimpleInput net ctx (Script ops) =+  maybeToEither errMsg $ matchPK ops <|> matchPKHash ops <|> matchMulSig ops+  where+    matchPK [op] = SpendPK <$> f op+    matchPK _ = Nothing+    matchPKHash [op, OP_PUSHDATA pub _] =+      SpendPKHash <$> f op <*> eitherToMaybe (unmarshal ctx pub)+    matchPKHash _ = Nothing+    matchMulSig (x : xs) = do+      guard $ x == OP_0+      SpendMulSig <$> mapM f xs+    matchMulSig _ = Nothing+    f OP_0 = return TxSignatureEmpty+    f (OP_PUSHDATA "" OPCODE) = f OP_0+    f (OP_PUSHDATA bs _) = eitherToMaybe $ decodeTxSig net ctx bs+    f _ = Nothing+    errMsg = "decodeInput: Could not decode script input"++-- | Heuristic to decode a 'ScriptInput' from a 'Script'. This function fails if+-- the script can not be parsed as a standard script input.+decodeInput :: Network -> Ctx -> Script -> Either String ScriptInput+decodeInput net ctx s@(Script ops) =+  maybeToEither errMsg $ matchSimpleInput <|> matchPayScriptHash+  where+    matchSimpleInput =+      RegularInput <$> eitherToMaybe (decodeSimpleInput net ctx s)+    matchPayScriptHash =+      case splitAt (length s.ops - 1) ops of+        (is, [OP_PUSHDATA bs _]) -> do+          rdm <- eitherToMaybe $ unmarshal ctx bs+          inp <- eitherToMaybe $ decodeSimpleInput net ctx $ Script is+          return $ ScriptHashInput inp rdm+        _ -> Nothing+    errMsg = "decodeInput: Could not decode script input"++instance Marshal (Network, Ctx) ScriptInput where+  marshalGet (net, ctx) =+    deserialize >>= either fail return . decodeInput net ctx++  marshalPut (net, ctx) =+    serialize . encodeInput net ctx++-- | Encode a standard input into a script.+encodeInput :: Network -> Ctx -> ScriptInput -> Script+encodeInput net ctx s = case s of+  RegularInput ri -> encodeSimpleInput net ctx ri+  ScriptHashInput i o ->+    Script $ (encodeSimpleInput net ctx i).ops ++ [opPushData $ marshal ctx o]++-- | Encode a standard 'SimpleInput' into opcodes as an input 'Script'.+encodeSimpleInput :: Network -> Ctx -> SimpleInput -> Script+encodeSimpleInput net ctx s =+  Script $+    case s of+      SpendPK ts -> [f ts]+      SpendPKHash ts p -> [f ts, opPushData $ marshal ctx p]+      SpendMulSig xs -> OP_0 : map f xs+  where+    f TxSignatureEmpty = OP_0+    f ts = opPushData $ encodeTxSig net ctx ts
+ src/Haskoin/Transaction.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE DuplicateRecordFields #-}++-- |+-- Module      : Haskoin.Transaction+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Transactions and related code.+module Haskoin.Transaction+  ( module Common,+    module Builder,+    module Segwit,+    module Taproot,+    module Partial,+    module Genesis,+  )+where++import Haskoin.Transaction.Builder as Builder+import Haskoin.Transaction.Common as Common+import Haskoin.Transaction.Genesis as Genesis+import Haskoin.Transaction.Partial as Partial+import Haskoin.Transaction.Segwit as Segwit+import Haskoin.Transaction.Taproot as Taproot
+ src/Haskoin/Transaction/Builder.hs view
@@ -0,0 +1,624 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Transaction.Builder+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Code to simplify transaction creation, signing, fee calculation and coin+-- selection.+module Haskoin.Transaction.Builder+  ( -- * Transaction Builder+    buildAddrTx,+    buildTx,+    buildInput,+    SigInput (..),+    signTx,+    signNestedWitnessTx,+    makeSignature,+    signInput,+    signNestedInput,+    verifyStdTx,+    mergeTxs,+    sigKeys,+    mergeTxInput,+    findSigInput,+    verifyStdInput,++    -- * Coin Selection+    Coin (..),+    chooseCoins,+    chooseCoinsSink,+    chooseMSCoins,+    chooseMSCoinsSink,+    countMulSig,+    greedyAddSink,+    guessTxFee,+    guessMSTxFee,+    guessTxSize,+    guessMSSize,+  )+where++import Control.Applicative ((<|>))+import Control.Arrow (first)+import Control.Monad (foldM, unless)+import Control.Monad.Identity (runIdentity)+import Crypto.Secp256k1+import Data.ByteString qualified as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Conduit (ConduitT, Void, await, runConduit, (.|))+import Data.Conduit.List (sourceList)+import Data.Either (fromRight, rights)+import Data.List (nub)+import Data.Maybe (catMaybes, fromJust, isJust)+import Data.String.Conversions (cs)+import Data.Text (Text)+import Data.Word (Word64)+import Haskoin.Address+import Haskoin.Crypto.Hash (Hash256, addressHash)+import Haskoin.Crypto.Keys.Common+import Haskoin.Crypto.Signature+import Haskoin.Network.Common+import Haskoin.Network.Data+import Haskoin.Script+import Haskoin.Transaction.Builder.Sign (SigInput, buildInput, makeSignature, sigKeys)+import Haskoin.Transaction.Builder.Sign qualified as Sign+import Haskoin.Transaction.Common+import Haskoin.Transaction.Segwit+import Haskoin.Util++-- | Any type can be used as a Coin if it can provide a value in Satoshi.+-- The value is used in coin selection algorithms.+class Coin c where+  coinValue :: c -> Word64++-- | Coin selection algorithm for normal (non-multisig) transactions. This+-- function returns the selected coins together with the amount of change to+-- send back to yourself, taking the fee into account.+chooseCoins ::+  (Coin c) =>+  -- | value to send+  Word64 ->+  -- | fee per byte+  Word64 ->+  -- | number of outputs (including change)+  Int ->+  -- | try to find better solutions+  Bool ->+  -- | list of ordered coins to choose from+  [c] ->+  -- | coin selection and change+  Either String ([c], Word64)+chooseCoins target fee nOut continue coins =+  runIdentity . runConduit $+    sourceList coins .| chooseCoinsSink target fee nOut continue++-- | Coin selection algorithm for normal (non-multisig) transactions. This+-- function returns the selected coins together with the amount of change to+-- send back to yourself, taking the fee into account. This version uses a Sink+-- for conduit-based coin selection.+chooseCoinsSink ::+  (Monad m, Coin c) =>+  -- | value to send+  Word64 ->+  -- | fee per byte+  Word64 ->+  -- | number of outputs (including change)+  Int ->+  -- | try to find better solution+  Bool ->+  -- | coin selection and change+  ConduitT c Void m (Either String ([c], Word64))+chooseCoinsSink target fee nOut continue+  | target > 0 =+      maybeToEither err+        <$> greedyAddSink target (guessTxFee fee nOut) continue+  | otherwise = return $ Left "chooseCoins: Target must be > 0"+  where+    err = "chooseCoins: No solution found"++-- | Coin selection algorithm for multisig transactions. This function returns+-- the selected coins together with the amount of change to send back to+-- yourself, taking the fee into account. This function assumes all the coins+-- are script hash outputs that send funds to a multisignature address.+chooseMSCoins ::+  (Coin c) =>+  -- | value to send+  Word64 ->+  -- | fee per byte+  Word64 ->+  -- | m of n multisig+  (Int, Int) ->+  -- | number of outputs (including change)+  Int ->+  -- | try to find better solution+  Bool ->+  [c] ->+  -- | coin selection change amount+  Either String ([c], Word64)+chooseMSCoins target fee ms nOut continue coins =+  runIdentity . runConduit $+    sourceList coins .| chooseMSCoinsSink target fee ms nOut continue++-- | Coin selection algorithm for multisig transactions. This function returns+-- the selected coins together with the amount of change to send back to+-- yourself, taking the fee into account. This function assumes all the coins+-- are script hash outputs that send funds to a multisignature address. This+-- version uses a Sink if you need conduit-based coin selection.+chooseMSCoinsSink ::+  (Monad m, Coin c) =>+  -- | value to send+  Word64 ->+  -- | fee per byte+  Word64 ->+  -- | m of n multisig+  (Int, Int) ->+  -- | number of outputs (including change)+  Int ->+  -- | try to find better solution+  Bool ->+  -- | coin selection and change+  ConduitT c Void m (Either String ([c], Word64))+chooseMSCoinsSink target fee ms nOut continue+  | target > 0 =+      maybeToEither err+        <$> greedyAddSink target (guessMSTxFee fee ms nOut) continue+  | otherwise = return $ Left "chooseMSCoins: Target must be > 0"+  where+    err = "chooseMSCoins: No solution found"++-- | Select coins greedily by starting from an empty solution. If the 'continue'+-- flag is set, the algorithm will try to find a better solution in the stream+-- after a solution is found. If the next solution found is not strictly better+-- than the previously found solution, the algorithm stops and returns the+-- previous solution. If the continue flag is not set, the algorithm will return+-- the first solution it finds in the stream.+greedyAddSink ::+  (Monad m, Coin c) =>+  -- | value to send+  Word64 ->+  -- | coin count to fee function+  (Int -> Word64) ->+  -- | try to find better solutions+  Bool ->+  -- | coin selection and change+  ConduitT c Void m (Maybe ([c], Word64))+greedyAddSink target guessFee continue =+  go [] 0 [] 0+  where+    -- The goal is the value we must reach (including the fee) for a certain+    -- amount of selected coins.+    goal c = target + guessFee c+    go acc aTot ps pTot =+      await >>= \case+        -- A coin is available in the stream+        Just coin -> do+          let val = coinValue coin+          -- We have reached the goal using this coin+          if val + aTot >= goal (length acc + 1)+            then -- If we want to continue searching for better solutions++              if continue+                then -- This solution is the first one or+                -- This solution is better than the previous one++                  if pTot == 0 || val + aTot < pTot+                    then -- Continue searching for better solutions in the stream+                      go [] 0 (coin : acc) (val + aTot)+                    else -- Otherwise, we stop here and return the previous+                    -- solution+                      return $ Just (ps, pTot - goal (length ps))+                else -- Otherwise, return this solution++                  return $+                    Just (coin : acc, val + aTot - goal (length acc + 1))+            else -- We have not yet reached the goal. Add the coin to the+            -- accumulator+              go (coin : acc) (val + aTot) ps pTot+        -- We reached the end of the stream+        Nothing ->+          return $+            if null ps+              then -- If no solution was found, return Nothing+                Nothing+              else -- If we have a solution, return it+                Just (ps, pTot - goal (length ps))++-- | Estimate tranasction fee to pay based on transaction size estimation.+guessTxFee :: Word64 -> Int -> Int -> Word64+guessTxFee byteFee nOut nIn =+  byteFee * fromIntegral (guessTxSize nIn [] nOut 0)++-- | Same as 'guessTxFee' but for multisig transactions.+guessMSTxFee :: Word64 -> (Int, Int) -> Int -> Int -> Word64+guessMSTxFee byteFee ms nOut nIn =+  byteFee * fromIntegral (guessTxSize 0 (replicate nIn ms) nOut 0)++-- | Computes an upper bound on the size of a transaction based on some known+-- properties of the transaction.+guessTxSize ::+  -- | number of regular transaction inputs+  Int ->+  -- | multisig m of n for each input+  [(Int, Int)] ->+  -- | number of P2PKH outputs+  Int ->+  -- | number of P2SH outputs+  Int ->+  -- | upper bound on transaction size+  Int+guessTxSize pki msi pkout msout =+  8 + inpLen + inp + outLen + out+  where+    inpLen =+      B.length+        . runPutS+        . serialize+        . VarInt+        . fromIntegral+        $ length msi + pki+    outLen =+      B.length+        . runPutS+        . serialize+        . VarInt+        . fromIntegral+        $ pkout + msout+    inp = pki * 148 + sum (map guessMSSize msi)+    -- (20: hash160) + (5: opcodes) ++    -- (1: script len) + (8: Word64)+    out =+      pkout * 34+        ++        -- (20: hash160) + (3: opcodes) ++        -- (20: hash160) + (3: opcodes) ++        -- (20: hash160) + (3: opcodes) ++        -- (20: hash160) + (3: opcodes) ++        -- (1: script len) + (8: Word64)+        -- (1: script len) + (8: Word64)+        -- (1: script len) + (8: Word64)+        -- (1: script len) + (8: Word64)++        -- (20: hash160) + (3: opcodes) +++        -- (20: hash160) + (3: opcodes) ++        -- (1: script len) + (8: Word64)+        -- (1: script len) + (8: Word64)++        -- (20: hash160) + (3: opcodes) ++        -- (20: hash160) + (3: opcodes) ++        -- (1: script len) + (8: Word64)+        -- (1: script len) + (8: Word64)++        -- (20: hash160) + (3: opcodes) ++        -- (1: script len) + (8: Word64)+        msout * 32++-- | Size of a multisig P2SH input.+guessMSSize :: (Int, Int) -> Int+guessMSSize (m, n) =+  -- OutPoint (36) + Sequence (4) + Script+  40+    + fromIntegral (B.length $ runPutS . serialize $ VarInt $ fromIntegral scp)+    + scp+  where+    -- OP_M + n*PubKey + OP_N + OP_CHECKMULTISIG++    rdm =+      fromIntegral $+        B.length $+          runPutS $+            serialize $+              opPushData $+                B.replicate (n * 34 + 3) 0+    -- Redeem + m*sig + OP_0+    scp = rdm + m * 73 + 1++{- Build a new Tx -}++-- | Build a transaction by providing a list of outpoints as inputs+-- and a list of recipient addresses and amounts as outputs.+buildAddrTx :: Network -> Ctx -> [OutPoint] -> [(Text, Word64)] -> Either String Tx+buildAddrTx net ctx ops rcps =+  buildTx ctx ops <$> mapM f rcps+  where+    f (aTxt, v) =+      maybeToEither ("buildAddrTx: Invalid address " <> cs aTxt) $ do+        a <- textToAddr net aTxt+        let o = addressToOutput a+        return (o, v)++-- | Build a transaction by providing a list of outpoints as inputs+-- and a list of 'ScriptOutput' and amounts as outputs.+buildTx :: Ctx -> [OutPoint] -> [(ScriptOutput, Word64)] -> Tx+buildTx ctx ops rcpts =+  Tx 1 (toIn <$> ops) (toOut <$> rcpts) [] 0+  where+    toIn op = TxIn op B.empty maxBound+    toOut (o, v) = TxOut v $ marshal ctx o++-- | Sign a transaction by providing the 'SigInput' signing parameters and a+-- list of private keys. The signature is computed deterministically as defined+-- in RFC-6979.+--+-- Example: P2SH-P2WKH+--+-- > sigIn = SigInput (PayWitnessPKHash h) 100000 op sigHashAll Nothing+-- > signedTx = signTx btc unsignedTx [sigIn] [key]+--+-- Example: P2SH-P2WSH multisig+--+-- > sigIn = SigInput (PayWitnessScriptHash h) 100000 op sigHashAll (Just $ PayMulSig [p1,p2,p3] 2)+-- > signedTx = signTx btc unsignedTx [sigIn] [k1,k3]+signTx ::+  Network ->+  Ctx ->+  -- | transaction to sign+  Tx ->+  -- | signing parameters+  [SigInput] ->+  -- | private keys to sign with+  [SecKey] ->+  -- | signed transaction+  Either String Tx+signTx net ctx tx si = Sign.signTx net ctx tx $ notNested <$> si+  where+    notNested s = (s, False)++-- | This function differs from 'signTx' by assuming all segwit inputs are+-- P2SH-nested.  Use the same signing parameters for segwit inputs as in 'signTx'.+signNestedWitnessTx ::+  Network ->+  Ctx ->+  -- | transaction to sign+  Tx ->+  -- | signing parameters+  [SigInput] ->+  -- | private keys to sign with+  [SecKey] ->+  -- | signed transaction+  Either String Tx+signNestedWitnessTx net ctx tx si = Sign.signTx net ctx tx $ nested <$> si+  where+    -- NOTE: the nesting flag is ignored for non-segwit inputs+    nested s = (s, True)++-- | Sign a single input in a transaction deterministically (RFC-6979).+signInput ::+  Network -> Ctx -> Tx -> Int -> SigInput -> PrivateKey -> Either String Tx+signInput net ctx tx i si =+  Sign.signInput net ctx tx i (si, False)++-- | Like 'signInput' but treat segwit inputs as nested+signNestedInput ::+  Network -> Ctx -> Tx -> Int -> SigInput -> PrivateKey -> Either String Tx+signNestedInput net ctx tx i si =+  Sign.signInput net ctx tx i (si, True)++-- | Order the 'SigInput' with respect to the transaction inputs. This allows+-- the user to provide the 'SigInput' in any order. Users can also provide only+-- a partial set of 'SigInput' entries.+findSigInput :: [SigInput] -> [TxIn] -> [(SigInput, Int)]+findSigInput = Sign.findInputIndex (.outpoint)++{- Merge multisig transactions -}++-- | Merge partially-signed multisig transactions.  This function does not+-- support segwit and P2SH-segwit inputs.  Use PSBTs to merge transactions with+-- segwit inputs.+mergeTxs ::+  Network ->+  Ctx ->+  [Tx] ->+  [(ScriptOutput, Word64, OutPoint)] ->+  Either String Tx+mergeTxs net ctx txs os+  | null txs = Left "Transaction list is empty"+  | length (nub emptyTxs) /= 1 = Left "Transactions do not match"+  | length txs == 1 = return $ head txs+  | otherwise = foldM (mergeTxInput net ctx txs) (head emptyTxs) outs+  where+    zipOp = zip (matchTemplate os (head txs).inputs f) [0 ..]+    outs =+      map (first $ (\(o, v, _) -> (o, v)) . fromJust) $+        filter (isJust . fst) zipOp+    f (_, _, o) txin = o == txin.outpoint+    emptyTxs = map (\tx -> foldl clearInput tx outs) txs+    ins is i = updateIndex i is (\TxIn {..} -> TxIn {script = B.empty, ..})+    clearInput tx (_, i) =+      Tx tx.version (ins tx.inputs i) tx.outputs [] tx.locktime++-- | Merge input from partially-signed multisig transactions.  This function+-- does not support segwit and P2SH-segwit inputs.+mergeTxInput ::+  Network ->+  Ctx ->+  [Tx] ->+  Tx ->+  ((ScriptOutput, Word64), Int) ->+  Either String Tx+mergeTxInput net ctx txs tx ((so, val), i) = do+  -- Ignore transactions with empty inputs+  let ins = map ((.script) . (!! i) . (.inputs)) txs+  sigRes <- mapM extractSigs $ filter (not . B.null) ins+  let rdm = snd $ head sigRes+  unless (all ((== rdm) . snd) sigRes) $ Left "Redeem scripts do not match"+  si <- marshal (net, ctx) <$> go (nub $ concatMap fst sigRes) so rdm+  let ins' = updateIndex i tx.inputs (\TxIn {..} -> TxIn {script = si, ..})+  return $ Tx tx.version ins' tx.outputs [] tx.locktime+  where+    go allSigs out rdmM =+      case out of+        PayMulSig msPubs r ->+          let sigs =+                take r $+                  catMaybes $+                    matchTemplate allSigs msPubs $+                      f out+           in return $ RegularInput $ SpendMulSig sigs+        PayScriptHash _ ->+          case rdmM of+            Just rdm -> do+              si <- go allSigs rdm Nothing+              return $ ScriptHashInput si.get rdm+            _ -> Left "Invalid output script type"+        _ -> Left "Invalid output script type"+    extractSigs si =+      case unmarshal (net, ctx) si of+        Right (RegularInput (SpendMulSig sigs)) ->+          Right (sigs, Nothing)+        Right (ScriptHashInput (SpendMulSig sigs) rdm) ->+          Right (sigs, Just rdm)+        _ -> Left "Invalid script input type"+    f out (TxSignature x sh) p =+      verifyHashSig+        ctx+        (txSigHash net tx (encodeOutput ctx out) val i sh)+        x+        p.point+    f _ TxSignatureEmpty _ = False++{- Tx verification -}++-- | Verify if a transaction is valid and all of its inputs are standard.+verifyStdTx ::+  Network -> Ctx -> Tx -> [(ScriptOutput, Word64, OutPoint)] -> Bool+verifyStdTx net ctx tx xs =+  not (null tx.inputs) && all go (zip (matchTemplate xs tx.inputs f) [0 ..])+  where+    f (_, _, o) txin = o == txin.outpoint+    go (Just (so, val, _), i) = verifyStdInput net ctx tx i so val+    go _ = False++-- | Verify if a transaction input is valid and standard.+verifyStdInput :: Network -> Ctx -> Tx -> Int -> ScriptOutput -> Word64 -> Bool+verifyStdInput net ctx tx i so0 val+  | isSegwit so0 =+      fromRight False $ (inp == mempty &&) . verifySegwitInput so0 <$> wp so0+  | otherwise =+      or $+        rights+          [ verifyLegacyInput so0 <$> unmarshal (net, ctx) inp,+            nestedScriptOutput >>= \so -> verifyNestedInput so0 so <$> wp so+          ]+  where+    inp = (tx.inputs !! i).script+    theTxSigHash so = Sign.makeSigHash net ctx tx i so val++    ws :: WitnessStack+    ws+      | length tx.witness > i = tx.witness !! i+      | otherwise = []++    wp :: ScriptOutput -> Either String (Maybe ScriptOutput, SimpleInput)+    wp so = decodeWitnessInput net ctx =<< viewWitnessProgram net ctx so ws++    nestedScriptOutput :: Either String ScriptOutput+    nestedScriptOutput =+      runGetS deserialize inp >>= dec . ops+      where+        ops (Script ops') = ops'+        dec = \case+          [OP_PUSHDATA bs _] -> unmarshal ctx bs+          _ -> Left "nestedScriptOutput: not a nested output"++    verifyLegacyInput :: ScriptOutput -> ScriptInput -> Bool+    verifyLegacyInput so si = case (so, si) of+      (PayPK pub, RegularInput (SpendPK (TxSignature sig sh))) ->+        verifyHashSig ctx (theTxSigHash so sh Nothing) sig pub.point+      (PayPKHash h, RegularInput (SpendPKHash (TxSignature sig sh) pub)) ->+        pubKeyAddr ctx pub == p2pkhAddr h+          && verifyHashSig ctx (theTxSigHash so sh Nothing) sig pub.point+      (PayMulSig pubs r, RegularInput (SpendMulSig sigs)) ->+        countMulSig net ctx tx out val i ((.point) <$> pubs) sigs == r+      (PayScriptHash h, ScriptHashInput si' rdm) ->+        payToScriptAddress ctx rdm == p2shAddr h && verifyLegacyInput rdm (RegularInput si')+      _ -> False+      where+        out = encodeOutput ctx so++    verifySegwitInput ::+      ScriptOutput -> (Maybe ScriptOutput, SimpleInput) -> Bool+    verifySegwitInput so (rdm, si) = case (so, rdm, si) of+      ( PayWitnessPKHash h,+        Nothing,+        SpendPKHash (TxSignature sig sh) pub+        ) ->+          let keytest = pubKeyWitnessAddr ctx pub == p2wpkhAddr h+              sighash = theTxSigHash so sh Nothing+              pkpoint = pub.point+              verify = verifyHashSig ctx sighash sig pkpoint+           in keytest && verify+      ( PayWitnessScriptHash h,+        Just rdm'@(PayPK pub),+        SpendPK (TxSignature sig sh)+        ) ->+          let keytest = payToWitnessScriptAddress ctx rdm' == p2wshAddr h+              sighash = theTxSigHash so sh $ Just rdm'+              pkpoint = pub.point+              verify = verifyHashSig ctx sighash sig pkpoint+           in keytest && verify+      ( PayWitnessScriptHash h,+        Just rdm'@(PayPKHash kh),+        SpendPKHash (TxSignature sig sh) pub+        ) ->+          let keytest = payToWitnessScriptAddress ctx rdm' == p2wshAddr h+              addrtest = addressHash (marshal ctx pub) == kh+              pkpoint = pub.point+              sighash = theTxSigHash so sh $ Just rdm'+              verify = verifyHashSig ctx sighash sig pkpoint+           in keytest && addrtest && verify+      ( PayWitnessScriptHash h,+        Just rdm'@(PayMulSig pubs r),+        SpendMulSig sigs+        ) ->+          let keytest = payToWitnessScriptAddress ctx rdm' == p2wshAddr h+              pkpoints = (.point) <$> pubs+              hashfun sh = theTxSigHash so sh $ Just rdm'+              verify = countMulSig' ctx hashfun pkpoints sigs == r+           in keytest && verify+      _ -> False++    verifyNestedInput ::+      ScriptOutput -> ScriptOutput -> (Maybe RedeemScript, SimpleInput) -> Bool+    verifyNestedInput so so' x = case so of+      PayScriptHash h -> payToScriptAddress ctx so' == p2shAddr h && verifySegwitInput so' x+      _ -> False++-- | Count the number of valid signatures for a multi-signature transaction.+countMulSig ::+  Network ->+  Ctx ->+  Tx ->+  Script ->+  Word64 ->+  Int ->+  [PubKey] ->+  [TxSignature] ->+  Int+countMulSig net ctx tx out val i =+  countMulSig' ctx h+  where+    h = txSigHash net tx out val i++countMulSig' :: Ctx -> (SigHash -> Hash256) -> [PubKey] -> [TxSignature] -> Int+countMulSig' _ _ [] _ = 0+countMulSig' _ _ _ [] = 0+countMulSig' ctx h (_ : pubs) (TxSignatureEmpty : sigs) =+  countMulSig' ctx h pubs sigs+countMulSig' ctx h (pub : pubs) sigs@(TxSignature sig sh : sigs')+  | verifyHashSig ctx (h sh) sig pub = 1 + countMulSig' ctx h pubs sigs'+  | otherwise = countMulSig' ctx h pubs sigs
+ src/Haskoin/Transaction/Builder/Sign.hs view
@@ -0,0 +1,322 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Transaction.Builder.Sign+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Types and logic for signing transactions.+module Haskoin.Transaction.Builder.Sign+  ( SigInput (..),+    makeSignature,+    makeSigHash,+    signTx,+    findInputIndex,+    signInput,+    buildInput,+    sigKeys,+  )+where++import Control.DeepSeq+import Control.Monad+import Crypto.Secp256k1+import Data.Aeson+import Data.Aeson.Encoding+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Either+import Data.Hashable+import Data.List+import Data.Maybe+import Data.Word+import GHC.Generics+import Haskoin.Address+import Haskoin.Crypto.Hash+import Haskoin.Crypto.Keys.Common+import Haskoin.Crypto.Signature+import Haskoin.Network.Data+import Haskoin.Script+import Haskoin.Transaction.Common+import Haskoin.Transaction.Segwit+import Haskoin.Util++-- | Data type used to specify the signing parameters of a transaction input.+-- To sign an input, the previous output script, outpoint and sighash are+-- required. When signing a pay to script hash output, an additional redeem+-- script is required.+data SigInput = SigInput+  { -- | output script to spend+    -- ^ output script value+    script :: !ScriptOutput,+    -- | output script value+    -- ^ outpoint to spend+    value :: !Word64,+    -- | outpoint to spend+    -- ^ signature type+    outpoint :: !OutPoint,+    -- | signature type+    -- ^ redeem script+    sighash :: !SigHash,+    -- | redeem script+    redeem :: !(Maybe RedeemScript)+  }+  deriving (Show, Read, Eq, Generic, NFData)++instance MarshalJSON Ctx SigInput where+  marshalValue ctx (SigInput s v o h r) =+    object $+      [ "pkscript" .= marshalValue ctx s,+        "value" .= v,+        "outpoint" .= o,+        "sighash" .= h+      ]+        ++ [ "redeem" .= marshalValue ctx r+             | r <- maybeToList r+           ]++  marshalEncoding ctx (SigInput s v o h r) =+    pairs $+      mconcat+        [ "pkscript" `pair` marshalEncoding ctx s,+          "value" `pair` word64 v,+          "outpoint" `pair` toEncoding o,+          "sighash" `pair` toEncoding h,+          maybe mempty (pair "redeem" . marshalEncoding ctx) r+        ]++  unmarshalValue ctx =+    withObject "SigInput" $ \o ->+      SigInput+        <$> (unmarshalValue ctx =<< o .: "pkscript")+        <*> o .: "value"+        <*> o .: "outpoint"+        <*> o .: "sighash"+        <*> (mapM (unmarshalValue ctx) =<< o .:? "redeem")++-- | Sign a transaction by providing the 'SigInput' signing parameters and a+-- list of private keys. The signature is computed deterministically as defined+-- in RFC-6979.+signTx ::+  Network ->+  Ctx ->+  -- | transaction to sign+  Tx ->+  -- | signing parameters, with nesting flag+  [(SigInput, Bool)] ->+  -- | private keys to sign with+  [SecKey] ->+  -- | signed transaction+  Either String Tx+signTx net ctx otx sigis allKeys+  | null ti = Left "signTx: Transaction has no inputs"+  | otherwise = foldM go otx $ findInputIndex ((.outpoint) . fst) sigis ti+  where+    ti = otx.inputs+    go tx (sigi@(SigInput so _ _ _ rdmM, _), i) = do+      keys <- sigKeys ctx so rdmM allKeys+      foldM (\t k -> signInput net ctx t i sigi k) tx keys++-- | Sign a single input in a transaction deterministically (RFC-6979).  The+-- nesting flag only affects the behavior of segwit inputs.+signInput ::+  Network ->+  Ctx ->+  Tx ->+  Int ->+  -- | boolean flag: nest input+  (SigInput, Bool) ->+  PrivateKey ->+  Either String Tx+signInput net ctx tx i (sigIn@(SigInput so val _ _ rdmM), nest) key = do+  let sig = makeSignature net ctx tx i sigIn key+  si <- buildInput net ctx tx i so val rdmM sig $ derivePublicKey ctx key+  w <- updatedWitnessData net ctx tx i so si+  return tx {inputs = nextTxIn so si, witness = w}+  where+    f si TxIn {..} = TxIn {script = marshal (net, ctx) si, ..}+    g so' TxIn {..} = TxIn {script = pkScript so', ..}+    pkScript so' = runPutS . serialize . opPushData $ marshal ctx so'+    nextTxIn so' si+      | isSegwit so' && nest = updateIndex i tx.inputs (g so')+      | isSegwit so' = tx.inputs+      | otherwise = updateIndex i tx.inputs (f si)++-- | Add the witness data of the transaction given segwit parameters for an input.+--+-- @since 0.11.0.0+updatedWitnessData ::+  Network ->+  Ctx ->+  Tx ->+  Int ->+  ScriptOutput ->+  ScriptInput ->+  Either String WitnessData+updatedWitnessData net ctx tx i so si+  | isSegwit so =+      updateWitness . toWitnessStack net ctx =<< calcWitnessProgram net ctx so si+  | otherwise =+      return tx.witness+  where+    updateWitness w+      | null tx.witness = return $ updateIndex i defaultStack (const w)+      | length tx.witness /= n = Left "Invalid number of witness stacks"+      | otherwise = return $ updateIndex i tx.witness (const w)+    defaultStack = replicate n $ toWitnessStack net ctx EmptyWitnessProgram+    n = length tx.inputs++-- | Associate an input index to each value in a list+findInputIndex ::+  -- | extract an outpoint+  (a -> OutPoint) ->+  -- | input list+  [a] ->+  -- | reference list of inputs+  [TxIn] ->+  [(a, Int)]+findInputIndex getOutPoint as ti =+  mapMaybe g $ zip (matchTemplate as ti f) [0 ..]+  where+    f s txin = getOutPoint s == txin.outpoint+    g (Just s, i) = Just (s, i)+    g (Nothing, _) = Nothing++-- | Find from the list of provided private keys which one is required to sign+-- the 'ScriptOutput'.+sigKeys ::+  Ctx ->+  ScriptOutput ->+  Maybe RedeemScript ->+  [SecKey] ->+  Either String [PrivateKey]+sigKeys ctx so rdmM keys =+  case (so, rdmM) of+    (PayPK p, Nothing) ->+      return . map fst . maybeToList $ find ((== p) . snd) zipKeys+    (PayPKHash h, Nothing) -> return $ keyByHash h+    (PayMulSig ps r, Nothing) ->+      return $ map fst $ take r $ filter ((`elem` ps) . snd) zipKeys+    (PayScriptHash _, Just rdm) -> sigKeys ctx rdm Nothing keys+    (PayWitnessPKHash h, _) -> return $ keyByHash h+    (PayWitnessScriptHash _, Just rdm) -> sigKeys ctx rdm Nothing keys+    _ -> Left "sigKeys: Could not decode output script"+  where+    zipKeys =+      [ (prv, pub)+        | k <- keys,+          t <- [True, False],+          let prv = wrapSecKey t k,+          let pub = derivePublicKey ctx prv+      ]+    keyByHash h = fmap fst . maybeToList . findKey h $ zipKeys+    findKey h = find $ (== h) . (.hash160) . pubKeyAddr ctx . snd++-- | Construct an input for a transaction given a signature, public key and data+-- about the previous output.+buildInput ::+  Network ->+  Ctx ->+  -- | transaction where input will be added+  Tx ->+  -- | input index where signature will go+  Int ->+  -- | output script being spent+  ScriptOutput ->+  -- | amount of previous output+  Word64 ->+  -- | redeem script if pay-to-script-hash+  Maybe RedeemScript ->+  TxSignature ->+  PublicKey ->+  Either String ScriptInput+buildInput net ctx tx i so val rdmM sig pub = do+  when (i >= length tx.inputs) $ Left "buildInput: Invalid input index"+  case (so, rdmM) of+    (PayScriptHash _, Just rdm) ->+      buildScriptHashInput rdm+    (PayWitnessScriptHash _, Just rdm) ->+      buildScriptHashInput rdm+    (PayWitnessPKHash _, Nothing) ->+      return . RegularInput $ SpendPKHash sig pub+    (_, Nothing) ->+      buildRegularInput so+    _ -> Left "buildInput: Invalid output/redeem script combination"+  where+    buildRegularInput = \case+      PayPK _ -> return $ RegularInput $ SpendPK sig+      PayPKHash _ -> return $ RegularInput $ SpendPKHash sig pub+      PayMulSig msPubs r -> do+        let mSigs = take r $ catMaybes $ matchTemplate allSigs msPubs f+            allSigs = nub $ sig : parseExistingSigs net ctx tx so i+        return $ RegularInput $ SpendMulSig mSigs+      _ -> Left "buildInput: Invalid output/redeem script combination"+    buildScriptHashInput rdm = do+      inp <- buildRegularInput rdm+      return $ ScriptHashInput inp.get rdm+    f (TxSignature x sh) p =+      verifyHashSig+        ctx+        (makeSigHash net ctx tx i so val sh rdmM)+        x+        p.point+    f TxSignatureEmpty _ = False++-- | Apply heuristics to extract the signatures for a particular input that are+-- embedded in the transaction.+--+-- @since 0.11.0.0+parseExistingSigs :: Network -> Ctx -> Tx -> ScriptOutput -> Int -> [TxSignature]+parseExistingSigs net ctx tx so i = insSigs <> witSigs+  where+    insSigs = case unmarshal (net, ctx) scp of+      Right (ScriptHashInput (SpendMulSig xs) _) -> xs+      Right (RegularInput (SpendMulSig xs)) -> xs+      _ -> []+    scp = (tx.inputs !! i).script+    witSigs+      | not $ isSegwit so = []+      | null tx.witness = []+      | otherwise = rights $ decodeTxSig net ctx <$> (tx.witness !! i)++-- | Produce a structured representation of a deterministic (RFC-6979) signature over an input.+makeSignature :: Network -> Ctx -> Tx -> Int -> SigInput -> PrivateKey -> TxSignature+makeSignature net ctx tx i (SigInput so val _ sh rdmM) key =+  TxSignature (signHash ctx key.key m) sh+  where+    m = makeSigHash net ctx tx i so val sh rdmM++-- | A function which selects the digest algorithm and parameters as appropriate+--+-- @since 0.11.0.0+makeSigHash ::+  Network ->+  Ctx ->+  Tx ->+  Int ->+  ScriptOutput ->+  Word64 ->+  SigHash ->+  Maybe RedeemScript ->+  Hash256+makeSigHash net ctx tx i so val sh rdmM = h net tx (encodeOutput ctx so') val i sh+  where+    so' = case so of+      PayWitnessPKHash h' -> PayPKHash h'+      _ -> fromMaybe so rdmM+    h+      | isSegwit so = txSigHashForkId+      | otherwise = txSigHash
+ src/Haskoin/Transaction/Common.hs view
@@ -0,0 +1,429 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Transaction.Common+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Code related to transactions parsing and serialization.+module Haskoin.Transaction.Common+  ( -- * Transactions+    Tx (..),+    TxIn (..),+    TxOut (..),+    OutPoint (..),+    TxHash (..),+    WitnessData,+    WitnessStack,+    WitnessStackItem,+    txHash,+    hexToTxHash,+    txHashToHex,+    nosigTxHash,+    nullOutPoint,+  )+where++import Control.Applicative ((<|>))+import Control.DeepSeq+import Control.Monad (forM_, guard, liftM2, mzero, replicateM, unless, when, (<=<))+import Data.Aeson as A+import Data.Aeson.Encoding qualified as E+import Data.Binary (Binary (..))+import Data.Bool (bool)+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.ByteString.Builder (char7)+import Data.ByteString.Lazy qualified as BL+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Hashable (Hashable)+import Data.Maybe (fromMaybe)+import Data.Serialize (Serialize (..))+import Data.String (IsString, fromString)+import Data.String.Conversions (cs)+import Data.Text (Text)+import Data.Word (Word32, Word64)+import GHC.Generics (Generic)+import Haskoin.Crypto.Hash+import Haskoin.Network.Common+import Haskoin.Util.Helpers+import Text.Read as R++-- | Transaction id: hash of transaction excluding witness data.+newtype TxHash = TxHash {get :: Hash256}+  deriving (Eq, Ord, Generic)+  deriving newtype (Hashable, NFData)++instance Serial TxHash where+  serialize (TxHash h) = serialize h+  deserialize = TxHash <$> deserialize++instance Serialize TxHash where+  put = serialize+  get = deserialize++instance Binary TxHash where+  put = serialize+  get = deserialize++instance Show TxHash where+  showsPrec _ = shows . txHashToHex++instance Read TxHash where+  readPrec = do+    R.String str <- R.lexP+    maybe R.pfail return $ hexToTxHash $ cs str++instance IsString TxHash where+  fromString s =+    let e = error "Could not read transaction hash from hex string"+     in fromMaybe e $ hexToTxHash $ cs s++instance FromJSON TxHash where+  parseJSON =+    withText "txid" $+      maybe mzero return . hexToTxHash++instance ToJSON TxHash where+  toJSON = A.String . txHashToHex+  toEncoding = hexEncoding . BL.reverse . runPutL . serialize++-- | Transaction hash excluding signatures.+nosigTxHash :: Tx -> TxHash+nosigTxHash Tx {..} =+  TxHash . doubleSHA256 . runPutS $ serialize tx+  where+    tx = Tx {inputs = map clr inputs, ..}+    clr TxIn {..} = TxIn {script = B.empty, ..}++-- | Convert transaction hash to hex form, reversing bytes.+txHashToHex :: TxHash -> Text+txHashToHex (TxHash h) =+  encodeHex . B.reverse . runPutS $ serialize h++-- | Convert transaction hash from hex, reversing bytes.+hexToTxHash :: Text -> Maybe TxHash+hexToTxHash hex = do+  bs <- B.reverse <$> decodeHex hex+  h <- either (const Nothing) Just (runGetS deserialize bs)+  return $ TxHash h++-- | Witness stack for SegWit transactions.+type WitnessData = [WitnessStack]++-- | Witness stack for SegWit transactions.+type WitnessStack = [WitnessStackItem]++-- | Witness stack item for SegWit transactions.+type WitnessStackItem = ByteString++-- | Data type representing a transaction.+data Tx = Tx+  { -- | transaction data format version+    version :: !Word32,+    -- | list of transaction inputs+    inputs :: ![TxIn],+    -- | list of transaction outputs+    outputs :: ![TxOut],+    -- | witness data for the transaction+    witness :: !WitnessData,+    -- | earliest mining height or time+    locktime :: !Word32+  }+  deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)++-- | Compute transaction hash.+txHash :: Tx -> TxHash+txHash tx = TxHash . doubleSHA256 . runPutS $ serialize tx {witness = []}++instance IsString Tx where+  fromString =+    fromMaybe e . (eitherToMaybe . runGetS deserialize <=< decodeHex) . cs+    where+      e = error "Could not read transaction from hex string"++instance Serial Tx where+  deserialize = isWitnessTx >>= bool parseLegacyTx parseWitnessTx+  serialize tx+    | null tx.witness = putLegacyTx tx+    | otherwise = putWitnessTx tx++instance Binary Tx where+  put = serialize+  get = deserialize++instance Serialize Tx where+  put = serialize+  get = deserialize++putInOut :: (MonadPut m) => Tx -> m ()+putInOut tx = do+  putVarInt $ length tx.inputs+  mapM_ serialize tx.inputs+  putVarInt $ length tx.outputs+  mapM_ serialize tx.outputs++-- | Non-SegWit transaction serializer.+putLegacyTx :: (MonadPut m) => Tx -> m ()+putLegacyTx tx = do+  putWord32le tx.version+  putInOut tx+  putWord32le tx.locktime++-- | Witness transaciton serializer.+putWitnessTx :: (MonadPut m) => Tx -> m ()+putWitnessTx tx = do+  putWord32le tx.version+  putWord8 0x00+  putWord8 0x01+  putInOut tx+  putWitnessData tx.witness+  putWord32le tx.locktime++isWitnessTx :: (MonadGet m) => m Bool+isWitnessTx = lookAhead $ do+  _ <- getWord32le+  m <- getWord8+  f <- getWord8+  return (m == 0x00 && f == 0x01)++-- | Non-SegWit transaction deseralizer.+parseLegacyTx :: (MonadGet m) => m Tx+parseLegacyTx = do+  version <- getWord32le+  inputs <- rl =<< deserialize+  outputs <- rl =<< deserialize+  when (length inputs == 0x00 && length outputs == 0x01) $+    fail "Witness transaction"+  locktime <- getWord32le+  return Tx {witness = [], ..}+  where+    rl (VarInt c) = replicateM (fromIntegral c) deserialize++-- | Witness transaction deserializer.+parseWitnessTx :: (MonadGet m) => m Tx+parseWitnessTx = do+  version <- getWord32le+  m <- getWord8+  f <- getWord8+  unless (m == 0x00 && f == 0x01) $ fail "Not a witness transaction"+  inputs <- replicateList =<< deserialize+  outputs <- replicateList =<< deserialize+  witness <- parseWitnessData $ length inputs+  locktime <- getWord32le+  return Tx {..}+  where+    replicateList (VarInt c) = replicateM (fromIntegral c) deserialize++-- | Witness data deserializer. Requires count of inputs.+parseWitnessData :: (MonadGet m) => Int -> m WitnessData+parseWitnessData n = replicateM n parseWitnessStack+  where+    parseWitnessStack = do+      VarInt i <- deserialize+      replicateM (fromIntegral i) parseWitnessStackItem+    parseWitnessStackItem = do+      VarInt i <- deserialize+      getByteString $ fromIntegral i++-- | Witness data serializer.+putWitnessData :: (MonadPut m) => WitnessData -> m ()+putWitnessData = mapM_ putWitnessStack+  where+    putWitnessStack ws = do+      putVarInt $ length ws+      mapM_ putWitnessStackItem ws+    putWitnessStackItem bs = do+      putVarInt $ B.length bs+      putByteString bs++instance FromJSON Tx where+  parseJSON = withObject "Tx" $ \o ->+    Tx+      <$> o .: "version"+      <*> o .: "inputs"+      <*> o .: "outputs"+      <*> (mapM (mapM f) =<< o .: "witnessdata")+      <*> o .: "locktime"+    where+      f = maybe mzero return . decodeHex++instance ToJSON Tx where+  toJSON (Tx v i o w l) =+    object+      [ "version" .= v,+        "inputs" .= i,+        "outputs" .= o,+        "witnessdata" .= fmap (fmap encodeHex) w,+        "locktime" .= l+      ]+  toEncoding (Tx v i o w l) =+    pairs $+      mconcat+        [ "version" `E.pair` E.word32 v,+          "inputs" `E.pair` E.list toEncoding i,+          "outputs" `E.pair` E.list toEncoding o,+          "witnessdata" `E.pair` E.list (E.list f) w,+          "locktime" `E.pair` E.word32 l+        ]+    where+      f = hexEncoding . BL.fromStrict++-- | Data type representing a transaction input.+data TxIn = TxIn+  { -- | output being spent+    outpoint :: !OutPoint,+    -- | signatures and redeem script+    script :: !ByteString,+    -- | lock-time using sequence numbers (BIP-68)+    sequence :: !Word32+  }+  deriving (Eq, Show, Read, Ord, Generic, Hashable, NFData)++instance Serial TxIn where+  deserialize =+    TxIn <$> deserialize <*> (readBS =<< deserialize) <*> getWord32le+    where+      readBS (VarInt len) = getByteString $ fromIntegral len++  serialize (TxIn o s q) = do+    serialize o+    putVarInt $ B.length s+    putByteString s+    putWord32le q++instance Binary TxIn where+  get = deserialize+  put = serialize++instance Serialize TxIn where+  get = deserialize+  put = serialize++instance FromJSON TxIn where+  parseJSON =+    withObject "TxIn" $ \o ->+      TxIn+        <$> o .: "prevoutput"+        <*> (maybe mzero return . decodeHex =<< o .: "inputscript")+        <*> o .: "sequence"++instance ToJSON TxIn where+  toJSON (TxIn o s q) =+    object+      [ "prevoutput" .= o,+        "inputscript" .= encodeHex s,+        "sequence" .= q+      ]+  toEncoding (TxIn o s q) =+    pairs $+      mconcat+        [ "prevoutput" `E.pair` toEncoding o,+          "inputscript" `E.pair` hexEncoding (BL.fromStrict s),+          "sequence" `E.pair` E.word32 q+        ]++-- | Data type representing a transaction output.+data TxOut = TxOut+  { -- | value of output is satoshi+    value :: !Word64,+    -- | pubkey script+    script :: !ByteString+  }+  deriving (Eq, Show, Read, Ord, Generic, Hashable, NFData)++instance Serial TxOut where+  deserialize = do+    val <- getWord64le+    VarInt len <- deserialize+    TxOut val <$> getByteString (fromIntegral len)++  serialize (TxOut o s) = do+    putWord64le o+    putVarInt $ B.length s+    putByteString s++instance Binary TxOut where+  put = serialize+  get = deserialize++instance Serialize TxOut where+  put = serialize+  get = deserialize++instance FromJSON TxOut where+  parseJSON =+    withObject "TxOut" $ \o -> do+      value <- o .: "value"+      t <- o .: "outputscript"+      script <- maybe mzero return (decodeHex t)+      return TxOut {..}++instance ToJSON TxOut where+  toJSON (TxOut o s) =+    object ["value" .= o, "outputscript" .= encodeHex s]+  toEncoding (TxOut o s) =+    pairs $+      mconcat+        [ "value" `E.pair` E.word64 o,+          "outputscript" `E.pair` hexEncoding (BL.fromStrict s)+        ]++-- | The 'OutPoint' refers to a transaction output being spent.+data OutPoint = OutPoint+  { -- | hash of previous transaction+    hash :: !TxHash,+    -- | position of output in previous transaction+    index :: !Word32+  }+  deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)++instance Serial OutPoint where+  deserialize = do+    (h, i) <- liftM2 (,) deserialize getWord32le+    return $ OutPoint h i+  serialize (OutPoint h i) = serialize h >> putWord32le i++instance Binary OutPoint where+  put = serialize+  get = deserialize++instance Serialize OutPoint where+  put = serialize+  get = deserialize++instance FromJSON OutPoint where+  parseJSON =+    withObject "OutPoint" $ \o ->+      OutPoint <$> o .: "txid" <*> o .: "index"++instance ToJSON OutPoint where+  toJSON (OutPoint h i) = object ["txid" .= h, "index" .= i]+  toEncoding (OutPoint h i) =+    pairs $+      mconcat+        [ "txid" `E.pair` toEncoding h,+          "index" `E.pair` E.word32 i+        ]++-- | Outpoint used in coinbase transactions.+nullOutPoint :: OutPoint+nullOutPoint =+  OutPoint+    { hash = "0000000000000000000000000000000000000000000000000000000000000000",+      index = maxBound+    }
+ src/Haskoin/Transaction/Genesis.hs view
@@ -0,0 +1,42 @@+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module      : Haskoin.Transaction.Genesis+-- Copyright   : No rights reserved+-- License     : UNLICENSE+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Code related to transactions parsing and serialization.+module Haskoin.Transaction.Genesis (genesisTx) where++import Crypto.Secp256k1 (Ctx, importPubKey)+import Data.Bytes.Get (runGetS)+import Haskoin.Crypto.Keys.Common+import Haskoin.Script.Standard+import Haskoin.Transaction.Common+import Haskoin.Util++-- | Transaction from Genesis block.+genesisTx :: Ctx -> Tx+genesisTx ctx =+  Tx 1 [txin] [txout] [] locktime+  where+    txin = TxIn outpoint inputBS maxBound+    txout = TxOut 5000000000 (marshal ctx output)+    locktime = 0+    outpoint = OutPoint z maxBound+    Just inputBS =+      decodeHex+        "04ffff001d0104455468652054696d65732030332f4a616e2f323030392043686\+        \16e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f\+        \757420666f722062616e6b73"+    Just pubKeyBS =+      decodeHex+        "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb\+        \649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"+    Right pubKey =+      unmarshal ctx pubKeyBS+    output = PayPK pubKey+    z = "0000000000000000000000000000000000000000000000000000000000000000"
+ src/Haskoin/Transaction/Partial.hs view
@@ -0,0 +1,949 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Transaction.Partial+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : matt@bitnomial.com+-- Stability   : experimental+-- Portability : POSIX+--+-- Code related to PSBT parsing and serialization.+module Haskoin.Transaction.Partial+  ( -- * Partially-Signed Transactions+    PSBT (..),+    getPSBT,+    putPSBT,+    Input (..),+    getInput,+    putInput,+    Output (..),+    getOutput,+    putOutput,+    UnknownMap (..),+    Key (..),+    merge,+    mergeMany,+    mergeInput,+    mergeOutput,+    complete,+    finalTransaction,+    emptyPSBT,+    emptyInput,+    emptyOutput,++    -- ** Signing+    PSBTSigner,+    getSignerKey,+    secKeySigner,+    xPrvSigner,+    signPSBT,+  )+where++import Control.Applicative ((<|>))+import Control.DeepSeq (NFData)+import Control.Monad (foldM, guard, replicateM, unless, void)+import Crypto.Secp256k1+import Data.Binary (Binary (..))+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial (Serial (..))+import Data.Either (fromRight)+import Data.HashMap.Strict (HashMap)+import Data.HashMap.Strict qualified as HashMap+import Data.Hashable (Hashable)+import Data.List (foldl')+import Data.Maybe (fromMaybe, isJust)+import Data.Serialize (Get, Put, Serialize (..))+import Data.Serialize qualified as S+import Data.String.Conversions (cs)+import GHC.Generics (Generic)+import GHC.Word (Word32, Word8)+import Haskoin.Address (Address (..), pubKeyAddr)+import Haskoin.Crypto.Keys.Common+import Haskoin.Crypto.Keys.Extended+import Haskoin.Network.Common+import Haskoin.Network.Data+import Haskoin.Script.Common+import Haskoin.Script.SigHash+import Haskoin.Script.Standard+import Haskoin.Transaction.Builder.Sign+import Haskoin.Transaction.Common+import Haskoin.Transaction.Segwit+import Haskoin.Util+import Numeric (showHex)++-- | PSBT data type as specified in+-- [BIP-174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki).+-- This contains an unsigned transaction, inputs and outputs, and unspecified+-- extra data. There is one input per input in the unsigned transaction, and one+-- output per output in the unsigned transaction. The inputs and outputs in the+-- 'PSBT' line up by index with the inputs and outputs in+-- the unsigned transaction.+data PSBT = PSBT+  { unsignedTransaction :: Tx,+    globalUnknown :: UnknownMap,+    inputs :: [Input],+    outputs :: [Output]+  }+  deriving (Show, Read, Eq, Generic)+  deriving anyclass (NFData)++-- | Inputs contain all of the data needed to sign a transaction and all of the+-- resulting signature data after signing.+data Input = Input+  { nonWitnessUtxo :: Maybe Tx,+    witnessUtxo :: Maybe TxOut,+    partialSigs :: HashMap PublicKey ByteString,+    sigHashType :: Maybe SigHash,+    inputRedeemScript :: Maybe Script,+    inputWitnessScript :: Maybe Script,+    inputHDKeypaths :: HashMap PublicKey (Fingerprint, [KeyIndex]),+    finalScriptSig :: Maybe Script,+    finalScriptWitness :: Maybe WitnessStack,+    inputUnknown :: UnknownMap+  }+  deriving (Show, Read, Eq, Generic)+  deriving anyclass (NFData)++-- | Outputs can contain information needed to spend the output at a later date.+data Output = Output+  { outputRedeemScript :: Maybe Script,+    outputWitnessScript :: Maybe Script,+    outputHDKeypaths :: HashMap PublicKey (Fingerprint, [KeyIndex]),+    outputUnknown :: UnknownMap+  }+  deriving (Show, Read, Eq, Generic)+  deriving anyclass (NFData)++-- | A map of raw PSBT keys to byte strings for extra data. The 'keyType' field+-- cannot overlap with any of the reserved 'keyType' fields specified in the+-- PSBT specification.+newtype UnknownMap = UnknownMap {unknownMap :: HashMap Key ByteString}+  deriving (Show, Read, Eq, Generic)+  deriving newtype (Semigroup, Monoid, NFData)++-- | Raw keys for the map type used in PSBTs.+data Key = Key+  { keyType :: Word8,+    key :: ByteString+  }+  deriving (Show, Read, Eq, Generic, NFData, Hashable)++-- | Take two 'PSBT's and merge them. The+-- 'unsignedTransaction' field in both must be the same.+merge ::+  PSBT ->+  PSBT ->+  Maybe PSBT+merge psbt1 psbt2+  | psbt1.unsignedTransaction == psbt2.unsignedTransaction =+      Just $+        psbt1+          { globalUnknown = psbt1.globalUnknown <> psbt2.globalUnknown,+            inputs = zipWith mergeInput psbt1.inputs psbt2.inputs,+            outputs = zipWith mergeOutput psbt1.outputs psbt2.outputs+          }+merge _ _ = Nothing++-- | A version of 'merge' for a collection of PSBTs.+--+-- @since 0.21.0+mergeMany :: [PSBT] -> Maybe PSBT+mergeMany (psbt : psbts) = foldM merge psbt psbts+mergeMany _ = Nothing++mergeInput :: Input -> Input -> Input+mergeInput a b =+  Input+    { nonWitnessUtxo =+        if isJust witUtx+          then Nothing+          else a.nonWitnessUtxo <|> b.nonWitnessUtxo,+      witnessUtxo =+        witUtx,+      sigHashType =+        a.sigHashType <|> b.sigHashType,+      partialSigs =+        a.partialSigs <> b.partialSigs,+      inputHDKeypaths =+        a.inputHDKeypaths <> b.inputHDKeypaths,+      inputUnknown =+        a.inputUnknown <> b.inputUnknown,+      inputRedeemScript =+        a.inputRedeemScript <|> b.inputRedeemScript,+      inputWitnessScript =+        a.inputWitnessScript <|> b.inputWitnessScript,+      finalScriptSig =+        a.finalScriptSig <|> b.finalScriptSig,+      finalScriptWitness =+        a.finalScriptWitness <|> b.finalScriptWitness+    }+  where+    witUtx = a.witnessUtxo <|> b.witnessUtxo++mergeOutput :: Output -> Output -> Output+mergeOutput a b =+  Output+    { outputRedeemScript =+        a.outputRedeemScript <|> b.outputRedeemScript,+      outputWitnessScript =+        a.outputWitnessScript <|> b.outputWitnessScript,+      outputHDKeypaths =+        a.outputHDKeypaths <> b.outputHDKeypaths,+      outputUnknown =+        a.outputUnknown <> b.outputUnknown+    }++-- | A abstraction which covers varying key configurations.  Use the 'Semigroup'+-- instance to create signers for sets of keys: `signerA <> signerB` can sign+-- anything for which `signerA` or `signerB` could sign.+--+-- @since 0.21@+newtype PSBTSigner = PSBTSigner+  { unPSBTSigner ::+      PublicKey ->+      Maybe (Fingerprint, DerivPath) ->+      Maybe SecKey+  }++instance Semigroup PSBTSigner where+  PSBTSigner signer1 <> PSBTSigner signer2 =+    PSBTSigner $ \pubKey origin ->+      signer1 pubKey origin <|> signer2 pubKey origin++instance Monoid PSBTSigner where+  mempty = PSBTSigner $ \_ _ -> Nothing++-- | Fetch the secret key for the given 'PublicKey' if possible.+--+-- @since 0.21@+getSignerKey :: PSBTSigner -> PublicKey -> Maybe (Fingerprint, DerivPath) -> Maybe SecKey+getSignerKey = (.unPSBTSigner)++-- | This signer can sign for one key.+--+-- @since 0.21@+secKeySigner :: Ctx -> SecKey -> PSBTSigner+secKeySigner ctx theSecKey =+  PSBTSigner signer+  where+    signer requiredKey _+      | requiredKey.point == derivePubKey ctx theSecKey = Just theSecKey+      | otherwise = Nothing++-- | This signer can sign with any child key, provided that derivation information is present.+--+-- @since 0.21@+xPrvSigner ::+  Ctx ->+  XPrvKey ->+  -- | Origin data, if the input key is explicitly a child key+  Maybe (Fingerprint, DerivPath) ->+  PSBTSigner+xPrvSigner ctx xprv origin = PSBTSigner signer+  where+    signer pubKey (Just hdData)+      | result@(Just theSecKey) <- maybe noOrigin onOrigin origin hdData,+        pubKey.point == derivePubKey ctx theSecKey =+          result+    signer _ _ = Nothing++    noOrigin (fp, path)+      | thisFP == fp = Just $ deriveSecKey path+      | otherwise = Nothing++    onOrigin (originFP, originPath) (fp, path)+      | thisFP == fp = Just $ deriveSecKey path+      | originFP == fp =+          deriveSecKey <$> adjustPath (pathToList originPath) (pathToList path)+      | otherwise = Nothing++    deriveSecKey path = (derivePath ctx path xprv).key++    thisFP = xPubFP ctx $ deriveXPubKey ctx xprv++    -- The origin path should be a prefix of the target path if we match the+    -- origin fingerprint.  We need to remove this prefix.+    adjustPath :: [KeyIndex] -> [KeyIndex] -> Maybe DerivPath+    adjustPath (originIx : originTail) (thisIx : thisTail)+      | originIx == thisIx = adjustPath originTail thisTail+      | otherwise = Nothing+    adjustPath [] thePath = Just $ listToPath thePath+    adjustPath _ _ = Nothing++-- | Update a PSBT with signatures when possible.  This function uses+-- 'inputHDKeypaths' in order to calculate secret keys.+--+-- @since 0.21@+signPSBT ::+  Network ->+  Ctx ->+  PSBTSigner ->+  PSBT ->+  PSBT+signPSBT net ctx signer PSBT {..} =+  PSBT {inputs = f <$> zip [0 ..] inputs, ..}+  where+    f = addSigsForInput net ctx signer unsignedTransaction++addSigsForInput :: Network -> Ctx -> PSBTSigner -> Tx -> (Int, Input) -> Input+addSigsForInput net ctx signer tx (ix, input) =+  maybe input (onPrevTxOut net ctx signer tx ix input) $+    Left <$> input.nonWitnessUtxo <|> Right <$> input.witnessUtxo++onPrevTxOut ::+  Network ->+  Ctx ->+  PSBTSigner ->+  Tx ->+  Int ->+  Input ->+  Either Tx TxOut ->+  Input+onPrevTxOut net ctx signer tx ix input prevTxData =+  input+    { partialSigs = newSigs <> input.partialSigs+    }+  where+    newSigs = HashMap.mapWithKey sigForInput sigKeys+    sigForInput thePubKey theSecKey =+      encodeTxSig net ctx . makeSignature net ctx tx ix theSigInput $+        PrivateKey theSecKey thePubKey.compress++    theSigInput =+      SigInput+        { -- Must be the segwit input script for segwit spends (even nested)+          script = fromMaybe theInputScript segwitInput,+          value = prevTxOut.value,+          outpoint = thePrevOutPoint,+          sighash = fromMaybe sigHashAll input.sigHashType,+          -- Must be the witness script for segwit spends (even nested)+          redeem = theWitnessScript <|> theRedeemScript+        }++    prevTxOut =+      let rf = ((!! (fromIntegral . (.index)) thePrevOutPoint) . (.outputs))+       in either rf id prevTxData+    thePrevOutPoint = (tx.inputs !! ix).outpoint++    segwitInput = justWhen isSegwit theInputScript <|> (justWhen isSegwit =<< theRedeemScript)++    theInputScript = fromRight inputScriptErr $ (unmarshal ctx . (.script)) prevTxOut+    inputScriptErr = error "addSigsForInput: Unable to decode input script"++    theRedeemScript = case decodeOutput ctx <$> input.inputRedeemScript of+      Just (Right script) -> Just script+      Just Left {} -> error "addSigsForInput: Unable to decode redeem script"+      _ -> Nothing++    theWitnessScript = case decodeOutput ctx <$> input.inputWitnessScript of+      Just (Right script) -> Just script+      Just Left {} -> error "addSigsForInput: Unable to decode witness script"+      _ -> Nothing++    sigKeys = HashMap.mapMaybeWithKey getSignerKey input.inputHDKeypaths+    getSignerKey pubKey (fp, ixs) = (.unPSBTSigner) signer pubKey $ Just (fp, listToPath ixs)++-- | Take partial signatures from all of the 'Input's and finalize the signature.+complete ::+  Ctx ->+  PSBT ->+  PSBT+complete ctx PSBT {..} =+  PSBT {inputs = map (completeInput . analyzeInputs) (indexed inputs), ..}+  where+    analyzeInputs (i, input) =+      (,)+        (outputScript =<< input.witnessUtxo <|> nonWitScript)+        input+      where+        nonWitScript = getPrevOut i =<< input.nonWitnessUtxo++    getPrevOut i tx =+      (tx.outputs !!?) . fromIntegral . (.outpoint.index)+        =<< unsignedTransaction.inputs !!? i+    xs !!? i = lookup i $ indexed xs++    outputScript = eitherToMaybe . unmarshal ctx . (.script)++    completeInput (Nothing, input) = input+    completeInput (Just script, input) = pruneInputFields $ completeSig ctx input script++    -- If we have final scripts, we can get rid of data for signing following+    -- the Bitcoin Core implementation.+    pruneInputFields input+      | isJust input.finalScriptSig || isJust input.finalScriptWitness =+          input+            { partialSigs = mempty,+              inputHDKeypaths = mempty,+              inputRedeemScript = Nothing,+              inputWitnessScript = Nothing,+              sigHashType = Nothing+            }+      | otherwise = input++    indexed :: [a] -> [(Word32, a)]+    indexed = zip [0 ..]++completeSig :: Ctx -> Input -> ScriptOutput -> Input+completeSig ctx input (PayPK k) =+  input+    { finalScriptSig =+        eitherToMaybe . runGetS deserialize+          =<< HashMap.lookup k input.partialSigs+    }+completeSig ctx input (PayPKHash h)+  | [(k, sig)] <- HashMap.toList input.partialSigs,+    PubKeyAddress h == pubKeyAddr ctx k =+      input+        { finalScriptSig =+            Just $+              Script+                [ opPushData sig,+                  opPushData (marshal ctx k)+                ]+        }+completeSig ctx input (PayMulSig pubKeys m)+  | length sigs >= m =+      input {finalScriptSig = Just finalSig}+  where+    sigs = collectSigs m pubKeys input+    finalSig = Script $ OP_0 : map opPushData sigs+completeSig ctx input (PayScriptHash h)+  | Just rdmScript <- input.inputRedeemScript,+    PayScriptHash h == toP2SH rdmScript,+    Right decodedScript <- decodeOutput ctx rdmScript,+    not (isPayScriptHash decodedScript) =+      pushScript rdmScript $ completeSig ctx input decodedScript+  where+    pushScript rdmScript updatedInput =+      updatedInput+        { finalScriptSig =+            Just $+              fromMaybe (Script mempty) updatedInput.finalScriptSig+                `scriptAppend` serializedRedeemScript rdmScript+        }+    scriptAppend (Script script1) (Script script2) = Script $ script1 <> script2+completeSig ctx input (PayWitnessPKHash h)+  | [(k, sig)] <- HashMap.toList input.partialSigs,+    PubKeyAddress h == pubKeyAddr ctx k =+      input {finalScriptWitness = Just [sig, marshal ctx k]}+completeSig ctx input (PayWitnessScriptHash h)+  | Just witScript <- input.inputWitnessScript,+    PayWitnessScriptHash h == toP2WSH witScript,+    Right decodedScript <- decodeOutput ctx witScript =+      completeWitnessSig ctx input decodedScript+completeSig _ input _ = input++serializedRedeemScript :: Script -> Script+serializedRedeemScript = Script . pure . opPushData . runPutS . serialize++completeWitnessSig :: Ctx -> Input -> ScriptOutput -> Input+completeWitnessSig ctx input script@(PayMulSig pubKeys m)+  | length sigs >= m =+      input {finalScriptWitness = Just finalWit}+  where+    sigs = collectSigs m pubKeys input+    finalWit = mempty : sigs <> [marshal ctx script]+completeWitnessSig _ input _ = input++collectSigs :: Int -> [PublicKey] -> Input -> [ByteString]+collectSigs m pubKeys input =+  take m . reverse $ foldl' lookupKey [] pubKeys+  where+    lookupKey sigs key =+      maybe sigs (: sigs) $+        HashMap.lookup key input.partialSigs++-- | Take a finalized 'PSBT' and produce the signed final+-- transaction. You may need to call 'complete' on the+-- 'PSBT' before producing the final transaction.+finalTransaction :: PSBT -> Tx+finalTransaction psbt =+  setInputs+    . foldl' finalizeInput ([], [])+    $ zip tx.inputs psbt.inputs+  where+    tx = psbt.unsignedTransaction+    hasWitness = any (isJust . (.finalScriptWitness)) psbt.inputs+    setInputs (ins, witData) =+      tx+        { inputs = reverse ins,+          witness = if hasWitness then reverse witData else []+        }+    finalizeInput (ins, witData) (TxIn {..}, psbtInput) =+      ( TxIn+          { script =+              maybe+                mempty+                (runPutS . serialize)+                psbtInput.finalScriptSig,+            ..+          }+          : ins,+        fromMaybe [] psbtInput.finalScriptWitness : witData+      )++-- | Take an unsigned transaction and produce an empty+-- 'PSBT'+emptyPSBT :: Tx -> PSBT+emptyPSBT tx =+  PSBT+    { unsignedTransaction = tx,+      globalUnknown = mempty,+      inputs = replicate (length tx.inputs) emptyInput,+      outputs = replicate (length tx.outputs) emptyOutput+    }++emptyInput :: Input+emptyInput =+  Input+    Nothing+    Nothing+    HashMap.empty+    Nothing+    Nothing+    Nothing+    HashMap.empty+    Nothing+    Nothing+    (UnknownMap HashMap.empty)++emptyOutput :: Output+emptyOutput = Output Nothing Nothing HashMap.empty (UnknownMap HashMap.empty)++getPSBT :: (MonadGet m) => Ctx -> m PSBT+getPSBT ctx = do+  magic <- getBytes 4+  unless (magic == "psbt") $+    fail $+      "Expected magic = 'psbt' but got '" ++ cs magic ++ "'"+  headerSep <- getWord8+  unless (headerSep == 0xff) $+    fail $+      "Expected headerSep = 0xff but got 0x" ++ showHex headerSep ""++  keySize <- getWord8+  unless (keySize == 1) $+    fail $+      "Expected keySize = 1 but got " ++ show keySize+  globalUnsignedTxType <- getWord8+  unless (globalUnsignedTxType == 0x00) $+    fail $+      "Expected globalUnsignedTxType = 0x00 but got 0x"+        ++ showHex globalUnsignedTxType ""+  unsignedTransaction <- getSizedBytes deserialize+  unless (all (B.null . (.script)) unsignedTransaction.inputs) $+    fail $+      "Not all inputs from unsignedTransaction have empty scripts"+  unless (null unsignedTransaction.witness) $+    fail $+      "Not all witnesses from unsignedTransaction are empty"++  globalUnknown <- deserialize+  globalEnd <- getWord8+  unless (globalEnd == 0x00) $+    fail $+      "Expected globalEnd == 0x00 but got 0x" ++ showHex globalEnd ""++  inputs <-+    replicateM+      (length unsignedTransaction.inputs)+      (getInput ctx)+  outputs <-+    replicateM+      (length unsignedTransaction.outputs)+      (getOutput ctx)++  return+    PSBT+      { unsignedTransaction,+        globalUnknown,+        inputs,+        outputs+      }++putPSBT :: (MonadPut m) => Ctx -> PSBT -> m ()+putPSBT+  ctx+  PSBT+    { unsignedTransaction,+      globalUnknown,+      inputs,+      outputs+    } = do+    putByteString "psbt"+    putWord8 0xff -- Header separator+    putWord8 0x01 -- Key size+    putWord8 0x00 -- Unsigned Transaction type+    putSizedBytes $ S.encode unsignedTransaction+    serialize globalUnknown+    putWord8 0x00 -- Global end+    mapM_ (putInput ctx) inputs+    mapM_ (putOutput ctx) outputs++instance Serial Key where+  deserialize = do+    VarInt keySize <- deserialize+    unless (keySize > 0) $+      fail $+        "Expected keySize > 0 but got " ++ show keySize+    t <- getWord8+    k <- getBytes (fromIntegral keySize - 1)+    return (Key t k)++  serialize (Key t k) = do+    putVarInt $ 1 + B.length k+    putWord8 t+    putByteString k++instance Binary Key where+  put = serialize+  get = deserialize++instance Serialize Key where+  put = serialize+  get = deserialize++instance Serial UnknownMap where+  deserialize = go HashMap.empty+    where+      getItem m = do+        k <- deserialize+        VarString v <- deserialize+        go $ HashMap.insert k v m+      go m = do+        isEnd <- lookAhead getWord8+        if isEnd == 0x00+          then return (UnknownMap m)+          else getItem m++  serialize (UnknownMap m) =+    void $+      HashMap.traverseWithKey+        (\k v -> serialize k >> serialize (VarString v))+        m++getInput :: (MonadGet m) => Ctx -> m Input+getInput ctx =+  getMap (getInputItem ctx) setInputUnknown emptyInput+  where+    setInputUnknown f input =+      input+        { inputUnknown =+            UnknownMap (f input.inputUnknown.unknownMap)+        }++putInput :: (MonadPut m) => Ctx -> Input -> m ()+putInput+  ctx+  Input+    { nonWitnessUtxo,+      witnessUtxo,+      partialSigs,+      sigHashType,+      inputRedeemScript,+      inputWitnessScript,+      inputHDKeypaths,+      finalScriptSig,+      finalScriptWitness,+      inputUnknown+    } = do+    whenJust+      (putKeyValue InNonWitnessUtxo . S.encode)+      nonWitnessUtxo+    whenJust+      (putKeyValue InWitnessUtxo . S.encode)+      witnessUtxo+    putPartialSig partialSigs+    whenJust+      putSigHash+      sigHashType+    whenJust+      (putKeyValue InRedeemScript . S.encode)+      inputRedeemScript+    whenJust+      (putKeyValue InWitnessScript . S.encode)+      inputWitnessScript+    putHDPath ctx InBIP32Derivation inputHDKeypaths+    whenJust+      (putKeyValue InFinalScriptSig . S.encode)+      finalScriptSig+    whenJust+      (putKeyValue InFinalScriptWitness . putFinalScriptWitness)+      finalScriptWitness+    serialize inputUnknown+    putWord8 0x00+    where+      putPartialSig =+        putPubKeyMap ctx serialize InPartialSig . fmap VarString+      putSigHash sigHash = do+        putKey InSigHashType+        putWord8 0x04+        putWord32le (fromIntegral sigHash)+      putFinalScriptWitness witnessStack = runPutS $ do+        serialize $ (VarInt . fromIntegral . length) witnessStack+        mapM_ (serialize . VarString) witnessStack++getOutput :: (MonadGet m) => Ctx -> m Output+getOutput ctx = getMap (getOutputItem ctx) setOutputUnknown emptyOutput+  where+    setOutputUnknown f output =+      output+        { outputUnknown =+            UnknownMap (f output.outputUnknown.unknownMap)+        }++putOutput :: (MonadPut m) => Ctx -> Output -> m ()+putOutput+  ctx+  Output+    { outputRedeemScript,+      outputWitnessScript,+      outputHDKeypaths,+      outputUnknown+    } = do+    whenJust+      (putKeyValue OutRedeemScript . S.encode)+      outputRedeemScript+    whenJust+      (putKeyValue OutWitnessScript . S.encode)+      outputWitnessScript+    putHDPath+      ctx+      OutBIP32Derivation+      outputHDKeypaths+    serialize outputUnknown+    putWord8 0x00++putSizedBytes :: (MonadPut m) => ByteString -> m ()+putSizedBytes bs = do+  putVarInt (B.length bs)+  putByteString bs++getSizedBytes :: (MonadGet m) => Get a -> m a+getSizedBytes =+  getNested $ (\(VarInt i) -> fromIntegral i) <$> deserialize++putKeyValue :: (Enum t, MonadPut m) => t -> ByteString -> m ()+putKeyValue t v = do+  putKey t+  putSizedBytes v++putKey :: (Enum t, MonadPut m) => t -> m ()+putKey t = do+  putVarInt (1 :: Word8)+  putWord8 (enumWord8 t)++getMap ::+  (Bounded t, Enum t, MonadGet m) =>+  (Int -> a -> t -> m a) ->+  ((HashMap Key ByteString -> HashMap Key ByteString) -> a -> a) ->+  a ->+  m a+getMap getMapItem setUnknown = go+  where+    getItem keySize m (Right t) =+      getMapItem (fromIntegral keySize - 1) m t >>= go+    getItem keySize m (Left t) = do+      k <- getBytes (fromIntegral keySize - 1)+      VarString v <- deserialize+      go $ setUnknown (HashMap.insert (Key t k) v) m+    go m = do+      keySize <- (\(VarInt i) -> i) <$> deserialize+      if keySize == 0+        then return m+        else getItem keySize m . word8Enum =<< getWord8++data InputType+  = InNonWitnessUtxo+  | InWitnessUtxo+  | InPartialSig+  | InSigHashType+  | InRedeemScript+  | InWitnessScript+  | InBIP32Derivation+  | InFinalScriptSig+  | InFinalScriptWitness+  deriving (Show, Eq, Enum, Bounded, Generic)+  deriving anyclass (NFData)++data OutputType+  = OutRedeemScript+  | OutWitnessScript+  | OutBIP32Derivation+  deriving (Show, Eq, Enum, Bounded, Generic)+  deriving anyclass (NFData)++getInputItem ::+  (MonadGet m) =>+  Ctx ->+  Int ->+  Input ->+  InputType ->+  m Input+getInputItem ctx 0 input@Input {nonWitnessUtxo = Nothing} InNonWitnessUtxo = do+  utxo <- getSizedBytes deserialize+  return input {nonWitnessUtxo = Just utxo}+getInputItem ctx 0 input@Input {witnessUtxo = Nothing} InWitnessUtxo = do+  utxo <- getSizedBytes deserialize+  return input {witnessUtxo = Just utxo}+getInputItem ctx keySize input InPartialSig = do+  (k, v) <- getPartialSig+  return+    input+      { partialSigs = HashMap.insert k v input.partialSigs+      }+  where+    getPartialSig =+      (,)+        <$> isolate keySize (marshalGet ctx :: Get PublicKey)+        <*> ((\(VarString s) -> s) <$> deserialize)+getInputItem ctx 0 input@Input {sigHashType = Nothing} InSigHashType = do+  VarInt size <- deserialize+  unless (size == 0x04) $+    fail $+      "Expected size == 0x04 but got 0x" ++ showHex size ""+  sigHash <- fromIntegral <$> getWord32le+  return $ input {sigHashType = Just sigHash}+getInputItem ctx 0 input@Input {inputRedeemScript = Nothing} InRedeemScript = do+  script <- getSizedBytes deserialize+  return $ input {inputRedeemScript = Just script}+getInputItem ctx 0 input@Input {inputWitnessScript = Nothing} InWitnessScript = do+  script <- getSizedBytes deserialize+  return $ input {inputWitnessScript = Just script}+getInputItem ctx keySize input InBIP32Derivation = do+  (k, v) <- getHDPath ctx keySize+  return+    input+      { inputHDKeypaths = HashMap.insert k v input.inputHDKeypaths+      }+getInputItem ctx 0 input@Input {finalScriptSig = Nothing} InFinalScriptSig = do+  script <- getSizedBytes deserialize+  return $ input {finalScriptSig = Just script}+getInputItem ctx 0 input@Input {finalScriptWitness = Nothing} InFinalScriptWitness = do+  scripts <- map (\(VarString s) -> s) <$> getVarIntList+  return $ input {finalScriptWitness = Just scripts}+  where+    getVarIntList = getSizedBytes $ do+      VarInt n <- deserialize -- Item count+      replicateM (fromIntegral n) deserialize+getInputItem ctx keySize input inputType =+  fail "Incorrect key size for input item or item already existed"++getOutputItem :: (MonadGet m) => Ctx -> Int -> Output -> OutputType -> m Output+getOutputItem ctx 0 output@Output {outputRedeemScript = Nothing} OutRedeemScript = do+  script <- getSizedBytes deserialize+  return $ output {outputRedeemScript = Just script}+getOutputItem ctx 0 output@Output {outputWitnessScript = Nothing} OutWitnessScript = do+  script <- getSizedBytes deserialize+  return $ output {outputWitnessScript = Just script}+getOutputItem ctx keySize output OutBIP32Derivation = do+  (k, v) <- getHDPath ctx keySize+  return $ output {outputHDKeypaths = HashMap.insert k v output.outputHDKeypaths}+getOutputItem ctx keySize output outputType =+  fail "Incorrect key size for output item or item already existed"++getHDPath ::+  forall m.+  (MonadGet m) =>+  Ctx ->+  Int ->+  m (PublicKey, (Fingerprint, [KeyIndex]))+getHDPath ctx keySize =+  (,)+    <$> isolate keySize (marshalGet ctx :: Get PublicKey)+    <*> ((\(PSBTHDPath s) -> s) <$> deserialize)++putHDPath ::+  (Enum t, MonadPut m) =>+  Ctx ->+  t ->+  HashMap PublicKey (Fingerprint, [KeyIndex]) ->+  m ()+putHDPath ctx t = putPubKeyMap ctx serialize t . fmap PSBTHDPath++newtype PSBTHDPath = PSBTHDPath {unPSBTHDPath :: (Fingerprint, [KeyIndex])}+  deriving (Show, Eq, Generic)+  deriving newtype (NFData)++instance Serial PSBTHDPath where+  deserialize = do+    VarInt valueSize <- deserialize+    unless (valueSize `mod` 4 == 0) $+      fail $+        "Expected valueSize = 4 but got " ++ show valueSize+    let numIndices = (fromIntegral valueSize - 4) `div` 4+    PSBTHDPath+      <$> isolate+        (fromIntegral valueSize)+        ((,) <$> deserialize <*> getKeyIndexList numIndices)+    where+      getKeyIndexList n = replicateM n getWord32le++  serialize (PSBTHDPath (fp, kis)) = do+    putVarInt (B.length bs)+    putByteString bs+    where+      bs = runPutS $ serialize fp >> mapM_ putWord32le kis++instance Binary PSBTHDPath where+  put = serialize+  get = deserialize++instance Serialize PSBTHDPath where+  put = serialize+  get = deserialize++putPubKeyMap ::+  (Enum t, MonadPut m) =>+  Ctx ->+  (a -> m ()) ->+  t ->+  HashMap PublicKey a ->+  m ()+putPubKeyMap ctx f t =+  void . HashMap.traverseWithKey putItem+  where+    putItem k v = do+      serialize $ Key (enumWord8 t) (marshal ctx k)+      f v++enumWord8 :: (Enum a) => a -> Word8+enumWord8 = fromIntegral . fromEnum++word8Enum :: forall a. (Bounded a, Enum a) => Word8 -> Either Word8 a+word8Enum n | n <= enumWord8 (maxBound :: a) = Right . toEnum $ fromIntegral n+word8Enum n = Left n++whenJust :: (Monad m) => (a -> m ()) -> Maybe a -> m ()+whenJust = maybe (return ())++justWhen :: (a -> Bool) -> a -> Maybe a+justWhen test x = if test x then Just x else Nothing++isolate :: (MonadGet m) => Int -> Get a -> m a+isolate length getVal = do+  bs <- getByteString length+  either fail return (runGetS getVal bs)++getNested :: (MonadGet m) => m Int -> Get a -> m a+getNested getLength getVal = do+  length <- getLength+  isolate length getVal
+ src/Haskoin/Transaction/Segwit.hs view
@@ -0,0 +1,175 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Transaction.Segwit+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Types to represent segregated witness data and auxilliary functions to+-- manipulate it.  See [BIP 141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki)+-- and [BIP 143](https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki) for+-- details.+module Haskoin.Transaction.Segwit+  ( -- * Segwit+    WitnessProgram (..),+    WitnessProgramPKH (..),+    WitnessProgramSH (..),+    isSegwit,+    viewWitnessProgram,+    decodeWitnessInput,+    calcWitnessProgram,+    simpleInputStack,+    toWitnessStack,+  )+where++import Crypto.Secp256k1+import Data.ByteString (ByteString)+import Data.Bytes.Get (runGetS)+import Data.Bytes.Put (runPutS)+import Data.Bytes.Serial (Serial (deserialize, serialize))+import Haskoin.Crypto.Keys.Common+import Haskoin.Network.Data+import Haskoin.Script.Common+import Haskoin.Script.SigHash+import Haskoin.Script.Standard+import Haskoin.Transaction.Common+import Haskoin.Util.Marshal++-- | Test if a 'ScriptOutput' is P2WPKH or P2WSH+--+-- @since 0.11.0.0+isSegwit :: ScriptOutput -> Bool+isSegwit = \case+  PayWitnessPKHash {} -> True+  PayWitnessScriptHash {} -> True+  _ -> False++-- | High level represenation of a (v0) witness program+--+-- @since 0.11.0.0+data WitnessProgram+  = P2WPKH WitnessProgramPKH+  | P2WSH WitnessProgramSH+  | EmptyWitnessProgram+  deriving (Eq)++-- | Encode a witness program+--+-- @since 0.11.0.0+toWitnessStack :: Network -> Ctx -> WitnessProgram -> WitnessStack+toWitnessStack net ctx = \case+  P2WPKH (WitnessProgramPKH sig key) ->+    [encodeTxSig net ctx sig, marshal ctx key]+  P2WSH (WitnessProgramSH stack scr) ->+    stack <> [runPutS (serialize scr)]+  EmptyWitnessProgram ->+    mempty++-- | High level representation of a P2WPKH witness+--+-- @since 0.11.0.0+data WitnessProgramPKH = WitnessProgramPKH+  { signature :: !TxSignature,+    key :: !PublicKey+  }+  deriving (Eq)++-- | High-level representation of a P2WSH witness+--+-- @since 0.11.0.0+data WitnessProgramSH = WitnessProgramSH+  { stack :: ![ByteString],+    script :: !Script+  }+  deriving (Eq, Show)++-- | Calculate the witness program from the transaction data+--+-- @since 0.11.0.0+viewWitnessProgram ::+  Network ->+  Ctx ->+  ScriptOutput ->+  WitnessStack ->+  Either String WitnessProgram+viewWitnessProgram net ctx so witness = case so of+  PayWitnessPKHash _ | length witness == 2 -> do+    sig <- decodeTxSig net ctx (head witness)+    pubkey <- unmarshal ctx $ witness !! 1+    return . P2WPKH $ WitnessProgramPKH sig pubkey+  PayWitnessScriptHash _ | not (null witness) -> do+    redeemScript <- runGetS deserialize $ last witness+    return . P2WSH $ WitnessProgramSH (init witness) redeemScript+  _+    | null witness -> return EmptyWitnessProgram+    | otherwise -> Left "viewWitnessProgram: Invalid witness program"++-- | Analyze the witness, trying to match it with standard input structures+--+-- @since 0.11.0.0+decodeWitnessInput ::+  Network ->+  Ctx ->+  WitnessProgram ->+  Either String (Maybe ScriptOutput, SimpleInput)+decodeWitnessInput net ctx = \case+  P2WPKH (WitnessProgramPKH sig key) -> return (Nothing, SpendPKHash sig key)+  P2WSH (WitnessProgramSH st scr) -> do+    so <- decodeOutput ctx scr+    fmap (Just so,) $ case (so, st) of+      (PayPK _, [sigBS]) ->+        SpendPK <$> decodeTxSig net ctx sigBS+      (PayPKHash _, [sigBS, keyBS]) ->+        SpendPKHash+          <$> decodeTxSig net ctx sigBS+          <*> unmarshal ctx keyBS+      (PayMulSig _ _, "" : sigsBS) ->+        SpendMulSig+          <$> traverse (decodeTxSig net ctx) sigsBS+      _ -> Left "decodeWitnessInput: Non-standard script output"+  EmptyWitnessProgram -> Left "decodeWitnessInput: Empty witness program"++-- | Create the witness program for a standard input+--+-- @since 0.11.0.0+calcWitnessProgram ::+  Network ->+  Ctx ->+  ScriptOutput ->+  ScriptInput ->+  Either String WitnessProgram+calcWitnessProgram net ctx so si = case (so, si) of+  (PayWitnessPKHash {}, RegularInput (SpendPKHash sig pk)) ->+    Right $ p2wpkh sig pk+  (PayScriptHash {}, RegularInput (SpendPKHash sig pk)) ->+    Right $ p2wpkh sig pk+  (PayWitnessScriptHash {}, ScriptHashInput i o) ->+    Right $ p2wsh i o+  (PayScriptHash {}, ScriptHashInput i o) ->+    Right $ p2wsh i o+  _ -> Left "calcWitnessProgram: Invalid segwit SigInput"+  where+    p2wpkh sig =+      P2WPKH . WitnessProgramPKH sig+    p2wsh i =+      P2WSH . WitnessProgramSH (simpleInputStack net ctx i) . encodeOutput ctx++-- | Create the witness stack required to spend a standard P2WSH input+--+-- @since 0.11.0.0+simpleInputStack :: Network -> Ctx -> SimpleInput -> [ByteString]+simpleInputStack net ctx = \case+  SpendPK sig -> [f sig]+  SpendPKHash sig k -> [f sig, marshal ctx k]+  SpendMulSig sigs -> "" : fmap f sigs+  where+    f TxSignatureEmpty = ""+    f sig = encodeTxSig net ctx sig
+ src/Haskoin/Transaction/Taproot.hs view
@@ -0,0 +1,315 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE NoFieldSelectors #-}++-- |+-- Module      : Haskoin.Transaction.Taproot+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- This module provides support for reperesenting full taproot outputs and parsing+-- taproot witnesses.  For reference see BIPS 340, 341, and 342.+module Haskoin.Transaction.Taproot+  ( XOnlyPubKey (..),+    TapLeafVersion,+    MAST (..),+    mastCommitment,+    getMerkleProofs,+    TaprootOutput (..),+    taprootOutputKey,+    taprootScriptOutput,+    TaprootWitness (..),+    ScriptPathData (..),+    viewTaprootWitness,+    encodeTaprootWitness,+    verifyScriptPathData,+  )+where++import Control.Applicative (many)+import Control.Monad ((<=<))+import Crypto.Hash+  ( Digest,+    SHA256,+    digestFromByteString,+    hashFinalize,+    hashUpdate,+    hashUpdates,+  )+import Crypto.Secp256k1+import Data.Aeson+  ( FromJSON (parseJSON),+    ToJSON (toJSON),+    Value (String),+    withText,+  )+import Data.Aeson.Types (Parser, Value)+import Data.Binary (Binary (..))+import Data.Bits ((.&.), (.|.))+import Data.Bool (bool)+import Data.ByteArray qualified as BA+import Data.ByteString (ByteString)+import Data.ByteString qualified as BS+import Data.Bytes.Get (MonadGet, getBytes, runGetS)+import Data.Bytes.Put (MonadPut, putByteString, runPutL, runPutS)+import Data.Bytes.Serial (Serial (..), deserialize, serialize)+import Data.Bytes.VarInt (VarInt (VarInt))+import Data.Foldable (foldl')+import Data.Maybe (fromMaybe, mapMaybe)+import Data.Serialize (Serialize, get, getByteString, getWord8, put)+import Data.Word (Word8)+import Haskoin.Crypto.Hash+import Haskoin.Crypto.Keys.Common+import Haskoin.Crypto.Keys.Extended+import Haskoin.Script.Common+import Haskoin.Script.Standard+import Haskoin.Transaction.Common+import Haskoin.Util++-- | An x-only pubkey corresponds to the keys @(x,y)@ and @(x, -y)@.  The+-- equality test only checks the x-coordinate.  An x-only pubkey serializes to 32+-- bytes.+--+-- @since 0.21.0+newtype XOnlyPubKey = XOnlyPubKey {point :: PubKey}+  deriving (Read, Show)++instance Eq XOnlyPubKey where+  XOnlyPubKey k1 == XOnlyPubKey k2 = f k1 == f k2+    where+      f = BS.take 32 . (.get)++instance Marshal Ctx XOnlyPubKey where+  marshalPut ctx (XOnlyPubKey pk) =+    putByteString+      . BS.drop 1+      . marshal ctx+      $ PublicKey pk True++  marshalGet ctx =+    either fail (pure . XOnlyPubKey . (\PublicKey {point} -> point))+      . unmarshal ctx+      . BS.cons 0x02+      =<< getBytes 32++instance MarshalJSON Ctx XOnlyPubKey where+  unmarshalValue ctx =+    withText "XOnlyPubKey" $ either fail pure . (des <=< hex)+    where+      hex = maybe (Left "Unable to decode hex") Right . decodeHex+      des = runGetS $ marshalGet ctx++  marshalValue ctx =+    String . encodeHex . marshal ctx++  marshalEncoding ctx =+    hexEncoding . runPutL . marshalPut ctx++-- | @since 0.21.0+type TapLeafVersion = Word8++-- | Merklized Abstract Syntax Tree.  This type can represent trees where only a+-- subset of the leaves are known.  Note that the tree is invariant under swapping+-- branches at an internal node.+--+-- @since 0.21.0+data MAST+  = MASTBranch MAST MAST+  | MASTLeaf TapLeafVersion Script+  | MASTCommitment (Digest SHA256)+  deriving (Show)++-- | Get the inclusion proofs for the leaves in the tree.  The proof is ordered+-- leaf-to-root.+--+-- @since 0.21.0+getMerkleProofs :: MAST -> [(TapLeafVersion, Script, [Digest SHA256])]+getMerkleProofs = getProofs mempty+  where+    getProofs proof = \case+      MASTBranch branchL branchR ->+        (updateProof proof (mastCommitment branchR) <$> getMerkleProofs branchL)+          <> (updateProof proof (mastCommitment branchL) <$> getMerkleProofs branchR)+      MASTLeaf v s -> [(v, s, proof)]+      MASTCommitment {} -> mempty+    updateProof proofInit branchCommitment (v, s, proofTail) =+      (v, s, reverse $ proofInit <> (branchCommitment : proofTail))++-- | Calculate the root hash for this tree.+--+-- @since 0.21.0+mastCommitment :: MAST -> Digest SHA256+mastCommitment = \case+  MASTBranch leftBranch rightBranch ->+    hashBranch (mastCommitment leftBranch) (mastCommitment rightBranch)+  MASTLeaf leafVersion leafScript -> leafHash leafVersion leafScript+  MASTCommitment theCommitment -> theCommitment++hashBranch :: Digest SHA256 -> Digest SHA256 -> Digest SHA256+hashBranch hashA hashB =+  hashFinalize $+    hashUpdates+      (initTaggedHash "TapBranch")+      [ min hashA hashB,+        max hashA hashB+      ]++leafHash :: TapLeafVersion -> Script -> Digest SHA256+leafHash leafVersion leafScript =+  hashFinalize+    . hashUpdate (initTaggedHash "TapLeaf")+    . runPutS+    $ do+      serialize leafVersion+      serialize $ VarInt (BS.length scriptBytes)+      putByteString scriptBytes+  where+    scriptBytes = runPutS $ serialize leafScript++-- | Representation of a full taproot output.+--+-- @since 0.21.0+data TaprootOutput = TaprootOutput+  { internalKey :: PubKey,+    mast :: Maybe MAST+  }++-- | @since 0.21.0+taprootOutputKey :: Ctx -> TaprootOutput -> PubKey+taprootOutputKey ctx TaprootOutput {..} =+  fromMaybe keyFail $+    tweak commitment >>= tweakAddPubKey ctx internalKey+  where+    commitment =+      taprootCommitment ctx internalKey $+        mastCommitment <$> mast+    keyFail = error "haskoin-core taprootOutputKey: key derivation failed"++taprootCommitment :: Ctx -> PubKey -> Maybe (Digest SHA256) -> ByteString+taprootCommitment ctx internalKey merkleRoot =+  BA.convert+    . hashFinalize+    . maybe id (flip hashUpdate) merkleRoot+    . (`hashUpdate` keyBytes)+    $ initTaggedHash "TapTweak"+  where+    keyBytes = runPutS . marshalPut ctx $ XOnlyPubKey internalKey++-- | Generate the output script for a taproot output+--+-- @since 0.21.0+taprootScriptOutput :: Ctx -> TaprootOutput -> ScriptOutput+taprootScriptOutput ctx =+  PayWitness 0x01+    . runPutS+    . marshalPut ctx+    . XOnlyPubKey+    . taprootOutputKey ctx++-- | Comprehension of taproot witness data+--+-- @since 0.21.0+data TaprootWitness+  = -- | Signature+    KeyPathSpend ByteString+  | ScriptPathSpend ScriptPathData+  deriving (Eq)++-- | @since 0.21.0+data ScriptPathData = ScriptPathData+  { annex :: Maybe ByteString,+    stack :: [ByteString],+    script :: Script,+    extIsOdd :: Bool,+    -- | This value is masked by 0xFE+    leafVersion :: Word8,+    internalKey :: PubKey,+    control :: [ByteString]+  }+  deriving (Eq)++-- | Try to interpret a 'WitnessStack' as taproot witness data.+--+-- @since 0.21.0+viewTaprootWitness :: Ctx -> WitnessStack -> Maybe TaprootWitness+viewTaprootWitness ctx witnessStack = case reverse witnessStack of+  [sig] -> Just $ KeyPathSpend sig+  annexA : remainingStack+    | 0x50 : _ <- BS.unpack annexA ->+        parseSpendPathData (Just annexA) remainingStack+  remainingStack -> parseSpendPathData Nothing remainingStack+  where+    parseSpendPathData annex = \case+      scriptBytes : controlBytes : stack -> do+        script <- eitherToMaybe $ runGetS deserialize scriptBytes+        (v, internalKey, control) <- deconstructControl controlBytes+        let extIsOdd = odd v+            leafVersion = v .&. 0xFE+        pure $ ScriptPathSpend ScriptPathData {..}+      _ -> Nothing+    deconstructControl = eitherToMaybe . runGetS deserializeControl+    deserializeControl = do+      v <- getWord8+      XOnlyPubKey k <- marshalGet ctx+      proof <- many $ getByteString 32+      pure (v, k, proof)++-- | Transform the high-level representation of taproot witness data into a witness stack+--+-- @since 0.21.0+encodeTaprootWitness :: Ctx -> TaprootWitness -> WitnessStack+encodeTaprootWitness ctx = \case+  KeyPathSpend signature -> pure signature+  ScriptPathSpend scriptPathData -> wit scriptPathData+  where+    wit d = (.stack) d <> [script d, keys d, annex d]+    keys d = mconcat [verpar d, xonlyk d, ctrl d]+    script = runPutS . serialize . (.script)+    verpar d = BS.pack [(.leafVersion) d .|. parity d]+    xonlyk = runPutS . marshalPut ctx . XOnlyPubKey . (.internalKey)+    annex = fromMaybe mempty . (.annex)+    ctrl = mconcat . (.control)+    parity = bool 0 1 . (.extIsOdd)++-- | Verify that the script path spend is valid, except for script execution.+--+-- @since 0.21.0+verifyScriptPathData ::+  Ctx ->+  -- | Output key+  PubKey ->+  ScriptPathData ->+  Bool+verifyScriptPathData ctx outkey spd = fromMaybe False $ do+  tweak commitment+    >>= fmap onComputedKey+      . tweakAddPubKey ctx spd.internalKey+  where+    onComputedKey computedKey =+      XOnlyPubKey outkey == XOnlyPubKey computedKey+        && expectedParity == keyParity ctx computedKey+    commitment =+      taprootCommitment ctx spd.internalKey (Just merkleRoot)+    merkleRoot =+      foldl' hashBranch theLeafHash $+        mapMaybe (digestFromByteString @SHA256) spd.control+    theLeafHash =+      (leafHash <$> (.&. 0xFE) . (.leafVersion) <*> (.script))+        spd+    expectedParity = bool 0 1 spd.extIsOdd++keyParity :: Ctx -> PubKey -> Word8+keyParity ctx key =+  case BS.unpack . marshal ctx $ PublicKey key True of+    0x02 : _ -> 0x00+    _ -> 0x01
+ src/Haskoin/Util.hs view
@@ -0,0 +1,17 @@+-- |+-- Module      : Haskoin.Util+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Marshalling and helper functions.+module Haskoin.Util+  ( module Marshal,+    module Helpers,+  )+where++import Haskoin.Util.Helpers as Helpers+import Haskoin.Util.Marshal as Marshal
+ src/Haskoin/Util/Arbitrary.hs view
@@ -0,0 +1,23 @@+-- |+-- Module      : Haskoin.Test+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- Arbitrary instances for testing.+module Haskoin.Util.Arbitrary+  ( module X,+  )+where++import Haskoin.Util.Arbitrary.Address as X+import Haskoin.Util.Arbitrary.Block as X+import Haskoin.Util.Arbitrary.Crypto as X+import Haskoin.Util.Arbitrary.Keys as X+import Haskoin.Util.Arbitrary.Message as X+import Haskoin.Util.Arbitrary.Network as X+import Haskoin.Util.Arbitrary.Script as X+import Haskoin.Util.Arbitrary.Transaction as X+import Haskoin.Util.Arbitrary.Util as X
+ src/Haskoin/Util/Arbitrary/Address.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE TupleSections #-}++-- |+-- Module      : Haskoin.Test.Address+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+module Haskoin.Util.Arbitrary.Address where++import qualified Data.ByteString as B+import Haskoin.Address+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Util.Arbitrary.Crypto+import Haskoin.Util.Arbitrary.Util+import Test.QuickCheck++-- | Arbitrary pay-to-public-key-hash or pay-to-script-hash address.+arbitraryAddress :: Gen Address+arbitraryAddress = oneof [arbitraryPubKeyAddress, arbitraryScriptAddress]++-- | Arbitrary address including pay-to-witness+arbitraryAddressAll :: Gen Address+arbitraryAddressAll =+  oneof+    [ arbitraryPubKeyAddress,+      arbitraryScriptAddress,+      arbitraryWitnessPubKeyAddress,+      arbitraryWitnessScriptAddress,+      arbitraryWitnessAddress+    ]++-- | Arbitrary valid combination of (Network, Address)+arbitraryNetAddress :: Gen (Network, Address)+arbitraryNetAddress = do+  net <- arbitraryNetwork+  if net `elem` [bch, bchTest, bchTest4, bchRegTest]+    then (net,) <$> arbitraryAddress+    else (net,) <$> arbitraryAddressAll++-- | Arbitrary pay-to-public-key-hash address.+arbitraryPubKeyAddress :: Gen Address+arbitraryPubKeyAddress = PubKeyAddress <$> arbitraryHash160++-- | Arbitrary pay-to-script-hash address.+arbitraryScriptAddress :: Gen Address+arbitraryScriptAddress = ScriptAddress <$> arbitraryHash160++-- | Arbitrary pay-to-witness public key hash+arbitraryWitnessPubKeyAddress :: Gen Address+arbitraryWitnessPubKeyAddress = WitnessPubKeyAddress <$> arbitraryHash160++-- | Arbitrary pay-to-witness script hash+arbitraryWitnessScriptAddress :: Gen Address+arbitraryWitnessScriptAddress = WitnessPubKeyAddress <$> arbitraryHash160++arbitraryWitnessAddress :: Gen Address+arbitraryWitnessAddress = do+  ver <- choose (1, 16)+  len <- choose (2, 40)+  ws <- vectorOf len arbitrary+  let bs = B.pack ws+  return $ WitnessAddress ver bs
+ src/Haskoin/Util/Arbitrary/Block.hs view
@@ -0,0 +1,87 @@+-- |+-- Module      : Haskoin.Test.Block+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+module Haskoin.Util.Arbitrary.Block where++import qualified Data.HashMap.Strict as HashMap+import Haskoin.Block+import Haskoin.Crypto (Ctx)+import Haskoin.Network.Data+import Haskoin.Util.Arbitrary.Crypto+import Haskoin.Util.Arbitrary.Network+import Haskoin.Util.Arbitrary.Transaction+import Haskoin.Util.Arbitrary.Util+import Test.QuickCheck++-- | Block full or arbitrary transactions.+arbitraryBlock :: Network -> Ctx -> Gen Block+arbitraryBlock net ctx = do+  h <- arbitraryBlockHeader+  c <- choose (0, 10)+  txs <- vectorOf c (arbitraryTx net ctx)+  return $ Block h txs++-- | Block header with random hash.+arbitraryBlockHeader :: Gen BlockHeader+arbitraryBlockHeader =+  BlockHeader+    <$> arbitrary+    <*> arbitraryBlockHash+    <*> arbitraryHash256+    <*> arbitrary+    <*> arbitrary+    <*> arbitrary++-- | Arbitrary block hash.+arbitraryBlockHash :: Gen BlockHash+arbitraryBlockHash = BlockHash <$> arbitraryHash256++-- | Arbitrary 'GetBlocks' object with at least one block hash.+arbitraryGetBlocks :: Gen GetBlocks+arbitraryGetBlocks =+  GetBlocks+    <$> arbitrary+    <*> listOf1 arbitraryBlockHash+    <*> arbitraryBlockHash++-- | Arbitrary 'GetHeaders' object with at least one block header.+arbitraryGetHeaders :: Gen GetHeaders+arbitraryGetHeaders =+  GetHeaders+    <$> arbitrary+    <*> listOf1 arbitraryBlockHash+    <*> arbitraryBlockHash++-- | Arbitrary 'Headers' object with at least one block header.+arbitraryHeaders :: Gen Headers+arbitraryHeaders =+  Headers <$> listOf1 ((,) <$> arbitraryBlockHeader <*> arbitraryVarInt)++-- | Arbitrary 'MerkleBlock' with at least one hash.+arbitraryMerkleBlock :: Gen MerkleBlock+arbitraryMerkleBlock = do+  bh <- arbitraryBlockHeader+  ntx <- arbitrary+  hashes <- listOf1 arbitraryHash256+  c <- choose (1, 10)+  flags <- vectorOf (c * 8) arbitrary+  return $ MerkleBlock bh ntx hashes flags++-- | Arbitrary 'BlockNode'+arbitraryBlockNode :: Gen BlockNode+arbitraryBlockNode =+  BlockNode+    <$> arbitraryBlockHeader+    <*> choose (0, maxBound)+    <*> arbitrarySizedNatural+    <*> arbitraryBlockHash++-- | Arbitrary 'HeaderMemory'+arbitraryHeaderMemory :: Gen HeaderMemory+arbitraryHeaderMemory = do+  ls <- listOf $ (,) <$> arbitrary <*> arbitraryBSS+  HeaderMemory (HashMap.fromList ls) <$> arbitraryBlockNode
+ src/Haskoin/Util/Arbitrary/Crypto.hs view
@@ -0,0 +1,32 @@+-- |+-- Module      : Haskoin.Test.Crypto+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+module Haskoin.Util.Arbitrary.Crypto where++import Haskoin.Crypto.Hash+import Haskoin.Util.Arbitrary.Util+import Test.QuickCheck++-- | Arbitrary 160-bit hash.+arbitraryHash160 :: Gen Hash160+arbitraryHash160 =+  ripemd160 <$> arbitraryBSn 20++-- | Arbitrary 256-bit hash.+arbitraryHash256 :: Gen Hash256+arbitraryHash256 =+  sha256 <$> arbitraryBSn 32++-- | Arbitrary 512-bit hash.+arbitraryHash512 :: Gen Hash512+arbitraryHash512 =+  sha512 <$> arbitraryBSn 64++-- | Arbitrary 32-bit checksum.+arbitraryCheckSum32 :: Gen CheckSum32+arbitraryCheckSum32 =+  checkSum32 <$> arbitraryBSn 4
+ src/Haskoin/Util/Arbitrary/Keys.hs view
@@ -0,0 +1,102 @@+-- |+-- Module      : Haskoin.Test.Keys+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+module Haskoin.Util.Arbitrary.Keys where++import Crypto.Secp256k1+import Data.Bits (clearBit)+import Data.Coerce (coerce)+import Data.List (foldl')+import Data.Word (Word32)+import Haskoin.Crypto.Hash+import Haskoin.Crypto.Keys.Common+import Haskoin.Crypto.Keys.Extended+import Haskoin.Crypto.Keys.Extended.Internal (Fingerprint (..))+import Haskoin.Crypto.Signature+import Haskoin.Util.Arbitrary.Crypto+import Test.QuickCheck++-- | Arbitrary private key with arbitrary compressed flag.+arbitraryPrivateKey :: Gen PrivateKey+arbitraryPrivateKey = wrapSecKey <$> arbitrary <*> arbitrary++-- | Arbitrary public key, either compressed or not.+arbitraryPublicKey :: Ctx -> Gen PublicKey+arbitraryPublicKey ctx = snd <$> arbitraryKeyPair ctx++-- | Arbitrary keypair, both either compressed or not.+arbitraryKeyPair :: Ctx -> Gen (PrivateKey, PublicKey)+arbitraryKeyPair ctx = do+  k <- arbitraryPrivateKey+  return (k, derivePublicKey ctx k)++arbitraryFingerprint :: Gen Fingerprint+arbitraryFingerprint = Fingerprint <$> arbitrary++-- | Arbitrary extended private key.+arbitraryXPrvKey :: Gen XPrvKey+arbitraryXPrvKey =+  XPrvKey+    <$> arbitrary+    <*> arbitraryFingerprint+    <*> arbitrary+    <*> arbitraryHash256+    <*> arbitrary++-- | Arbitrary extended public key.+arbitraryXPubKey :: Ctx -> Gen XPubKey+arbitraryXPubKey ctx = snd <$> arbitraryXKeyPair ctx++-- | Arbitrary extended public key with its corresponding private key.+arbitraryXKeyPair :: Ctx -> Gen (XPrvKey, XPubKey)+arbitraryXKeyPair ctx = (\k -> (k, deriveXPubKey ctx k)) <$> arbitraryXPrvKey++{- Custom derivations -}++-- | Arbitrary derivation index with last bit unset.+genIndex :: Gen Word32+genIndex = (`clearBit` 31) <$> arbitrary++-- | Arbitrary BIP-32 path index. Can be hardened or not.+arbitraryBip32PathIndex :: Gen Bip32PathIndex+arbitraryBip32PathIndex =+  oneof+    [ Bip32SoftIndex <$> genIndex,+      Bip32HardIndex <$> genIndex+    ]++-- | Arbitrary BIP-32 derivation path composed of only hardened derivations.+arbitraryHardPath :: Gen HardPath+arbitraryHardPath = foldl' (:|) Deriv <$> listOf genIndex++-- | Arbitrary BIP-32 derivation path composed of only non-hardened derivations.+arbitrarySoftPath :: Gen SoftPath+arbitrarySoftPath = foldl' (:/) Deriv <$> listOf genIndex++-- | Arbitrary derivation path composed of hardened and non-hardened derivations.+arbitraryDerivPath :: Gen DerivPath+arbitraryDerivPath = concatBip32Segments <$> listOf arbitraryBip32PathIndex++-- | Arbitrary parsed derivation path. Can contain 'ParsedPrv', 'ParsedPub' or+-- 'ParsedEmpty' elements.+arbitraryParsedPath :: Gen ParsedPath+arbitraryParsedPath =+  oneof+    [ ParsedPrv <$> arbitraryDerivPath,+      ParsedPub <$> arbitraryDerivPath,+      ParsedEmpty <$> arbitraryDerivPath+    ]++-- | Arbitrary message hash, private key, nonce and corresponding signature. The+-- signature is generated with a random message, random private key and a random+-- nonce.+arbitrarySignature :: Ctx -> Gen (Hash256, SecKey, Sig)+arbitrarySignature ctx = do+  m <- arbitraryHash256+  key <- arbitrary+  let sig = signHash ctx key m+  return (m, key, sig)
+ src/Haskoin/Util/Arbitrary/Message.hs view
@@ -0,0 +1,53 @@+-- |+-- Module      : Haskoin.Test.Message+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+module Haskoin.Util.Arbitrary.Message where++import Haskoin.Crypto (Ctx)+import Haskoin.Network.Data+import Haskoin.Network.Message+import Haskoin.Util.Arbitrary.Block+import Haskoin.Util.Arbitrary.Crypto+import Haskoin.Util.Arbitrary.Network+import Haskoin.Util.Arbitrary.Transaction+import Test.QuickCheck++-- | Arbitrary 'MessageHeader'.+arbitraryMessageHeader :: Gen MessageHeader+arbitraryMessageHeader =+  MessageHeader+    <$> arbitrary+    <*> arbitraryMessageCommand+    <*> arbitrary+    <*> arbitraryCheckSum32++-- | Arbitrary 'Message'.+arbitraryMessage :: Network -> Ctx -> Gen Message+arbitraryMessage net ctx =+  oneof+    [ MVersion <$> arbitraryVersion,+      return MVerAck,+      MAddr <$> arbitraryAddr1,+      MInv <$> arbitraryInv1,+      MGetData <$> arbitraryGetData,+      MNotFound <$> arbitraryNotFound,+      MGetBlocks <$> arbitraryGetBlocks,+      MGetHeaders <$> arbitraryGetHeaders,+      MTx <$> arbitraryTx net ctx,+      MBlock <$> arbitraryBlock net ctx,+      MMerkleBlock <$> arbitraryMerkleBlock,+      MHeaders <$> arbitraryHeaders,+      return MGetAddr,+      MFilterLoad <$> arbitraryFilterLoad,+      MFilterAdd <$> arbitraryFilterAdd,+      return MFilterClear,+      MPing <$> arbitraryPing,+      MPong <$> arbitraryPong,+      MAlert <$> arbitraryAlert,+      MReject <$> arbitraryReject,+      return MSendHeaders+    ]
+ src/Haskoin/Util/Arbitrary/Network.hs view
@@ -0,0 +1,181 @@+-- |+-- Module      : Haskoin.Test.Network+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+module Haskoin.Util.Arbitrary.Network where++import qualified Data.ByteString as BS (empty, pack)+import qualified Data.ByteString.Char8 as C8+import Data.Word (Word16, Word32)+import Haskoin.Network+import Haskoin.Util.Arbitrary.Crypto+import Haskoin.Util.Arbitrary.Util+import Network.Socket (SockAddr (..))+import Test.QuickCheck++-- | Arbitrary 'VarInt'.+arbitraryVarInt :: Gen VarInt+arbitraryVarInt = VarInt <$> arbitrary++-- | Arbitrary 'VarString'.+arbitraryVarString :: Gen VarString+arbitraryVarString = VarString <$> arbitraryBS++-- | Arbitrary 'NetworkAddress'.+arbitraryNetworkAddress :: Gen NetworkAddress+arbitraryNetworkAddress = do+  s <- arbitrary+  a <- arbitrary+  p <- arbitrary+  d <-+    oneof+      [ do+          b <- arbitrary+          c <- arbitrary+          d <- arbitrary+          return $ SockAddrInet6 (fromIntegral p) 0 (a, b, c, d) 0,+        return $ SockAddrInet (fromIntegral (p :: Word16)) a+      ]+  let n = sockToHostAddress d+  return $ NetworkAddress s n++-- | Arbitrary 'NetworkAddressTime'.+arbitraryNetworkAddressTime :: Gen (Word32, NetworkAddress)+arbitraryNetworkAddressTime = (,) <$> arbitrary <*> arbitraryNetworkAddress++-- | Arbitrary 'InvType'.+arbitraryInvType :: Gen InvType+arbitraryInvType = elements [InvError, InvTx, InvBlock, InvMerkleBlock]++-- | Arbitrary 'InvVector'.+arbitraryInvVector :: Gen InvVector+arbitraryInvVector = InvVector <$> arbitraryInvType <*> arbitraryHash256++-- | Arbitrary non-empty 'Inv'.+arbitraryInv1 :: Gen Inv+arbitraryInv1 = Inv <$> listOf1 arbitraryInvVector++-- | Arbitrary 'Version'.+arbitraryVersion :: Gen Version+arbitraryVersion =+  Version+    <$> arbitrary+    <*> arbitrary+    <*> arbitrary+    <*> arbitraryNetworkAddress+    <*> arbitraryNetworkAddress+    <*> arbitrary+    <*> arbitraryVarString+    <*> arbitrary+    <*> arbitrary++-- | Arbitrary non-empty 'Addr'.+arbitraryAddr1 :: Gen Addr+arbitraryAddr1 = Addr <$> listOf1 arbitraryNetworkAddressTime++-- | Arbitrary 'Alert' with random payload and signature. Signature is not+-- valid.+arbitraryAlert :: Gen Alert+arbitraryAlert = Alert <$> arbitraryVarString <*> arbitraryVarString++-- | Arbitrary 'Reject'.+arbitraryReject :: Gen Reject+arbitraryReject = do+  m <- arbitraryMessageCommand+  c <- arbitraryRejectCode+  s <- arbitraryVarString+  d <-+    oneof+      [ return BS.empty,+        BS.pack <$> vectorOf 32 arbitrary+      ]+  return $ Reject m c s d++-- | Arbitrary 'RejectCode'.+arbitraryRejectCode :: Gen RejectCode+arbitraryRejectCode =+  elements+    [ RejectMalformed,+      RejectInvalid,+      RejectInvalid,+      RejectDuplicate,+      RejectNonStandard,+      RejectDust,+      RejectInsufficientFee,+      RejectCheckpoint+    ]++-- | Arbitrary non-empty 'GetData'.+arbitraryGetData :: Gen GetData+arbitraryGetData = GetData <$> listOf1 arbitraryInvVector++-- | Arbitrary 'NotFound'.+arbitraryNotFound :: Gen NotFound+arbitraryNotFound = NotFound <$> listOf1 arbitraryInvVector++-- | Arbitrary 'Ping'.+arbitraryPing :: Gen Ping+arbitraryPing = Ping <$> arbitrary++-- | Arbitrary 'Pong'.+arbitraryPong :: Gen Pong+arbitraryPong = Pong <$> arbitrary++-- | Arbitrary bloom filter flags.+arbitraryBloomFlags :: Gen BloomFlags+arbitraryBloomFlags =+  elements+    [ BloomUpdateNone,+      BloomUpdateAll,+      BloomUpdateP2PubKeyOnly+    ]++-- | Arbitrary bloom filter with its corresponding number of elements+-- and false positive rate.+arbitraryBloomFilter :: Gen (Int, Double, BloomFilter)+arbitraryBloomFilter = do+  n <- choose (0, 100000)+  fp <- choose (1e-8, 1)+  tweak <- arbitrary+  fl <- arbitraryBloomFlags+  return (n, fp, bloomCreate n fp tweak fl)++-- | Arbitrary 'FilterLoad'.+arbitraryFilterLoad :: Gen FilterLoad+arbitraryFilterLoad = do+  (_, _, bf) <- arbitraryBloomFilter+  return $ FilterLoad bf++-- | Arbitrary 'FilterAdd'.+arbitraryFilterAdd :: Gen FilterAdd+arbitraryFilterAdd = FilterAdd <$> arbitraryBS++-- | Arbitrary 'MessageCommand'.+arbitraryMessageCommand :: Gen MessageCommand+arbitraryMessageCommand = do+  ASCIIString str <- arbitrary+  elements+    [ MCVersion,+      MCVerAck,+      MCAddr,+      MCInv,+      MCGetData,+      MCNotFound,+      MCGetBlocks,+      MCGetHeaders,+      MCTx,+      MCBlock,+      MCMerkleBlock,+      MCHeaders,+      MCGetAddr,+      MCFilterLoad,+      MCFilterAdd,+      MCFilterClear,+      MCPing,+      MCPong,+      MCAlert,+      MCOther (C8.take 12 (C8.pack (filter (/= '\NUL') str)))+    ]
+ src/Haskoin/Util/Arbitrary/Script.hs view
@@ -0,0 +1,420 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedRecordDot #-}++-- |+-- Module      : Haskoin.Test.Script+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+module Haskoin.Util.Arbitrary.Script where++import Crypto.Secp256k1+import Data.ByteString qualified as B+import Data.Maybe+import Data.Word+import Haskoin.Address+import Haskoin.Crypto.Keys.Common+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Script+import Haskoin.Transaction.Common+import Haskoin.Util+import Haskoin.Util.Arbitrary.Address+import Haskoin.Util.Arbitrary.Crypto+import Haskoin.Util.Arbitrary.Keys+import Haskoin.Util.Arbitrary.Util+import Test.QuickCheck++-- | Arbitrary 'Script' with random script ops.+arbitraryScript :: Gen Script+arbitraryScript = Script <$> listOf arbitraryScriptOp++-- | Arbitrary 'ScriptOp' (push operations have random data).+arbitraryScriptOp :: Gen ScriptOp+arbitraryScriptOp =+  oneof+    -- Pushing Data+    [ opPushData <$> arbitraryBS1,+      return OP_0,+      return OP_1NEGATE,+      return OP_RESERVED,+      return OP_1,+      return OP_2,+      return OP_3,+      return OP_4,+      return OP_5,+      return OP_6,+      return OP_7,+      return OP_8,+      return OP_9,+      return OP_10,+      return OP_11,+      return OP_12,+      return OP_13,+      return OP_14,+      return OP_15,+      return OP_16,+      -- Flow control+      return OP_NOP,+      return OP_VER,+      return OP_IF,+      return OP_NOTIF,+      return OP_VERIF,+      return OP_VERNOTIF,+      return OP_ELSE,+      return OP_ENDIF,+      return OP_VERIFY,+      return OP_RETURN,+      -- Stack operations+      return OP_TOALTSTACK,+      return OP_FROMALTSTACK,+      return OP_IFDUP,+      return OP_DEPTH,+      return OP_DROP,+      return OP_DUP,+      return OP_NIP,+      return OP_OVER,+      return OP_PICK,+      return OP_ROLL,+      return OP_ROT,+      return OP_SWAP,+      return OP_TUCK,+      return OP_2DROP,+      return OP_2DUP,+      return OP_3DUP,+      return OP_2OVER,+      return OP_2ROT,+      return OP_2SWAP,+      -- Splice+      return OP_CAT,+      return OP_SUBSTR,+      return OP_LEFT,+      return OP_RIGHT,+      return OP_SIZE,+      -- Bitwise logic+      return OP_INVERT,+      return OP_AND,+      return OP_OR,+      return OP_XOR,+      return OP_EQUAL,+      return OP_EQUALVERIFY,+      return OP_RESERVED1,+      return OP_RESERVED2,+      -- Arithmetic+      return OP_1ADD,+      return OP_1SUB,+      return OP_2MUL,+      return OP_2DIV,+      return OP_NEGATE,+      return OP_ABS,+      return OP_NOT,+      return OP_0NOTEQUAL,+      return OP_ADD,+      return OP_SUB,+      return OP_MUL,+      return OP_DIV,+      return OP_MOD,+      return OP_LSHIFT,+      return OP_RSHIFT,+      return OP_BOOLAND,+      return OP_BOOLOR,+      return OP_NUMEQUAL,+      return OP_NUMEQUALVERIFY,+      return OP_NUMNOTEQUAL,+      return OP_LESSTHAN,+      return OP_GREATERTHAN,+      return OP_LESSTHANOREQUAL,+      return OP_GREATERTHANOREQUAL,+      return OP_MIN,+      return OP_MAX,+      return OP_WITHIN,+      -- Crypto+      return OP_RIPEMD160,+      return OP_SHA1,+      return OP_SHA256,+      return OP_HASH160,+      return OP_HASH256,+      return OP_CODESEPARATOR,+      return OP_CHECKSIG,+      return OP_CHECKSIGVERIFY,+      return OP_CHECKMULTISIG,+      return OP_CHECKMULTISIGVERIFY,+      -- Expansion+      return OP_NOP1,+      return OP_CHECKLOCKTIMEVERIFY,+      return OP_CHECKSEQUENCEVERIFY,+      return OP_NOP4,+      return OP_NOP5,+      return OP_NOP6,+      return OP_NOP7,+      return OP_NOP8,+      return OP_NOP9,+      return OP_NOP10,+      -- Bitcoin Cash Nov 2018 hard fork+      return OP_CHECKDATASIG,+      return OP_CHECKDATASIGVERIFY,+      -- Bitcoin Cash May 2020 hard fork+      return OP_REVERSEBYTES,+      -- Other+      return OP_PUBKEYHASH,+      return OP_PUBKEY,+      return $ OP_INVALIDOPCODE 0xff+    ]++-- | Arbtirary 'ScriptOp' with a value in @[OP_1 .. OP_16]@.+arbitraryIntScriptOp :: Gen ScriptOp+arbitraryIntScriptOp =+  elements+    [ OP_1,+      OP_2,+      OP_3,+      OP_4,+      OP_5,+      OP_6,+      OP_7,+      OP_8,+      OP_9,+      OP_10,+      OP_11,+      OP_12,+      OP_13,+      OP_14,+      OP_15,+      OP_16+    ]++-- | Arbitrary 'PushDataType'.+arbitraryPushDataType :: Gen PushDataType+arbitraryPushDataType = elements [OPCODE, OPDATA1, OPDATA2, OPDATA4]++-- | Arbitrary 'SigHash' (including invalid/unknown sighash codes).+arbitrarySigHash :: Gen SigHash+arbitrarySigHash = fromIntegral <$> (arbitrary :: Gen Word32)++-- | Arbitrary valid 'SigHash'.+arbitraryValidSigHash :: Network -> Gen SigHash+arbitraryValidSigHash net = do+  sh <- elements [sigHashAll, sigHashNone, sigHashSingle]+  f1 <-+    elements $+      if isJust net.sigHashForkId+        then [id, setForkIdFlag]+        else [id]+  f2 <- elements [id, setAnyoneCanPay]+  return $ f1 $ f2 sh++arbitrarySigHashFlag :: Gen SigHashFlag+arbitrarySigHashFlag =+  elements+    [ SIGHASH_ALL,+      SIGHASH_NONE,+      SIGHASH_SINGLE,+      SIGHASH_FORKID,+      SIGHASH_ANYONECANPAY+    ]++-- | Arbitrary message hash, private key and corresponding 'TxSignature'. The+-- signature is generated deterministically using a random message and a random+-- private key.+arbitraryTxSignature :: Network -> Ctx -> Gen (TxHash, SecKey, TxSignature)+arbitraryTxSignature net ctx = do+  (m, key, sig) <- arbitrarySignature ctx+  sh <- (fromIntegral <$> (arbitrary :: Gen Word8)) `suchThat` filterBad+  let txsig = TxSignature sig sh+  return (TxHash m, key, txsig)+  where+    filterBad sh =+      not $+        isSigHashUnknown sh+          || isNothing net.sigHashForkId && hasForkIdFlag sh++-- | Arbitrary transaction signature that could also be empty.+arbitraryTxSignatureEmpty :: Network -> Ctx -> Gen TxSignature+arbitraryTxSignatureEmpty net ctx =+  frequency+    [ (1, return TxSignatureEmpty),+      (10, lst3 <$> arbitraryTxSignature net ctx)+    ]++-- | Arbitrary m of n parameters.+arbitraryMSParam :: Gen (Int, Int)+arbitraryMSParam = do+  m <- choose (1, 16)+  n <- choose (m, 16)+  return (m, n)++-- | Arbitrary 'ScriptOutput' (Can by any valid type).+arbitraryScriptOutput :: Network -> Ctx -> Gen ScriptOutput+arbitraryScriptOutput net ctx =+  oneof $+    [ arbitraryPKOutput ctx,+      arbitraryPKHashOutput,+      arbitraryMSOutput ctx,+      arbitrarySHOutput,+      arbitraryDCOutput+    ]+      ++ if net.segWit+        then+          [ arbitraryWPKHashOutput,+            arbitraryWSHOutput,+            arbitraryWitOutput+          ]+        else []++-- | Arbitrary 'ScriptOutput' of type 'PayPK', 'PayPKHash' or 'PayMS'+-- (Not 'PayScriptHash', 'DataCarrier', or SegWit)+arbitrarySimpleOutput :: Ctx -> Gen ScriptOutput+arbitrarySimpleOutput ctx =+  oneof+    [ arbitraryPKOutput ctx,+      arbitraryPKHashOutput,+      arbitraryMSOutput ctx+    ]++-- | Arbitrary 'ScriptOutput' of type 'PayPK'+arbitraryPKOutput :: Ctx -> Gen ScriptOutput+arbitraryPKOutput ctx = PayPK . snd <$> arbitraryKeyPair ctx++-- | Arbitrary 'ScriptOutput' of type 'PayPKHash'+arbitraryPKHashOutput :: Gen ScriptOutput+arbitraryPKHashOutput = PayPKHash <$> arbitraryHash160++-- | Arbitrary 'PayWitnessPKHash' output.+arbitraryWPKHashOutput :: Gen ScriptOutput+arbitraryWPKHashOutput = PayWitnessPKHash <$> arbitraryHash160++-- | Arbitrary 'PayWitnessScriptHash' output.+arbitraryWSHOutput :: Gen ScriptOutput+arbitraryWSHOutput = PayWitnessScriptHash <$> arbitraryHash256++arbitraryWitOutput :: Gen ScriptOutput+arbitraryWitOutput = do+  ver <- choose (1, 16)+  len <- choose (2, 40)+  ws <- vectorOf len arbitrary+  let bs = B.pack ws+  return $ PayWitness ver bs++-- | Arbitrary 'ScriptOutput' of type 'PayMS'.+arbitraryMSOutput :: Ctx -> Gen ScriptOutput+arbitraryMSOutput ctx = do+  (m, n) <- arbitraryMSParam+  keys <- map snd <$> vectorOf n (arbitraryKeyPair ctx)+  return $ PayMulSig keys m++-- | Arbitrary 'ScriptOutput' of type 'PayMS', only using compressed keys.+arbitraryMSOutputC :: Ctx -> Gen ScriptOutput+arbitraryMSOutputC ctx = do+  (m, n) <- arbitraryMSParam+  keys <-+    map snd+      <$> vectorOf n (arbitraryKeyPair ctx `suchThat` ((.compress) . snd))+  return $ PayMulSig keys m++-- | Arbitrary 'ScriptOutput' of type 'PayScriptHash'.+arbitrarySHOutput :: Gen ScriptOutput+arbitrarySHOutput = PayScriptHash . (.hash160) <$> arbitraryScriptAddress++-- | Arbitrary 'ScriptOutput' of type 'DataCarrier'.+arbitraryDCOutput :: Gen ScriptOutput+arbitraryDCOutput = DataCarrier <$> arbitraryBS1++-- | Arbitrary 'ScriptInput'.+arbitraryScriptInput :: Network -> Ctx -> Gen ScriptInput+arbitraryScriptInput net ctx =+  oneof+    [ arbitraryPKInput net ctx,+      arbitraryPKHashInput net ctx,+      arbitraryMSInput net ctx,+      arbitrarySHInput net ctx+    ]++-- | Arbitrary 'ScriptInput' of type 'SpendPK', 'SpendPKHash' or 'SpendMulSig'+-- (not 'ScriptHashInput')+arbitrarySimpleInput :: Network -> Ctx -> Gen ScriptInput+arbitrarySimpleInput net ctx =+  oneof+    [ arbitraryPKInput net ctx,+      arbitraryPKHashInput net ctx,+      arbitraryMSInput net ctx+    ]++-- | Arbitrary 'ScriptInput' of type 'SpendPK'.+arbitraryPKInput :: Network -> Ctx -> Gen ScriptInput+arbitraryPKInput net ctx = RegularInput . SpendPK <$> arbitraryTxSignatureEmpty net ctx++-- | Arbitrary 'ScriptInput' of type 'SpendPK'.+arbitraryPKHashInput :: Network -> Ctx -> Gen ScriptInput+arbitraryPKHashInput net ctx = do+  sig <- arbitraryTxSignatureEmpty net ctx+  key <- snd <$> arbitraryKeyPair ctx+  return $ RegularInput $ SpendPKHash sig key++-- | Like 'arbitraryPKHashInput' without empty signatures.+arbitraryPKHashInputFull :: Network -> Ctx -> Gen ScriptInput+arbitraryPKHashInputFull net ctx = do+  sig <- lst3 <$> arbitraryTxSignature net ctx+  key <- snd <$> arbitraryKeyPair ctx+  return $ RegularInput $ SpendPKHash sig key++-- | Like above but only compressed.+arbitraryPKHashInputFullC :: Network -> Ctx -> Gen ScriptInput+arbitraryPKHashInputFullC net ctx = do+  sig <- lst3 <$> arbitraryTxSignature net ctx+  key <- fmap snd $ arbitraryKeyPair ctx `suchThat` ((.compress) . snd)+  return $ RegularInput $ SpendPKHash sig key++-- | Arbitrary 'ScriptInput' of type 'SpendMulSig'.+arbitraryMSInput :: Network -> Ctx -> Gen ScriptInput+arbitraryMSInput net ctx = do+  m <- fst <$> arbitraryMSParam+  sigs <- vectorOf m (arbitraryTxSignatureEmpty net ctx)+  return $ RegularInput $ SpendMulSig sigs++-- | Arbitrary 'ScriptInput' of type 'ScriptHashInput'.+arbitrarySHInput :: Network -> Ctx -> Gen ScriptInput+arbitrarySHInput net ctx = do+  i <- arbitrarySimpleInput net ctx+  ScriptHashInput i.get <$> arbitrarySimpleOutput ctx++-- | Arbitrary 'ScriptInput' of type 'ScriptHashInput' containing a+-- 'RedeemScript' of type 'PayMulSig' and an input of type 'SpendMulSig'.+arbitraryMulSigSHInput :: Network -> Ctx -> Gen ScriptInput+arbitraryMulSigSHInput net ctx =+  arbitraryMSOutput ctx >>= \case+    rdm@(PayMulSig _ m) -> do+      sigs <- vectorOf m (arbitraryTxSignatureEmpty net ctx)+      return $ ScriptHashInput (SpendMulSig sigs) rdm+    _ -> undefined++-- | Arbitrary 'ScriptInput' of type 'ScriptHashInput' containing a+-- 'RedeemScript' of type 'PayMulSig' and an input of type 'SpendMulSig'.+arbitraryMulSigSHInputC :: Network -> Ctx -> Gen ScriptInput+arbitraryMulSigSHInputC net ctx =+  arbitraryMSOutputC ctx >>= \case+    rdm@(PayMulSig _ m) -> do+      sigs <- vectorOf m (arbitraryTxSignatureEmpty net ctx)+      return $ ScriptHashInput (SpendMulSig sigs) rdm+    _ -> undefined++-- | Like 'arbitraryMulSigSHCInput' with no empty signatures.+arbitraryMulSigSHInputFull :: Network -> Ctx -> Gen ScriptInput+arbitraryMulSigSHInputFull net ctx =+  arbitraryMSOutput ctx >>= \case+    rdm@(PayMulSig _ m) -> do+      sigs <- map lst3 <$> vectorOf m (arbitraryTxSignature net ctx)+      return $ ScriptHashInput (SpendMulSig sigs) rdm+    _ -> undefined++-- | Like 'arbitraryMulSigSHCInput' with no empty signatures.+arbitraryMulSigSHInputFullC :: Network -> Ctx -> Gen ScriptInput+arbitraryMulSigSHInputFullC net ctx =+  arbitraryMSOutputC ctx >>= \case+    rdm@(PayMulSig _ m) -> do+      sigs <- map lst3 <$> vectorOf m (arbitraryTxSignature net ctx)+      return $ ScriptHashInput (SpendMulSig sigs) rdm+    _ -> undefined
+ src/Haskoin/Util/Arbitrary/Transaction.hs view
@@ -0,0 +1,283 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}++-- |+-- Module      : Haskoin.Test.Transaction+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+module Haskoin.Util.Arbitrary.Transaction where++import Control.Monad+import Data.ByteString qualified as BS+import Data.Either (fromRight)+import Data.List (nub, nubBy, permutations)+import Data.Word (Word64)+import Haskoin.Address+import Haskoin.Crypto (Ctx)+import Haskoin.Crypto.Keys.Common+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Script+import Haskoin.Transaction+import Haskoin.Util+import Haskoin.Util.Arbitrary.Crypto+import Haskoin.Util.Arbitrary.Keys+import Haskoin.Util.Arbitrary.Script+import Haskoin.Util.Arbitrary.Util+import Test.QuickCheck++-- | Wrapped coin value for testing.+newtype TestCoin = TestCoin {getTestCoin :: Word64}+  deriving (Eq, Show)++instance Coin TestCoin where+  coinValue = getTestCoin++-- | Arbitrary transaction hash (for non-existent transaction).+arbitraryTxHash :: Gen TxHash+arbitraryTxHash = TxHash <$> arbitraryHash256++-- | Arbitrary amount of Satoshi as 'Word64' (Between 1 and 21e14)+arbitrarySatoshi :: Network -> Gen TestCoin+arbitrarySatoshi net = TestCoin <$> choose (1, net.maxSatoshi)++-- | Arbitrary 'OutPoint'.+arbitraryOutPoint :: Gen OutPoint+arbitraryOutPoint = OutPoint <$> arbitraryTxHash <*> arbitrary++-- | Arbitrary 'TxOut'.+arbitraryTxOut :: Network -> Ctx -> Gen TxOut+arbitraryTxOut net ctx =+  TxOut+    <$> (getTestCoin <$> arbitrarySatoshi net)+    <*> (marshal ctx <$> arbitraryScriptOutput net ctx)++-- | Arbitrary 'TxIn'.+arbitraryTxIn :: Network -> Ctx -> Gen TxIn+arbitraryTxIn net ctx =+  TxIn+    <$> arbitraryOutPoint+    <*> (marshal (net, ctx) <$> arbitraryScriptInput net ctx)+    <*> arbitrary++-- | Arbitrary transaction. Can be regular or with witnesses.+arbitraryTx :: Network -> Ctx -> Gen Tx+arbitraryTx net ctx =+  oneof [arbitraryLegacyTx net ctx, arbitraryWitnessTx net ctx]++-- | Arbitrary regular transaction.+arbitraryLegacyTx :: Network -> Ctx -> Gen Tx+arbitraryLegacyTx net ctx = arbitraryWLTx net ctx False++-- | Arbitrary witness transaction (witness data is fake).+arbitraryWitnessTx :: Network -> Ctx -> Gen Tx+arbitraryWitnessTx net ctx = arbitraryWLTx net ctx True++-- | Arbitrary witness or legacy transaction.+arbitraryWLTx :: Network -> Ctx -> Bool -> Gen Tx+arbitraryWLTx net ctx wit = do+  ni <- choose (1, 5)+  no <- choose (1, 5)+  inps <- vectorOf ni (arbitraryTxIn net ctx)+  outs <- vectorOf no (arbitraryTxOut net ctx)+  let uniqueInps = nubBy (\a b -> a.outpoint == b.outpoint) inps+  w <-+    if wit+      then vectorOf (length uniqueInps) (listOf arbitraryBS)+      else return []+  Tx <$> arbitrary <*> pure uniqueInps <*> pure outs <*> pure w <*> arbitrary++-- | Arbitrary transaction containing only inputs of type 'SpendPKHash',+-- 'SpendScriptHash' (multisig) and outputs of type 'PayPKHash' and 'PaySH'.+-- Only compressed public keys are used.+arbitraryAddrOnlyTx :: Network -> Ctx -> Gen Tx+arbitraryAddrOnlyTx net ctx = do+  ni <- choose (1, 5)+  no <- choose (1, 5)+  inps <- vectorOf ni (arbitraryAddrOnlyTxIn net ctx)+  outs <- vectorOf no (arbitraryAddrOnlyTxOut net ctx)+  Tx <$> arbitrary <*> pure inps <*> pure outs <*> pure [] <*> arbitrary++-- | Like 'arbitraryAddrOnlyTx' without empty signatures in the inputs.+arbitraryAddrOnlyTxFull :: Network -> Ctx -> Gen Tx+arbitraryAddrOnlyTxFull net ctx = do+  ni <- choose (1, 5)+  no <- choose (1, 5)+  inps <- vectorOf ni (arbitraryAddrOnlyTxInFull net ctx)+  outs <- vectorOf no (arbitraryAddrOnlyTxOut net ctx)+  Tx <$> arbitrary <*> pure inps <*> pure outs <*> pure [] <*> arbitrary++-- | Arbitrary TxIn that can only be of type 'SpendPKHash' or 'SpendScriptHash'+-- (multisig). Only compressed public keys are used.+arbitraryAddrOnlyTxIn :: Network -> Ctx -> Gen TxIn+arbitraryAddrOnlyTxIn net ctx = do+  inp <- oneof [arbitraryPKHashInput net ctx, arbitraryMulSigSHInput net ctx]+  TxIn <$> arbitraryOutPoint <*> pure (marshal (net, ctx) inp) <*> arbitrary++-- | like 'arbitraryAddrOnlyTxIn' with no empty signatures.+arbitraryAddrOnlyTxInFull :: Network -> Ctx -> Gen TxIn+arbitraryAddrOnlyTxInFull net ctx = do+  inp <-+    oneof [arbitraryPKHashInputFullC net ctx, arbitraryMulSigSHInputFullC net ctx]+  TxIn <$> arbitraryOutPoint <*> pure (marshal (net, ctx) inp) <*> arbitrary++-- | Arbitrary 'TxOut' that can only be of type 'PayPKHash' or 'PaySH'.+arbitraryAddrOnlyTxOut :: Network -> Ctx -> Gen TxOut+arbitraryAddrOnlyTxOut net ctx = do+  v <- getTestCoin <$> arbitrarySatoshi net+  out <- oneof [arbitraryPKHashOutput, arbitrarySHOutput]+  return $ TxOut v $ marshal ctx out++-- | Arbitrary 'SigInput' with the corresponding private keys used+-- to generate the 'ScriptOutput' or 'RedeemScript'.+arbitrarySigInput :: Network -> Ctx -> Gen (SigInput, [PrivateKey])+arbitrarySigInput net ctx =+  oneof+    [ wrapKey <$> arbitraryPKSigInput net ctx,+      wrapKey <$> arbitraryPKHashSigInput net ctx,+      arbitraryMSSigInput net ctx,+      arbitrarySHSigInput net ctx,+      wrapKey <$> arbitraryWPKHSigInput net ctx,+      arbitraryWSHSigInput net ctx+    ]++-- | Arbitrary 'SigInput' with a 'ScriptOutput' of type 'PayPK'.+arbitraryPKSigInput :: Network -> Ctx -> Gen (SigInput, PrivateKey)+arbitraryPKSigInput net ctx = arbitraryAnyInput net ctx False++-- | Arbitrary 'SigInput' with a 'ScriptOutput' of type 'PayPKHash'.+arbitraryPKHashSigInput :: Network -> Ctx -> Gen (SigInput, PrivateKey)+arbitraryPKHashSigInput net ctx = arbitraryAnyInput net ctx True++-- | Arbitrary 'SigInput'.+arbitraryAnyInput :: Network -> Ctx -> Bool -> Gen (SigInput, PrivateKey)+arbitraryAnyInput net ctx pkh = do+  (k, p) <- arbitraryKeyPair ctx+  let out+        | pkh = PayPKHash (pubKeyAddr ctx p).hash160+        | otherwise = PayPK p+  (val, op, sh) <- arbitraryInputStuff net+  return (SigInput out val op sh Nothing, k)++-- | Arbitrary value, out point and sighash for an input.+arbitraryInputStuff :: Network -> Gen (Word64, OutPoint, SigHash)+arbitraryInputStuff net = do+  val <- getTestCoin <$> arbitrarySatoshi net+  op <- arbitraryOutPoint+  sh <- arbitraryValidSigHash net+  return (val, op, sh)++-- | Arbitrary 'SigInput' with a 'ScriptOutput' of type 'PayMulSig'.+arbitraryMSSigInput :: Network -> Ctx -> Gen (SigInput, [PrivateKey])+arbitraryMSSigInput net ctx = do+  (m, n) <- arbitraryMSParam+  ks <- vectorOf n (arbitraryKeyPair ctx)+  let out = PayMulSig (map snd ks) m+  (val, op, sh) <- arbitraryInputStuff net+  perm <- choose (0, n - 1)+  let ksPerm = map fst $ take m $ permutations ks !! perm+  return (SigInput out val op sh Nothing, ksPerm)++-- | Arbitrary 'SigInput' with 'ScriptOutput' of type 'PaySH' and a+-- 'RedeemScript'.+arbitrarySHSigInput :: Network -> Ctx -> Gen (SigInput, [PrivateKey])+arbitrarySHSigInput net ctx = do+  (SigInput rdm val op sh _, ks) <-+    oneof+      [ wrapKey <$> arbitraryPKSigInput net ctx,+        wrapKey <$> arbitraryPKHashSigInput net ctx,+        arbitraryMSSigInput net ctx+      ]+  let out = PayScriptHash (payToScriptAddress ctx rdm).hash160+  return (SigInput out val op sh $ Just rdm, ks)++arbitraryWPKHSigInput :: Network -> Ctx -> Gen (SigInput, PrivateKey)+arbitraryWPKHSigInput net ctx = do+  (k, p) <- arbitraryKeyPair ctx+  (val, op, sh) <- arbitraryInputStuff net+  let out = PayWitnessPKHash (pubKeyAddr ctx p).hash160+  return (SigInput out val op sh Nothing, k)++arbitraryWSHSigInput :: Network -> Ctx -> Gen (SigInput, [PrivateKey])+arbitraryWSHSigInput net ctx = do+  (SigInput rdm val op sh _, ks) <-+    oneof+      [ wrapKey <$> arbitraryPKSigInput net ctx,+        wrapKey <$> arbitraryPKHashSigInput net ctx,+        arbitraryMSSigInput net ctx+      ]+  let out = PayWitnessScriptHash (payToWitnessScriptAddress ctx rdm).hash256+  return (SigInput out val op sh $ Just rdm, ks)++-- | Arbitrary 'Tx' (empty 'TxIn'), 'SigInputs' and private keys that can be+-- passed to 'signTx' or 'detSignTx' to fully sign the 'Tx'.+arbitrarySigningData :: Network -> Ctx -> Gen (Tx, [SigInput], [PrivateKey])+arbitrarySigningData net ctx = do+  v <- arbitrary+  ni <- choose (1, 5)+  no <- choose (1, 5)+  sigis <- vectorOf ni (arbitrarySigInput net ctx)+  let uSigis = nubBy (\(a, _) (b, _) -> a.outpoint == b.outpoint) sigis+  inps <- forM uSigis $ \(s, _) -> TxIn s.outpoint BS.empty <$> arbitrary+  outs <- vectorOf no (arbitraryTxOut net ctx)+  l <- arbitrary+  perm <- choose (0, length inps - 1)+  let tx = Tx v (permutations inps !! perm) outs [] l+      keys = concatMap snd uSigis+  return (tx, map fst uSigis, keys)++-- | Arbitrary transaction with empty inputs.+arbitraryEmptyTx :: Network -> Ctx -> Gen Tx+arbitraryEmptyTx net ctx = do+  v <- arbitrary+  no <- choose (1, 5)+  ni <- choose (1, 5)+  outs <- vectorOf no (arbitraryTxOut net ctx)+  ops <- vectorOf ni arbitraryOutPoint+  t <- arbitrary+  s <- arbitrary+  return $ Tx v (map (\op -> TxIn op BS.empty s) (nub ops)) outs [] t++-- | Arbitrary partially-signed transactions.+arbitraryPartialTxs ::+  Network -> Ctx -> Gen ([Tx], [(ScriptOutput, Word64, OutPoint, Int, Int)])+arbitraryPartialTxs net ctx = do+  tx <- arbitraryEmptyTx net ctx+  res <-+    forM (map (.outpoint) tx.inputs) $ \op -> do+      (so, val, rdmM, prvs, m, n) <- arbitraryData+      txs <- mapM (singleSig so val rdmM tx op . (.key)) prvs+      return (txs, (so, val, op, m, n))+  return (concatMap fst res, map snd res)+  where+    singleSig so val rdmM tx op prv = do+      sh <- arbitraryValidSigHash net+      let sigi = SigInput so val op sh rdmM+      return . fromRight (error "Could not decode transaction") $+        signTx net ctx tx [sigi] [prv]+    arbitraryData = do+      (m, n) <- arbitraryMSParam+      val <- getTestCoin <$> arbitrarySatoshi net+      nPrv <- choose (m, n)+      keys <- vectorOf n (arbitraryKeyPair ctx)+      perm <- choose (0, length keys - 1)+      let pubKeys = map snd keys+          prvKeys = take nPrv $ permutations (map fst keys) !! perm+      let so = PayMulSig pubKeys m+      elements+        [ (so, val, Nothing, prvKeys, m, n),+          ( PayScriptHash (payToScriptAddress ctx so).hash160,+            val,+            Just so,+            prvKeys,+            m,+            n+          )+        ]++wrapKey :: (SigInput, PrivateKey) -> (SigInput, [PrivateKey])+wrapKey (s, k) = (s, [k])
+ src/Haskoin/Util/Arbitrary/Util.hs view
@@ -0,0 +1,254 @@+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module      : Haskoin.Test.Util+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+module Haskoin.Util.Arbitrary.Util+  ( arbitraryBS,+    arbitraryBS1,+    arbitraryBSn,+    arbitraryBSS,+    arbitraryBSS1,+    arbitraryBSSn,+    arbitraryMaybe,+    arbitraryNetwork,+    arbitraryUTCTime,+    ReadBox (..),+    JsonBox (..),+    MarshalJsonBox (..),+    SerialBox (..),+    MarshalBox (..),+    IdentityTests (..),+    testIdentity,+    testSerial,+    testRead,+    testJson,+  )+where++import Control.Monad (forM_, (<=<))+import qualified Data.Aeson as A+import qualified Data.Aeson.Encoding as A+import qualified Data.Aeson.Types as A+import Data.ByteString (ByteString, pack)+import Data.ByteString.Lazy (fromStrict, toStrict)+import qualified Data.ByteString.Short as BSS+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Default+import qualified Data.Map.Strict as Map+import Data.Proxy+import Data.Time.Clock (UTCTime (..))+import Data.Time.Clock.POSIX (posixSecondsToUTCTime)+import qualified Data.Typeable as T+import Data.Word (Word32)+import Haskoin.Crypto (Ctx)+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Util+import Test.Hspec (Spec, describe, shouldBe, shouldSatisfy)+import Test.Hspec.QuickCheck (prop)+import Test.QuickCheck++-- | Arbitrary strict 'ByteString'.+arbitraryBS :: Gen ByteString+arbitraryBS = pack <$> arbitrary++-- | Arbitrary non-empty strict 'ByteString'+arbitraryBS1 :: Gen ByteString+arbitraryBS1 = pack <$> listOf1 arbitrary++-- | Arbitrary strict 'ByteString' of a given length+arbitraryBSn :: Int -> Gen ByteString+arbitraryBSn n = pack <$> vectorOf n arbitrary++-- | Arbitrary 'ShortByteString'.+arbitraryBSS :: Gen BSS.ShortByteString+arbitraryBSS = BSS.pack <$> arbitrary++-- | Arbitrary non-empty 'ShortByteString'+arbitraryBSS1 :: Gen BSS.ShortByteString+arbitraryBSS1 = BSS.pack <$> listOf1 arbitrary++-- | Arbitrary 'ShortByteString' of a given length+arbitraryBSSn :: Int -> Gen BSS.ShortByteString+arbitraryBSSn n = BSS.pack <$> vectorOf n arbitrary++-- | Arbitrary UTCTime that generates dates after 01 Jan 1970 01:00:00 CET+arbitraryUTCTime :: Gen UTCTime+arbitraryUTCTime = do+  w <- arbitrary :: Gen Word32+  return $ posixSecondsToUTCTime $ realToFrac w++-- | Generate a Maybe from a Gen a+arbitraryMaybe :: Gen a -> Gen (Maybe a)+arbitraryMaybe g =+  frequency+    [ (1, return Nothing),+      (5, Just <$> g)+    ]++-- | Generate an Network+arbitraryNetwork :: Gen Network+arbitraryNetwork = elements allNets++-- Helpers for creating Serial and JSON Identity tests++instance Show Ctx where+  show _ = "Ctx"++data ReadBox+  = forall a.+    (Read a, Show a, Eq a, T.Typeable a) =>+    ReadBox (Gen a)++data JsonBox+  = forall a.+    (Show a, Eq a, T.Typeable a, A.ToJSON a, A.FromJSON a) =>+    JsonBox (Gen a)++data MarshalJsonBox+  = forall s a.+    (Show a, Show s, Eq a, T.Typeable a, MarshalJSON s a) =>+    MarshalJsonBox (Gen (s, a))++data SerialBox+  = forall a.+    (Show a, Eq a, T.Typeable a, Serial a) =>+    SerialBox (Gen a)++data MarshalBox+  = forall s a.+    (Show a, Show s, Eq a, T.Typeable a, Marshal s a) =>+    MarshalBox (Gen (s, a))++data IdentityTests = IdentityTests+  { readTests :: [ReadBox],+    jsonTests :: [JsonBox],+    marshalJsonTests :: [MarshalJsonBox],+    serialTests :: [SerialBox],+    marshalTests :: [MarshalBox]+  }++instance Default IdentityTests where+  def =+    IdentityTests+      { readTests = [],+        jsonTests = [],+        marshalJsonTests = [],+        serialTests = [],+        marshalTests = []+      }++testIdentity :: IdentityTests -> Spec+testIdentity t = do+  describe "Read/Show Encoding" $+    forM_ t.readTests $+      \(ReadBox g) -> testRead g+  describe "Data.Aeson Encoding" $+    forM_ t.jsonTests $+      \(JsonBox g) -> testJson g+  describe "MarshalJSON Encoding" $+    forM_ t.marshalJsonTests $+      \(MarshalJsonBox g) -> testMarshalJson g+  describe "Binary Encoding" $+    forM_ t.serialTests $+      \(SerialBox g) -> testSerial g+  describe "Marshal Encoding" $+    forM_ t.marshalTests $+      \(MarshalBox g) -> testMarshal g++-- | Generate Read/Show identity tests+testRead ::+  (Eq a, Read a, Show a, T.Typeable a) => Gen a -> Spec+testRead gen =+  prop ("read/show identity for " <> name) $+    forAll gen $+      \x -> (read . show) x `shouldBe` x+  where+    name = show $ T.typeRep $ proxy gen+    proxy :: Gen a -> Proxy a+    proxy = const Proxy++-- | Generate binary identity tests+testSerial ::+  (Eq a, Show a, T.Typeable a, Serial a) => Gen a -> Spec+testSerial gen =+  prop ("Binary encoding/decoding identity for " <> name) $+    forAll gen $ \x -> do+      (runGetL deserialize . runPutL . serialize) x `shouldBe` x+      (runGetL deserialize . fromStrict . runPutS . serialize) x `shouldBe` x+      (runGetS deserialize . runPutS . serialize) x `shouldBe` Right x+      (runGetS deserialize . toStrict . runPutL . serialize) x `shouldBe` Right x+  where+    name = show $ T.typeRep $ proxy gen+    proxy :: Gen a -> Proxy a+    proxy = const Proxy++-- | Generate Marshal identity tests+testMarshal ::+  (Eq a, Show a, Show s, T.Typeable a, Marshal s a) =>+  Gen (s, a) ->+  Spec+testMarshal gen = do+  prop ("Marshal marshalPut/marshalGet identity for " <> name) $+    forAll gen $ \(s, a) -> do+      (unmarshal s . marshal s) a `shouldBe` Right a+      (unmarshalLazy s . marshalLazy s) a `shouldBe` a+  where+    name = show $ T.typeRep $ proxy gen+    proxy :: Gen (s, a) -> Proxy a+    proxy = const Proxy++-- | Generate Data.Aeson identity tests+testJson ::+  (Eq a, Show a, T.Typeable a, A.ToJSON a, A.FromJSON a) => Gen a -> Spec+testJson gen = do+  prop ("Data.Aeson toJSON/fromJSON identity for " <> name) $+    forAll gen (`shouldSatisfy` jsonID)+  prop ("Data.Aeson toEncoding/fromJSON identity for " <> name) $+    forAll gen (`shouldSatisfy` encodingID)+  where+    name = show $ T.typeRep $ proxy gen+    proxy :: Gen a -> Proxy a+    proxy = const Proxy+    jsonID x = (A.fromJSON . A.toJSON) (toMap x) == A.Success (toMap x)+    encodingID x =+      (A.decode . A.encodingToLazyByteString . A.toEncoding) (toMap x)+        == Just (toMap x)++-- | Generate MarshalJSON identity tests+testMarshalJson ::+  (Eq a, Show a, Show s, T.Typeable a, MarshalJSON s a) =>+  Gen (s, a) ->+  Spec+testMarshalJson gen = do+  prop ("MarshalJSON marshalValue/unmarshalValue identity for " <> name) $+    forAll gen $+      \(s, a) -> a `shouldSatisfy` marshalJsonID s+  prop ("MarshalJSON marshalEncoding/unmarshalValue identity for " <> name) $+    forAll gen $+      \(s, a) -> a `shouldSatisfy` marshalEncodingID s+  where+    name = show $ T.typeRep $ proxy gen+    proxy :: Gen (s, a) -> Proxy a+    proxy = const Proxy+    marshalJsonID s a =+      A.parseMaybe (unmarshalValue s) (marshalValue s a) == Just a+    marshalEncodingID s a = unmarshalJSON s (marshalJSON s a) == Just a++toMap :: a -> Map.Map String a+toMap = Map.singleton "object"++toMapE :: A.Encoding -> A.Encoding+toMapE = A.pairs . A.pair "object"++fromMap :: Map.Map String a -> a+fromMap = (Map.! "object")
+ src/Haskoin/Util/Helpers.hs view
@@ -0,0 +1,426 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}++-- |+-- Module      : Haskoin.Util+-- Copyright   : No rights reserved+-- License     : MIT+-- Maintainer  : jprupp@protonmail.ch+-- Stability   : experimental+-- Portability : POSIX+--+-- This module defines various utility functions used across the library.+module Haskoin.Util.Helpers+  ( -- * ByteString Helpers+    bsToInteger,+    integerToBS,+    hexEncoding,+    hexBuilder,+    encodeHex,+    encodeHexLazy,+    decodeHex,+    decodeHexLazy,+    getBits,++    -- * Maybe & Either Helpers+    eitherToMaybe,+    maybeToEither,+    liftEither,+    liftMaybe,++    -- * Other Helpers+    updateIndex,+    matchTemplate,+    convertBits,++    -- * Triples+    fst3,+    snd3,+    lst3,++    -- * JSON Utilities+    dropFieldLabel,+    dropSumLabels,++    -- * Serialization Helpers+    putList,+    getList,+    putMaybe,+    getMaybe,+    putLengthBytes,+    getLengthBytes,+    putInteger,+    getInteger,+    putInt32be,+    getInt32be,+    putInt64be,+    getInt64be,+    getIntMap,+    putIntMap,+    getTwo,+    putTwo,++    -- * Test Helpers+    prepareContext,+    customCerealID,+    readTestFile,+    readTestFileParser,+  )+where++import Control.Monad+import Control.Monad.Except (ExceptT (..), liftEither)+import Crypto.Secp256k1+import Data.Aeson (eitherDecodeFileStrict)+import Data.Aeson.Encoding+import Data.Aeson.Types+import Data.Base16.Types+import Data.Bits+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.ByteString.Base16+import Data.ByteString.Builder+import Data.ByteString.Lazy qualified as LB+import Data.ByteString.Lazy.Base16 qualified as LB16+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Char (toLower)+import Data.Int+import Data.IntMap (IntMap)+import Data.IntMap qualified as IntMap+import Data.List+import Data.Serialize qualified as S+import Data.Text (Text)+import Data.Text.Encoding qualified as T+import Data.Text.Lazy qualified as LT+import Data.Text.Lazy.Encoding qualified as LT+import Data.Word+import Test.Hspec++-- ByteString helpers++-- | Decode a big endian 'Integer' from a 'ByteString'.+bsToInteger :: ByteString -> Integer+bsToInteger = B.foldr f 0 . B.reverse+  where+    f w n = toInteger w .|. shiftL n 8++-- | Encode an 'Integer' to a 'ByteString' as big endian.+integerToBS :: Integer -> ByteString+integerToBS 0 = B.pack [0]+integerToBS i+  | i > 0 = B.reverse $ B.unfoldr f i+  | otherwise = error "integerToBS not defined for negative values"+  where+    f 0 = Nothing+    f x = Just (fromInteger x :: Word8, x `shiftR` 8)++hexEncoding :: LB.ByteString -> Encoding+hexEncoding b =+  unsafeToEncoding $+    char7 '"' <> hexBuilder b <> char7 '"'++hexBuilder :: LB.ByteString -> Builder+hexBuilder = lazyByteStringHex++encodeHex :: ByteString -> Text+encodeHex = extractBase16 . encodeBase16++-- | Encode as string of human-readable hex characters.+encodeHexLazy :: LB.ByteString -> LT.Text+encodeHexLazy = extractBase16 . LB16.encodeBase16++decodeHex :: Text -> Maybe ByteString+decodeHex t =+  if isBase16 u8+    then Just . decodeBase16 $ assertBase16 u8+    else Nothing+  where+    u8 = T.encodeUtf8 t++-- | Decode string of human-readable hex characters.+decodeHexLazy :: LT.Text -> Maybe LB.ByteString+decodeHexLazy t =+  if LB16.isBase16 u8+    then Just . LB16.decodeBase16 $ assertBase16 u8+    else Nothing+  where+    u8 = LT.encodeUtf8 t++-- | Obtain 'Int' bits from beginning of 'ByteString'. Resulting 'ByteString'+-- will be smallest required to hold that many bits, padded with zeroes to the+-- right.+getBits :: Int -> ByteString -> ByteString+getBits b bs+  | r == 0 = B.take q bs+  | otherwise = i `B.snoc` l+  where+    (q, r) = b `quotRem` 8+    s = B.take (q + 1) bs+    i = B.init s+    l = B.last s .&. (0xff `shiftL` (8 - r)) -- zero unneeded bits++-- Maybe and Either monad helpers++-- | Transform an 'Either' value into a 'Maybe' value. 'Right' is mapped to+-- 'Just' and 'Left' is mapped to 'Nothing'. The value inside 'Left' is lost.+eitherToMaybe :: Either a b -> Maybe b+eitherToMaybe (Right b) = Just b+eitherToMaybe _ = Nothing++-- | Transform a 'Maybe' value into an 'Either' value. 'Just' is mapped to+-- 'Right' and 'Nothing' is mapped to 'Left'. Default 'Left' required.+maybeToEither :: b -> Maybe a -> Either b a+maybeToEither err = maybe (Left err) Right++-- | Lift a 'Maybe' computation into the 'ExceptT' monad.+liftMaybe :: (Monad m) => b -> Maybe a -> ExceptT b m a+liftMaybe err = liftEither . maybeToEither err++-- Various helpers++-- | Applies a function to only one element of a list defined by its index.  If+-- the index is out of the bounds of the list, the original list is returned.+updateIndex ::+  -- | index of the element to change+  Int ->+  -- | list of elements+  [a] ->+  -- | function to apply+  (a -> a) ->+  -- | result with one element changed+  [a]+updateIndex i xs f+  | i < 0 || i >= length xs = xs+  | otherwise = l ++ (f h : r)+  where+    (l, h : r) = splitAt i xs++-- | Use the list @[b]@ as a template and try to match the elements of @[a]@+-- against it. For each element of @[b]@ return the (first) matching element of+-- @[a]@, or 'Nothing'. Output list has same size as @[b]@ and contains results+-- in same order. Elements of @[a]@ can only appear once.+matchTemplate ::+  -- | input list+  [a] ->+  -- | list to serve as a template+  [b] ->+  -- | comparison function+  (a -> b -> Bool) ->+  [Maybe a]+matchTemplate [] bs _ = replicate (length bs) Nothing+matchTemplate _ [] _ = []+matchTemplate as (b : bs) f = case break (`f` b) as of+  (l, r : rs) -> Just r : matchTemplate (l ++ rs) bs f+  _ -> Nothing : matchTemplate as bs f++-- | Returns the first value of a triple.+fst3 :: (a, b, c) -> a+fst3 (a, _, _) = a++-- | Returns the second value of a triple.+snd3 :: (a, b, c) -> b+snd3 (_, b, _) = b++-- | Returns the last value of a triple.+lst3 :: (a, b, c) -> c+lst3 (_, _, c) = c++-- | Field label goes lowercase and first @n@ characters get removed.+dropFieldLabel :: Int -> Options+dropFieldLabel n =+  defaultOptions+    { fieldLabelModifier = map toLower . drop n+    }++-- | Transformation from 'dropFieldLabel' is applied with argument @f@, plus+-- constructor tags are lowercased and first @c@ characters removed. @tag@ is+-- used as the name of the object field name that will hold the transformed+-- constructor tag as its value.+dropSumLabels :: Int -> Int -> String -> Options+dropSumLabels c f tag =+  (dropFieldLabel f)+    { constructorTagModifier = map toLower . drop c,+      sumEncoding = defaultTaggedObject {tagFieldName = tag}+    }++-- | Convert from one power-of-two base to another, as long as it fits in a+-- 'Word'.+convertBits :: Bool -> Int -> Int -> [Word] -> ([Word], Bool)+convertBits pad frombits tobits i = (reverse yout, rem')+  where+    (xacc, xbits, xout) = foldl' outer (0, 0, []) i+    (yout, rem')+      | pad && xbits /= 0 =+          let xout' = (xacc `shiftL` (tobits - xbits)) .&. maxv : xout+           in (xout', False)+      | pad = (xout, False)+      | xbits /= 0 = (xout, True)+      | otherwise = (xout, False)+    maxv = 1 `shiftL` tobits - 1+    max_acc = 1 `shiftL` (frombits + tobits - 1) - 1+    outer (acc, bits, out) it =+      let acc' = ((acc `shiftL` frombits) .|. it) .&. max_acc+          bits' = bits + frombits+          (out', bits'') = inner acc' out bits'+       in (acc', bits'', out')+    inner acc out bits+      | bits >= tobits =+          let bits' = bits - tobits+              out' = ((acc `shiftR` bits') .&. maxv) : out+           in inner acc out' bits'+      | otherwise = (out, bits)++--+-- Serialization helpers+--++putInt32be :: (MonadPut m) => Int32 -> m ()+putInt32be n+  | n < 0 = putWord32be (complement (fromIntegral (abs n)) + 1)+  | otherwise = putWord32be (fromIntegral (abs n))++getInt32be :: (MonadGet m) => m Int32+getInt32be = do+  n <- getWord32be+  if testBit n 31+    then return (negate (complement (fromIntegral n) + 1))+    else return (fromIntegral n)++putInt64be :: (MonadPut m) => Int64 -> m ()+putInt64be n+  | n < 0 = putWord64be (complement (fromIntegral (abs n)) + 1)+  | otherwise = putWord64be (fromIntegral (abs n))++getInt64be :: (MonadGet m) => m Int64+getInt64be = do+  n <- getWord64be+  if testBit n 63+    then return (negate (complement (fromIntegral n) + 1))+    else return (fromIntegral n)++putInteger :: (MonadPut m) => Integer -> m ()+putInteger n+  | n >= lo && n <= hi = do+      putWord8 0x00+      putInt32be (fromIntegral n)+  | otherwise = do+      putWord8 0x01+      putWord8 (fromIntegral (signum n))+      let len = (nrBits (abs n) + 7) `div` 8+      putWord64be (fromIntegral len)+      mapM_ putWord8 (unroll (abs n))+  where+    lo = fromIntegral (minBound :: Int32)+    hi = fromIntegral (maxBound :: Int32)++getInteger :: (MonadGet m) => m Integer+getInteger =+  getWord8 >>= \case+    0 -> fromIntegral <$> getInt32be+    _ -> do+      sign <- getWord8+      bytes <- getList getWord8+      let v = roll bytes+      return $! if sign == 0x01 then v else -v++putMaybe :: (MonadPut m) => (a -> m ()) -> Maybe a -> m ()+putMaybe f Nothing = putWord8 0x00+putMaybe f (Just x) = putWord8 0x01 >> f x++getMaybe :: (MonadGet m) => m a -> m (Maybe a)+getMaybe f =+  getWord8 >>= \case+    0x00 -> return Nothing+    0x01 -> Just <$> f+    _ -> fail "Not a Maybe"++putLengthBytes :: (MonadPut m) => ByteString -> m ()+putLengthBytes bs = do+  putWord64be (fromIntegral (B.length bs))+  putByteString bs++getLengthBytes :: (MonadGet m) => m ByteString+getLengthBytes = do+  len <- fromIntegral <$> getWord64be+  getByteString len++--+-- Fold and unfold an Integer to and from a list of its bytes+--+unroll :: (Integral a, Bits a) => a -> [Word8]+unroll = unfoldr step+  where+    step 0 = Nothing+    step i = Just (fromIntegral i, i `shiftR` 8)++roll :: (Integral a, Bits a) => [Word8] -> a+roll = foldr unstep 0+  where+    unstep b a = a `shiftL` 8 .|. fromIntegral b++nrBits :: (Ord a, Integral a) => a -> Int+nrBits k =+  let expMax = until (\e -> 2 ^ e > k) (* 2) 1+      findNr :: Int -> Int -> Int+      findNr lo hi+        | mid == lo = hi+        | 2 ^ mid <= k = findNr mid hi+        | 2 ^ mid > k = findNr lo mid+        where+          mid = (lo + hi) `div` 2+   in findNr (expMax `div` 2) expMax++-- | Read as a list of pairs of int and element.+getIntMap :: (MonadGet m) => m Int -> m a -> m (IntMap a)+getIntMap i m = IntMap.fromList <$> getList (getTwo i m)++putIntMap :: (MonadPut m) => (Int -> m ()) -> (a -> m ()) -> IntMap a -> m ()+putIntMap f g = putList (putTwo f g) . IntMap.toAscList++putTwo :: (MonadPut m) => (a -> m ()) -> (b -> m ()) -> (a, b) -> m ()+putTwo f g (x, y) = f x >> g y++getTwo :: (MonadGet m) => m a -> m b -> m (a, b)+getTwo f g = (,) <$> f <*> g++putList :: (MonadPut m) => (a -> m ()) -> [a] -> m ()+putList f ls = do+  putWord64be (fromIntegral (length ls))+  mapM_ f ls++getList :: (MonadGet m) => m a -> m [a]+getList f = do+  l <- fromIntegral <$> getWord64be+  replicateM l f++--+-- Test Helpers+--++prepareContext :: (Ctx -> SpecWith a) -> SpecWith a+prepareContext go = do+  ctx <- runIO $ do+    ctx <- createContext+    randomizeContext ctx+    return ctx+  afterAll_ (destroyContext ctx) (go ctx)++customCerealID :: (Eq a) => S.Get a -> S.Putter a -> a -> Bool+customCerealID g p a = (S.runGet g . S.runPut . p) a == Right a++readTestFile :: (FromJSON a) => FilePath -> IO a+readTestFile fp =+  eitherDecodeFileStrict ("data/" <> fp) >>= either (error . message) return+  where+    message aesonErr = "Could not read test file " <> fp <> ": " <> aesonErr++readTestFileParser :: (Value -> Parser a) -> FilePath -> IO a+readTestFileParser p fp = do+  v <- readTestFile fp+  case parse p v of+    Error s -> error s+    Success x -> return x
+ src/Haskoin/Util/Marshal.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE ImportQualifiedPost #-}++module Haskoin.Util.Marshal where++import Control.Monad+import Crypto.Secp256k1+import Data.Aeson+import Data.Aeson.Encoding+import Data.Aeson.Types+import Data.ByteString+import Data.ByteString.Builder+import Data.ByteString.Lazy qualified as Lazy+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial++class Marshal s a | a -> s where+  marshalPut :: (MonadPut m) => s -> a -> m ()+  marshalGet :: (MonadGet m) => s -> m a++marshal :: (Marshal s a) => s -> a -> ByteString+marshal s = runPutS . marshalPut s++marshalLazy :: (Marshal s a) => s -> a -> Lazy.ByteString+marshalLazy s = runPutL . marshalPut s++unmarshal :: (Marshal s a) => s -> ByteString -> Either String a+unmarshal = runGetS . marshalGet++unmarshalLazy :: (Marshal s a) => s -> Lazy.ByteString -> a+unmarshalLazy = runGetL . marshalGet++class MarshalJSON s a | a -> s where+  marshalValue :: s -> a -> Value+  marshalEncoding :: s -> a -> Encoding+  marshalEncoding x = value . marshalValue x+  unmarshalValue :: s -> Value -> Parser a++marshalJSON :: (MarshalJSON s a) => s -> a -> Lazy.ByteString+marshalJSON s = toLazyByteString . fromEncoding . marshalEncoding s++unmarshalJSON :: (MarshalJSON s a) => s -> Lazy.ByteString -> Maybe a+unmarshalJSON s = parseMaybe (unmarshalValue s) <=< decode
− src/Network/Haskoin/Address.hs
@@ -1,299 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE FlexibleContexts  #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Address-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Base58, CashAddr, Bech32 address and WIF private key serialization support.--}-module Network.Haskoin.Address-    ( Address(..)-    , isPubKeyAddress-    , isScriptAddress-    , isWitnessPubKeyAddress-    , isWitnessScriptAddress-    , addrToString-    , stringToAddr-    , addrToJSON-    , addrFromJSON-    , pubKeyAddr-    , pubKeyWitnessAddr-    , pubKeyCompatWitnessAddr-    , p2pkhAddr-    , p2wpkhAddr-    , p2shAddr-    , p2wshAddr-    , inputAddress-    , outputAddress-    , addressToScript-    , addressToScriptBS-    , addressToOutput-    , payToScriptAddress-    , payToWitnessScriptAddress-    , payToNestedScriptAddress-    , scriptToAddress-    , scriptToAddressBS-      -- * Private Key Wallet Import Format (WIF)-    , fromWif-    , toWif-    ) where--import           Control.Applicative-import           Control.DeepSeq-import           Control.Monad-import           Data.Aeson                       as A-import           Data.Aeson.Types-import           Data.ByteString                  (ByteString)-import qualified Data.ByteString                  as B-import           Data.Hashable-import           Data.Maybe-import           Data.Serialize                   as S-import           Data.Text                        (Text)-import           GHC.Generics                     (Generic)-import           Network.Haskoin.Address.Base58-import           Network.Haskoin.Address.Bech32-import           Network.Haskoin.Address.CashAddr-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto-import           Network.Haskoin.Keys.Common-import           Network.Haskoin.Script-import           Network.Haskoin.Util---- | Address format for Bitcoin and Bitcoin Cash.-data Address-    -- | pay to public key hash (regular)-    = PubKeyAddress { getAddrHash160 :: !Hash160-                      -- ^ RIPEMD160 hash of public key's SHA256 hash-                     }-    -- | pay to script hash-    | ScriptAddress { getAddrHash160 :: !Hash160-                      -- ^ RIPEMD160 hash of script's SHA256 hash-                     }-    -- | pay to witness public key hash-    | WitnessPubKeyAddress { getAddrHash160 :: !Hash160-                             -- ^ RIPEMD160 hash of public key's SHA256 hash-                            }-    -- | pay to witness script hash-    | WitnessScriptAddress { getAddrHash256 :: !Hash256-                             -- ^ HASH256 hash of script-                            }-    deriving (Eq, Ord, Generic, Show, Read, Serialize, Hashable, NFData)---- | 'Address' pays to a public key hash.-isPubKeyAddress :: Address -> Bool-isPubKeyAddress PubKeyAddress {} = True-isPubKeyAddress _                = False---- | 'Address' pays to a script hash.-isScriptAddress :: Address -> Bool-isScriptAddress ScriptAddress {} = True-isScriptAddress _                = False---- | 'Address' pays to a witness public key hash. Only valid for SegWit--- networks.-isWitnessPubKeyAddress :: Address -> Bool-isWitnessPubKeyAddress WitnessPubKeyAddress {} = True-isWitnessPubKeyAddress _                       = False---- | 'Address' pays to a witness script hash. Only valid for SegWit networks.-isWitnessScriptAddress :: Address -> Bool-isWitnessScriptAddress WitnessScriptAddress {} = True-isWitnessScriptAddress _                       = False---- | Deserializer for binary 'Base58' addresses.-base58get :: Network -> Get Address-base58get net = do-    pfx <- getWord8-    addr <- S.get-    f pfx addr-  where-    f x a-        | x == getAddrPrefix net = return $ PubKeyAddress a-        | x == getScriptPrefix net = return $ ScriptAddress a-        | otherwise = fail "Does not recognize address prefix"---- | Binary serializer for 'Base58' addresses.-base58put :: Network -> Putter Address-base58put net (PubKeyAddress h) = do-        putWord8 (getAddrPrefix net)-        put h-base58put net (ScriptAddress h) = do-        putWord8 (getScriptPrefix net)-        put h-base58put _ _ = error "Cannot serialize this address as Base58"--addrToJSON :: Network -> Address -> Value-addrToJSON net a = toJSON (addrToString net a)---- | JSON parsing for Bitcoin addresses. Works with 'Base58', 'CashAddr' and--- 'Bech32'.-addrFromJSON :: Network -> Value -> Parser Address-addrFromJSON net =-    withText "address" $ \t ->-        case stringToAddr net t of-            Nothing -> fail "could not decode address"-            Just x  -> return x---- | Convert address to human-readable string. Uses 'Base58', 'Bech32', or--- 'CashAddr' depending on network.-addrToString :: Network -> Address -> Maybe Text-addrToString net a@PubKeyAddress {getAddrHash160 = h}-    | isNothing (getCashAddrPrefix net) =-        Just . encodeBase58Check . runPut $ base58put net a-    | otherwise =-        cashAddrEncode net 0 (S.encode h)-addrToString net a@ScriptAddress {getAddrHash160 = h}-    | isNothing (getCashAddrPrefix net) =-        Just . encodeBase58Check . runPut $ base58put net a-    | otherwise =-        cashAddrEncode net 1 (S.encode h)-addrToString net WitnessPubKeyAddress {getAddrHash160 = h} = do-    hrp <- getBech32Prefix net-    segwitEncode hrp 0 (B.unpack (S.encode h))-addrToString net WitnessScriptAddress {getAddrHash256 = h} = do-    hrp <- getBech32Prefix net-    segwitEncode hrp 0 (B.unpack (S.encode h))---- | Parse 'Base58', 'Bech32' or 'CashAddr' address, depending on network.-stringToAddr :: Network -> Text -> Maybe Address-stringToAddr net bs = cash <|> segwit <|> b58-  where-    b58 = eitherToMaybe . runGet (base58get net) =<< decodeBase58Check bs-    cash = cashAddrDecode net bs >>= \(ver, bs') -> case ver of-        0 -> do-            h <- eitherToMaybe (S.decode bs')-            return $ PubKeyAddress h-        1 -> do-            h <- eitherToMaybe (S.decode bs')-            return $ ScriptAddress h-        _ -> Nothing-    segwit = do-        hrp <- getBech32Prefix net-        (ver, bs') <- segwitDecode hrp bs-        guard (ver == 0)-        let bs'' = B.pack bs'-        case B.length bs'' of-            20 -> do-                h <- eitherToMaybe (S.decode bs'')-                return $ WitnessPubKeyAddress h-            32 -> do-                h <- eitherToMaybe (S.decode bs'')-                return $ WitnessScriptAddress h-            _ -> Nothing---- | Obtain a standard pay-to-public-key-hash address from a public key.-pubKeyAddr :: PubKeyI -> Address-pubKeyAddr = PubKeyAddress . addressHash . S.encode---- | Obtain a standard pay-to-public-key-hash (P2PKH) address from a 'Hash160'.-p2pkhAddr :: Hash160 -> Address-p2pkhAddr = PubKeyAddress---- | Obtain a SegWit pay-to-witness-public-key-hash (P2WPKH) address from a--- public key.-pubKeyWitnessAddr :: PubKeyI -> Address-pubKeyWitnessAddr = WitnessPubKeyAddress . addressHash . S.encode---- | Obtain a backwards-compatible SegWit P2SH-P2WPKH address from a public key.-pubKeyCompatWitnessAddr :: PubKeyI -> Address-pubKeyCompatWitnessAddr =-    p2shAddr .-    addressHash . encodeOutputBS . PayWitnessPKHash . addressHash . S.encode---- | Obtain a SegWit pay-to-witness-public-key-hash (P2WPKH) address from a--- 'Hash160'.-p2wpkhAddr :: Hash160 -> Address-p2wpkhAddr = WitnessPubKeyAddress---- | Obtain a standard pay-to-script-hash (P2SH) address from a 'Hash160'.-p2shAddr :: Hash160 -> Address-p2shAddr = ScriptAddress---- | Obtain a SegWit pay-to-witness-script-hash (P2WSH) address from a 'Hash256'-p2wshAddr :: Hash256 -> Address-p2wshAddr = WitnessScriptAddress---- | Compute a standard pay-to-script-hash (P2SH) address for an output script.-payToScriptAddress :: ScriptOutput -> Address-payToScriptAddress = p2shAddr . addressHash . encodeOutputBS---- | Compute a SegWit pay-to-witness-script-hash (P2WSH) address for an output--- script.-payToWitnessScriptAddress :: ScriptOutput -> Address-payToWitnessScriptAddress = p2wshAddr . sha256 . encodeOutputBS---- | Compute a backwards-compatible SegWit P2SH-P2WSH address.-payToNestedScriptAddress :: ScriptOutput -> Address-payToNestedScriptAddress =-    p2shAddr . addressHash . encodeOutputBS . toP2WSH . encodeOutput---- | Encode an output script from an address. Will fail if using a--- pay-to-witness address on a non-SegWit network.-addressToOutput :: Address -> ScriptOutput-addressToOutput (PubKeyAddress h)        = PayPKHash h-addressToOutput (ScriptAddress h)        = PayScriptHash h-addressToOutput (WitnessPubKeyAddress h) = PayWitnessPKHash h-addressToOutput (WitnessScriptAddress h) = PayWitnessScriptHash h---- | Get output script AST for an 'Address'.-addressToScript :: Address -> Script-addressToScript = encodeOutput . addressToOutput---- | Encode address as output script in 'ByteString' form.-addressToScriptBS :: Address -> ByteString-addressToScriptBS = S.encode . addressToScript---- | Decode an output script into an 'Address' if it has such representation.-scriptToAddress :: Script -> Either String Address-scriptToAddress =-    maybeToEither "Could not decode address" . outputAddress <=< decodeOutput---- | Decode a serialized script into an 'Address'.-scriptToAddressBS :: ByteString -> Either String Address-scriptToAddressBS =-    maybeToEither "Could not decode address" . outputAddress <=< decodeOutputBS---- | Get the 'Address' of a 'ScriptOutput'.-outputAddress :: ScriptOutput -> Maybe Address-outputAddress (PayPKHash h)            = Just $ PubKeyAddress h-outputAddress (PayScriptHash h)        = Just $ ScriptAddress h-outputAddress (PayPK k)                = Just $ pubKeyAddr k-outputAddress (PayWitnessPKHash h)     = Just $ WitnessPubKeyAddress h-outputAddress (PayWitnessScriptHash h) = Just $ WitnessScriptAddress h-outputAddress _                        = Nothing---- | Infer the 'Address' of a 'ScriptInput'.-inputAddress :: ScriptInput -> Maybe Address-inputAddress (RegularInput (SpendPKHash _ key)) = Just $ pubKeyAddr key-inputAddress (ScriptHashInput _ rdm) = Just $ payToScriptAddress rdm-inputAddress _ = Nothing---- | Decode private key from WIF (wallet import format) string.-fromWif :: Network -> Base58 -> Maybe SecKeyI-fromWif net wif = do-    bs <- decodeBase58Check wif-    -- Check that this is a private key-    guard (B.head bs == getSecretPrefix net)-    case B.length bs of-        -- Uncompressed format-        33 -> wrapSecKey False <$> secKey (B.tail bs)-        -- Compressed format-        34 -> do-            guard $ B.last bs == 0x01-            wrapSecKey True <$> secKey (B.tail $ B.init bs)-        -- Bad length-        _  -> Nothing---- | Encode private key into a WIF string.-toWif :: Network -> SecKeyI -> Base58-toWif net (SecKeyI k c) =-    encodeBase58Check . B.cons (getSecretPrefix net) $-    if c-        then getSecKey k `B.snoc` 0x01-        else getSecKey k
− src/Network/Haskoin/Address/Base58.hs
@@ -1,102 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Address.Base58-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Support for legacy 'Base58' addresses. Superseded by Bech32 for Bitcoin SegWit-(BTC) and CashAddr for Bitcoin Cash (BCH).--}-module Network.Haskoin.Address.Base58-    ( Base58-    , encodeBase58-    , decodeBase58-    , encodeBase58Check-    , decodeBase58Check-    ) where--import           Control.Monad-import           Data.ByteString             (ByteString)-import qualified Data.ByteString             as BS-import qualified Data.ByteString.Char8       as C-import           Data.Maybe                  (fromMaybe, isJust, listToMaybe)-import           Data.Serialize              as S-import           Data.String.Conversions     (cs)-import           Data.Text                   (Text)-import qualified Data.Text                   as T-import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Util-import           Numeric                     (readInt, showIntAtBase)---- | 'Base58' classic Bitcoin address format.-type Base58 = Text---- | Symbols for Base58 encoding.-b58Data :: ByteString-b58Data = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"---- | Convert a number less than or equal to provided integer into a 'Base58'--- character.-b58 :: Int -> Char-b58 = C.index b58Data---- | Convert a 'Base58' character into the number it represents.-b58' :: Char -> Maybe Int-b58' = flip C.elemIndex b58Data---- | Encode an arbitrary-length 'Integer' into a 'Base58' string. Leading zeroes--- will not be part of the resulting string.-encodeBase58I :: Integer -> Base58-encodeBase58I i = cs $ showIntAtBase 58 b58 i ""---- | Decode a 'Base58' string into an arbitrary-length 'Integer'.-decodeBase58I :: Base58 -> Maybe Integer-decodeBase58I s =-    case go of-        Just (r,[]) -> Just r-        _           -> Nothing-  where-    p  = isJust . b58'-    f  = fromMaybe e . b58'-    go = listToMaybe $ readInt 58 p f (cs s)-    e  = error "Could not decode base58"---- | Encode an arbitrary 'ByteString' into a its 'Base58' representation,--- preserving leading zeroes.-encodeBase58 :: ByteString -> Base58-encodeBase58 bs =-    l `mappend` r-  where-    (z, b) = BS.span (== 0) bs-    l = cs $ BS.replicate (BS.length z) (BS.index b58Data 0) -- preserve leading 0's-    r | BS.null b = T.empty-      | otherwise = encodeBase58I $ bsToInteger b---- | Decode a 'Base58'-encoded 'Text' to a 'ByteString'.-decodeBase58 :: Base58 -> Maybe ByteString-decodeBase58 t =-    BS.append prefix <$> r-  where-    (z, b) = BS.span (== BS.index b58Data 0) (cs t)-    prefix = BS.replicate (BS.length z) 0 -- preserve leading 1's-    r | BS.null b = Just BS.empty-      | otherwise = integerToBS <$> decodeBase58I (cs b)---- | Computes a checksum for the input 'ByteString' and encodes the input and--- the checksum as 'Base58'.-encodeBase58Check :: ByteString -> Base58-encodeBase58Check bs =-    encodeBase58 $ BS.append bs $ encode $ checkSum32 bs---- | Decode a 'Base58'-encoded string that contains a checksum. This function--- returns 'Nothing' if the input string contains invalid 'Base58' characters or--- if the checksum fails.-decodeBase58Check :: Base58 -> Maybe ByteString-decodeBase58Check bs = do-    rs <- decodeBase58 bs-    let (res, chk) = BS.splitAt (BS.length rs - 4) rs-    guard $ chk == encode (checkSum32 res)-    return res
− src/Network/Haskoin/Address/Bech32.hs
@@ -1,228 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Address.Base58-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Support for Bitcoin SegWit (BTC) Bech32 addresses. This module is a modified-version of Marko Bencun's reference implementation.--}-module Network.Haskoin.Address.Bech32-    ( HRP-    , Bech32-    , Data-    , bech32Encode-    , bech32Decode-    , toBase32-    , toBase256-    , segwitEncode-    , segwitDecode-    , Word5(..)-    , word5-    , fromWord5-    ) where--import           Control.Monad         (guard)-import           Data.Array            (Array, assocs, bounds, listArray, (!),-                                        (//))-import           Data.Bits             (Bits, testBit, unsafeShiftL,-                                        unsafeShiftR, xor, (.&.), (.|.))-import qualified Data.ByteString       as B-import           Data.Char             (toUpper)-import           Data.Foldable         (foldl')-import           Data.Functor.Identity (Identity, runIdentity)-import           Data.Ix               (Ix (..))-import           Data.Text             (Text)-import qualified Data.Text             as T-import qualified Data.Text.Encoding    as E-import           Data.Word             (Word8)---- | Bech32 human-readable string.-type Bech32 = Text---- | Human-readable part of 'Bech32' address.-type HRP = Text---- | Data part of 'Bech32' address.-type Data = [Word8]--(.>>.), (.<<.) :: Bits a => a -> Int -> a-(.>>.) = unsafeShiftR-(.<<.) = unsafeShiftL---- | Five-bit word for Bech32.-newtype Word5 =-    UnsafeWord5 Word8-    deriving (Eq, Ord)--instance Ix Word5 where-    range (UnsafeWord5 m, UnsafeWord5 n) = map UnsafeWord5 $ range (m, n)-    index (UnsafeWord5 m, UnsafeWord5 n) (UnsafeWord5 i) = index (m, n) i-    inRange (m, n) i = m <= i && i <= n---- | Convert an integer number into a five-bit word.-word5 :: Integral a => a -> Word5-word5 x = UnsafeWord5 (fromIntegral x .&. 31)-{-# INLINE word5 #-}-{-# SPECIALIZE INLINE word5 :: Word8 -> Word5 #-}---- | Convert a five-bit word into a number.-fromWord5 :: Num a => Word5 -> a-fromWord5 (UnsafeWord5 x) = fromIntegral x-{-# INLINE fromWord5 #-}-{-# SPECIALIZE INLINE fromWord5 :: Word5 -> Word8 #-}---- | 'Bech32' character map as array of five-bit integers to character.-charset :: Array Word5 Char-charset =-    listArray (UnsafeWord5 0, UnsafeWord5 31) "qpzry9x8gf2tvdw0s3jn54khce6mua7l"---- | Convert a character to its five-bit value from 'Bech32' 'charset'.-charsetMap :: Char -> Maybe Word5-charsetMap c-    | inRange (bounds inv) upperC = inv ! upperC-    | otherwise = Nothing-  where-    upperC = toUpper c-    inv = listArray ('0', 'Z') (repeat Nothing) // map swap (assocs charset)-    swap (a, b) = (toUpper b, Just a)---- | Calculate or validate 'Bech32' checksum.-bech32Polymod :: [Word5] -> Word-bech32Polymod values = foldl' go 1 values .&. 0x3fffffff-  where-    go chk value =-        foldl' xor chk' [g | (g, i) <- zip generator [25 ..], testBit chk i]-      where-        generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]-        chk' = chk .<<. 5 `xor` fromWord5 value---- | Convert human-readable part of 'Bech32' string into a list of five-bit--- words.-bech32HRPExpand :: HRP -> [Word5]-bech32HRPExpand hrp =-    map (UnsafeWord5 . (.>>. 5)) hrpBytes ++-    [UnsafeWord5 0] ++ map word5 hrpBytes-  where-    hrpBytes = B.unpack $ E.encodeUtf8 hrp---- | Calculate checksum for a string of five-bit words.-bech32CreateChecksum :: HRP -> [Word5] -> [Word5]-bech32CreateChecksum hrp dat = [word5 (polymod .>>. i) | i <- [25,20 .. 0]]-  where-    values = bech32HRPExpand hrp ++ dat-    polymod =-        bech32Polymod (values ++ map UnsafeWord5 [0, 0, 0, 0, 0, 0]) `xor` 1---- | Verify checksum for a human-readable part and string of five-bit words.-bech32VerifyChecksum :: HRP -> [Word5] -> Bool-bech32VerifyChecksum hrp dat = bech32Polymod (bech32HRPExpand hrp ++ dat) == 1---- | Maximum length of a Bech32 result.-maxBech32Length :: Int-maxBech32Length = 90---- | Encode string of five-bit words into 'Bech32' using a provided--- human-readable part. Can fail if 'HRP' is invalid or result would be longer--- than 90 characters.-bech32Encode :: HRP -> [Word5] -> Maybe Bech32-bech32Encode hrp dat = do-    guard $ checkHRP hrp-    let dat' = dat ++ bech32CreateChecksum (T.toLower hrp) dat-        rest = map (charset !) dat'-        result = T.concat [T.toLower hrp, T.pack "1", T.pack rest]-    guard $ T.length result <= maxBech32Length-    return result---- | Check that human-readable part is valid for a 'Bech32' string.-checkHRP :: HRP -> Bool-checkHRP hrp = not (T.null hrp) && T.all (\char -> char >= '\x21' && char <= '\x7e') hrp---- | Decode human-readable 'Bech32' string into a human-readable part and a--- string of five-bit words.-bech32Decode :: Bech32 -> Maybe (HRP, [Word5])-bech32Decode bech32 = do-    guard $ T.length bech32 <= maxBech32Length-    guard $ T.toUpper bech32 == bech32 || lowerBech32 == bech32-    let (hrp, dat) = T.breakOnEnd "1" lowerBech32-    guard $ T.length dat >= 6-    hrp' <- T.stripSuffix "1" hrp-    guard $ checkHRP hrp'-    dat' <- mapM charsetMap $ T.unpack dat-    guard $ bech32VerifyChecksum hrp' dat'-    return (hrp', take (T.length dat - 6) dat')-  where-    lowerBech32 = T.toLower bech32--type Pad f = Int -> Int -> Word -> [[Word]] -> f [[Word]]--yesPadding :: Pad Identity-yesPadding _ 0 _ result        = return result-yesPadding _ _ padValue result = return $ [padValue] : result-{-# INLINE yesPadding #-}--noPadding :: Pad Maybe-noPadding frombits bits padValue result = do-    guard $ bits < frombits && padValue == 0-    return result-{-# INLINE noPadding #-}---- | Big endian conversion of a bytestring from base \(2^{frombits}\) to base--- \(2^{tobits}\). {frombits} and {twobits} must be positive and--- \(2^{frombits}\) and \(2^{tobits}\) must be smaller than the size of Word.--- Every value in 'dat' must be strictly smaller than \(2^{frombits}\).-convertBits :: Functor f => [Word] -> Int -> Int -> Pad f -> f [Word]-convertBits dat frombits tobits pad = concat . reverse <$> go dat 0 0 []-  where-    go [] acc bits result =-        let padValue = (acc .<<. (tobits - bits)) .&. maxv-        in pad frombits bits padValue result-    go (value:dat') acc bits result =-        go dat' acc' (bits' `rem` tobits) (result' : result)-      where-        acc' = (acc .<<. frombits) .|. fromIntegral value-        bits' = bits + frombits-        result' =-            [ (acc' .>>. b) .&. maxv-            | b <- [bits' - tobits,bits' - 2 * tobits .. 0]-            ]-    maxv = (1 .<<. tobits) - 1-{-# INLINE convertBits #-}---- | Convert from eight-bit to five-bit word string, adding padding as required.-toBase32 :: [Word8] -> [Word5]-toBase32 dat =-    map word5 $ runIdentity $ convertBits (map fromIntegral dat) 8 5 yesPadding---- | Convert from five-bit word string to eight-bit word string, ignoring padding.-toBase256 :: [Word5] -> Maybe [Word8]-toBase256 dat =-    map fromIntegral <$> convertBits (map fromWord5 dat) 5 8 noPadding---- | Check if witness version and program are valid.-segwitCheck :: Word8 -> Data -> Bool-segwitCheck witver witprog =-    witver <= 16 &&-    if witver == 0-        then length witprog == 20 || length witprog == 32-        else length witprog >= 2 && length witprog <= 40---- | Decode SegWit 'Bech32' address from a string and expected human-readable part.-segwitDecode :: HRP -> Bech32 -> Maybe (Word8, Data)-segwitDecode hrp addr = do-    (hrp', dat) <- bech32Decode addr-    guard $ (hrp == hrp') && not (null dat)-    let (UnsafeWord5 witver:datBase32) = dat-    decoded <- toBase256 datBase32-    guard $ segwitCheck witver decoded-    return (witver, decoded)---- | Encode 'Data' as a SegWit 'Bech32' address. Needs human-readable part and--- witness program version.-segwitEncode :: HRP -> Word8 -> Data -> Maybe Text-segwitEncode hrp witver witprog = do-    guard $ segwitCheck witver witprog-    bech32Encode hrp $ UnsafeWord5 witver : toBase32 witprog
− src/Network/Haskoin/Address/CashAddr.hs
@@ -1,198 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Address.CashAddr-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Support for Bitcoin Cash (BCH) CashAddr format.--}-module Network.Haskoin.Address.CashAddr-    ( CashPrefix-    , CashVersion-    , CashAddr-    , Cash32-    , cashAddrDecode-    , cashAddrEncode-    , cash32decodeType-    , cash32encodeType-    , cash32decode-    , cash32encode-    ) where--import           Control.Monad-import           Data.Bits-import           Data.ByteString            (ByteString)-import qualified Data.ByteString            as B-import qualified Data.ByteString.Char8      as C-import           Data.Char-import           Data.List-import           Data.Text                  (Text)-import qualified Data.Text                  as T-import qualified Data.Text.Encoding         as E-import           Data.Word-import           Network.Haskoin.Constants-import           Network.Haskoin.Util---- | 'CashAddr' prefix, usually shown before the colon in addresses, but sometimes--- omitted. It is used in the checksum calculation to avoid parsing an address--- from the wrong network.-type CashPrefix = Text---- | 'CashAddr' version, until new address schemes appear it will be zero.-type CashVersion = Word8---- | High level 'CashAddr' human-reabale string, with explicit or implicit prefix.-type CashAddr = Text---- | Low level 'Cash32' is the human-readable low-level encoding used by 'CashAddr'. It--- need not encode a valid address but any binary data.-type Cash32 = Text---- | Symbols for encoding 'Cash32' data in human-readable strings.-charset :: String-charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"---- | Get the 32-bit number associated with this 'Cash32' character.-base32char :: Char -> Maybe Word8-base32char = fmap fromIntegral . (`elemIndex` charset)---- | High-Level: decode 'CashAddr' string if it is valid for the--- provided 'Network'. Prefix may be omitted from the string.-cashAddrDecode :: Network -> CashAddr -> Maybe (CashVersion, ByteString)-cashAddrDecode net ca = do-    epfx <- getCashAddrPrefix net-    let (cpfx, cdat) = T.breakOnEnd ":" (T.toLower ca)-    guard (T.null cpfx || T.init cpfx == epfx)-    (dpfx, ver, bs) <- cash32decodeType (epfx <> ":" <> cdat)-    guard (dpfx == epfx)-    return (ver, bs)---- | High-Level: encode 'CashAddr' string for the provided network and hash.--- Fails if the 'CashVersion' or length of hash 'ByteString' is invalid.-cashAddrEncode :: Network -> CashVersion -> ByteString -> Maybe CashAddr-cashAddrEncode net cv bs = do-    pfx <- getCashAddrPrefix net-    cash32encodeType pfx cv bs---- | Mid-Level: decode 'CashAddr' string containing arbitrary prefix, plus a--- version byte before the 'ByteString' that encodes type and length.-cash32decodeType :: Cash32 -> Maybe (CashPrefix, CashVersion, ByteString)-cash32decodeType ca' = do-    guard (T.toUpper ca' == ca' || ca == ca')-    (dpfx, bs) <- cash32decode ca-    guard (not (B.null bs))-    let vb = B.head bs-        pay = B.tail bs-    (ver, len) <- decodeVersionByte vb-    guard (B.length pay == len)-    return (dpfx, ver, pay)-  where-    ca = T.toLower ca'---- | Mid-Level: encode 'CashAddr' string containing arbitrary prefix and--- 'CashVersion'. Length must be among those allowed by the standard.-cash32encodeType :: CashPrefix -> CashVersion -> ByteString -> Maybe Cash32-cash32encodeType pfx cv bs = do-    let len = B.length bs-    vb <- encodeVersionByte cv len-    let pl = vb `B.cons` bs-    return (cash32encode pfx pl)---- | Low-Level: decode 'Cash32' string. 'CashPrefix' must be part of the string.--- No version or hash length validation is performed.-cash32decode :: Cash32 -> Maybe (CashPrefix, ByteString)-cash32decode text = do-    let bs = C.map toLower bs'-    guard (C.map toUpper bs' == bs' || bs == bs')-    let (pfx', dat) = C.breakEnd (== ':') bs-    pfx <--        if B.null pfx' || pfx' == C.singleton ':'-            then Nothing-            else Just (B.init pfx')-    b32 <- B.pack <$> mapM base32char (C.unpack dat)-    let px = B.map (.&. 0x1f) pfx-        pd = px <> B.singleton 0 <> b32-        cs = cash32Polymod pd-        bb = B.take (B.length b32 - 8) b32-    guard (verifyCash32Polymod cs)-    let out = toBase256 bb-    return (E.decodeUtf8 pfx, out)-  where-    bs' = E.encodeUtf8 text---- | Low-Level: encode 'Cash32' string for 'CashPrefix' provided. Can encode--- arbitrary data. No prefix or length validation is performed.-cash32encode :: CashPrefix -> ByteString -> Cash32-cash32encode pfx bs =-    let b32 = toBase32 bs-        px = B.map (.&. 0x1f) (E.encodeUtf8 pfx)-        pd = px <> B.singleton 0 <> b32 <> B.replicate 8 0-        cs = cash32Polymod pd-        c32 = B.map f (b32 <> cs)-        f = fromIntegral . ord . (charset !!) . fromIntegral-    in pfx <> ":" <> E.decodeUtf8 c32---- | Convert base of 'ByteString' from eight bits per byte to five bits per--- byte, adding padding as necessary.-toBase32 :: ByteString -> ByteString-toBase32 =-    B.pack .-    map fromIntegral . fst . convertBits True 8 5 . map fromIntegral . B.unpack---- | Convert base of 'ByteString' from five to eight bits per byte. Ignore--- padding to be symmetric with respect to 'toBase32' function.-toBase256 :: ByteString -> ByteString-toBase256 =-    B.pack .-    map fromIntegral . fst . convertBits False 5 8 . map fromIntegral . B.unpack---- | Obtain 'CashVersion' and payload length from 'CashAddr' version byte.-decodeVersionByte :: Word8 -> Maybe (CashVersion, Int)-decodeVersionByte vb = do-    guard (vb .&. 0x80 == 0)-    return (ver, len)-  where-    ver = vb `shiftR` 3-    len = ls !! fromIntegral (vb .&. 0x07)-    ls = [20, 24, 28, 32, 40, 48, 56, 64]---- | Encode 'CashVersion' and length into version byte. Fail if version is--- larger than five bits, or length incorrect, since that is invalid.-encodeVersionByte :: CashVersion -> Int -> Maybe Word8-encodeVersionByte ver len = do-    guard (ver == ver .&. 0x0f)-    l <- case len of-        20 -> Just 0-        24 -> Just 1-        28 -> Just 2-        32 -> Just 3-        40 -> Just 4-        48 -> Just 5-        56 -> Just 6-        64 -> Just 7-        _  -> Nothing-    return ((ver `shiftL` 3) .|. l)---- | Calculate or validate checksum from base32 'ByteString' (excluding prefix).-cash32Polymod :: ByteString -> ByteString-cash32Polymod v =-    B.pack-        [fromIntegral (polymod `shiftR` (5 * (7 - i))) .&. 0x1f | i <- [0 .. 7]]-  where-    polymod = B.foldl' outer (1 :: Word64) v `xor` 1-    outer c d =-        let c0 = (fromIntegral (c `shiftR` 35) :: Word8)-            c' = ((c .&. 0x07ffffffff) `shiftL` 5) `xor` fromIntegral d-        in foldl' (inner c0) c' (zip [0 ..] generator)-    generator =-        [0x98f2bc8e61, 0x79b76d99e2, 0xf33e5fb3c4, 0xae2eabe2a8, 0x1e4f43e470]-    inner c0 c (b, g)-        | c0 `testBit` b = c `xor` g-        | otherwise = c---- | Validate that polymod 'ByteString' (eight bytes) is equal to zero.-verifyCash32Polymod :: ByteString -> Bool-verifyCash32Polymod = (== B.replicate 8 0)
− src/Network/Haskoin/Block.hs
@@ -1,43 +0,0 @@-{-|-Module      : Network.Haskoin.Block-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Most functions relating to blocks are exported by this module.--}-module Network.Haskoin.Block-    ( module Network.Haskoin.Block.Common-      -- * Block Header Chain-    , BlockWork-    , BlockHeaders(..)-    , BlockNode(..)-    , HeaderMemory(..)-    , BlockMap-    , getAncestor-    , isGenesis-    , initialChain-    , genesisMap-    , genesisNode-    , genesisBlock-    , connectBlocks-    , connectBlock-    , parentBlock-    , splitPoint-    , blockLocator-      -- * Merkle Blocks-    , MerkleBlock(..)-    , MerkleRoot-    , FlagBits-    , PartialMerkleTree-    , buildMerkleRoot-    , buildPartialMerkle-    , merkleBlockTxs-    , testMerkleRoot-    ) where--import           Network.Haskoin.Block.Headers-import           Network.Haskoin.Block.Merkle-import           Network.Haskoin.Block.Common
− src/Network/Haskoin/Block/Common.hs
@@ -1,339 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric  #-}-{-|-Module      : Network.Haskoin.Block.Common-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Common data types and functions to handle blocks from the block chain.--}-module Network.Haskoin.Block.Common-    ( Block(..)-    , BlockHeight-    , Timestamp-    , BlockHeader(..)-    , headerHash-    , BlockLocator-    , GetBlocks(..)-    , GetHeaders(..)-    , BlockHeaderCount-    , BlockHash(..)-    , blockHashToHex-    , hexToBlockHash-    , Headers(..)-    , decodeCompact-    , encodeCompact-    ) where--import           Control.DeepSeq-import           Control.Monad                      (forM_, liftM2, mzero,-                                                     replicateM)-import           Data.Aeson                         (FromJSON, ToJSON,-                                                     Value (String), parseJSON,-                                                     toJSON, withText)-import           Data.Bits                          (shiftL, shiftR, (.&.),-                                                     (.|.))-import qualified Data.ByteString                    as B-import           Data.Hashable                      (Hashable)-import           Data.Maybe                         (fromMaybe)-import           Data.Serialize                     (Serialize, decode, encode,-                                                     get, put)-import           Data.Serialize.Get                 (getWord32le)-import           Data.Serialize.Put                 (Put, putWord32le)-import           Data.String                        (IsString, fromString)-import           Data.String.Conversions            (cs)-import           Data.Text                          (Text)-import           Data.Word                          (Word32)-import           GHC.Generics                       (Generic)-import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Network.Common-import           Network.Haskoin.Transaction.Common-import           Network.Haskoin.Util-import qualified Text.Read                          as R---- | Height of a block in the block chain, starting at 0 for Genesis.-type BlockHeight = Word32---- | Block timestamp as Unix time (seconds since 1970-01-01 00:00 UTC).-type Timestamp = Word32---- | Block header and transactions.-data Block =-    Block { blockHeader :: !BlockHeader-          , blockTxns   :: ![Tx]-          } deriving (Eq, Show, Read, Generic, Hashable, NFData)--instance Serialize Block where-    get = do-        header     <- get-        (VarInt c) <- get-        txs        <- replicateM (fromIntegral c) get-        return $ Block header txs-    put (Block h txs) = do-        put h-        putVarInt $ length txs-        forM_ txs put--instance ToJSON Block where-    toJSON = String . encodeHex . encode--instance FromJSON Block where-    parseJSON =-        withText "Block" $ \t -> do-            bin <--                case decodeHex t of-                    Nothing  -> mzero-                    Just bin -> return bin-            case decode bin of-                Left e  -> fail e-                Right h -> return h--instance ToJSON BlockHeader where-    toJSON = String . encodeHex . encode--instance FromJSON BlockHeader where-    parseJSON =-        withText "BlockHeader" $ \t -> do-            bin <--                case decodeHex t of-                    Nothing  -> mzero-                    Just bin -> return bin-            case decode bin of-                Left e  -> fail e-                Right h -> return h---- | Block header hash. To be serialized reversed for display purposes.-newtype BlockHash = BlockHash-    { getBlockHash :: Hash256 }-    deriving (Eq, Ord, Generic, Hashable, Serialize, NFData)--instance Show BlockHash where-    showsPrec _ = shows . blockHashToHex--instance Read BlockHash where-    readPrec = do-        R.String str <- R.lexP-        maybe R.pfail return $ hexToBlockHash $ cs str--instance IsString BlockHash where-    fromString s =-        let e = error "Could not read block hash from hex string"-        in fromMaybe e $ hexToBlockHash $ cs s--instance FromJSON BlockHash where-    parseJSON = withText "block hash" $-        maybe mzero return . hexToBlockHash--instance ToJSON BlockHash where-    toJSON = String . blockHashToHex---- | Block hashes are reversed with respect to the in-memory byte order in a--- block hash when displayed.-blockHashToHex :: BlockHash -> Text-blockHashToHex (BlockHash h) = encodeHex (B.reverse (encode h))---- | Convert a human-readable hex block hash into a 'BlockHash'. Bytes are--- reversed as normal.-hexToBlockHash :: Text -> Maybe BlockHash-hexToBlockHash hex = do-    bs <- B.reverse <$> decodeHex hex-    h <- eitherToMaybe (decode bs)-    return $ BlockHash h---- | Data type recording information of a 'Block'. The hash of a block is--- defined as the hash of this data structure, serialized. The block mining--- process involves finding a partial hash collision by varying the nonce in the--- 'BlockHeader' and/or additional entropy in the coinbase 'Transaction' of this--- 'Block'. Variations in the coinbase will result in different merkle roots in--- the 'BlockHeader'.-data BlockHeader =-    BlockHeader { blockVersion   :: !Word32      --  4 bytes-                  -- | hash of the previous block (parent)-                , prevBlock      :: !BlockHash   -- 32 bytes-                  -- | root of the merkle tree of transactions-                , merkleRoot     :: !Hash256     -- 32 bytes-                  -- | unix timestamp-                , blockTimestamp :: !Timestamp   --  4 bytes-                  -- | difficulty target-                , blockBits      :: !Word32      --  4 bytes-                  -- | random nonce-                , bhNonce        :: !Word32      --  4 bytes-                } deriving (Eq, Ord, Show, Read, Generic, Hashable, NFData)-                                                 -- 80 bytes---- | Compute hash of 'BlockHeader'.-headerHash :: BlockHeader -> BlockHash-headerHash = BlockHash . doubleSHA256 . encode--instance Serialize BlockHeader where-    get = do-        v <- getWord32le-        p <- get-        m <- get-        t <- getWord32le-        b <- getWord32le-        n <- getWord32le-        return BlockHeader-            { blockVersion   = v-            , prevBlock      = p-            , merkleRoot     = m-            , blockTimestamp = t-            , blockBits      = b-            , bhNonce        = n-            }--    put (BlockHeader v p m bt bb n) = do-        putWord32le v-        put         p-        put         m-        putWord32le bt-        putWord32le bb-        putWord32le n---- | A block locator is a set of block headers, denser towards the best block--- and sparser towards the genesis block. It starts at the highest block known.--- It is used by a node to synchronize against the network. When the locator is--- provided to a peer, it will send back block hashes starting from the first--- block in the locator that it recognizes.-type BlockLocator = [BlockHash]---- | Data type representing a getblocks message request. It is used in the--- bitcoin protocol to retrieve blocks from a peer by providing it a--- 'BlockLocator' object. The response to a 'GetBlocks' message is an 'Inv'--- message containing a list of block hashes that the peer believes this node is--- missing. The number of block hashes in that inv message will end at the stop--- block hash, at at the tip of the chain, or after 500 entries, whichever comes--- earlier.-data GetBlocks =-    GetBlocks { -- | protocol version.-                getBlocksVersion  :: !Word32-                -- | block locator object-              , getBlocksLocator  :: !BlockLocator-                -- | hash of the last desired block-              , getBlocksHashStop :: !BlockHash-              } deriving (Eq, Show, Generic, NFData)--instance Serialize GetBlocks where--    get = GetBlocks <$> getWord32le-                    <*> (repList =<< get)-                    <*> get-      where-        repList (VarInt c) = replicateM (fromIntegral c) get--    put (GetBlocks v xs h) = putGetBlockMsg v xs h--putGetBlockMsg :: Word32 -> BlockLocator -> BlockHash -> Put-putGetBlockMsg v xs h = do-    putWord32le v-    putVarInt $ length xs-    forM_ xs put-    put h---- | Similar to the 'GetBlocks' message type but for retrieving block headers--- only. The response to a 'GetHeaders' request is a 'Headers' message--- containing a list of block headers. A maximum of 2000 block headers can be--- returned. 'GetHeaders' is used by simplified payment verification (SPV)--- clients to exclude block contents when synchronizing the block chain.-data GetHeaders =-    GetHeaders {-                 -- | protocol version-                 getHeadersVersion  :: !Word32-                 -- | block locator object-               , getHeadersBL       :: !BlockLocator-                 -- | hash of the last desired block header-               , getHeadersHashStop :: !BlockHash-               } deriving (Eq, Show, Generic, NFData)--instance Serialize GetHeaders where--    get = GetHeaders <$> getWord32le-                     <*> (repList =<< get)-                     <*> get-      where-        repList (VarInt c) = replicateM (fromIntegral c) get--    put (GetHeaders v xs h) = putGetBlockMsg v xs h---- | 'BlockHeader' type with a transaction count as 'VarInt'-type BlockHeaderCount = (BlockHeader, VarInt)---- | The 'Headers' type is used to return a list of block headers in--- response to a 'GetHeaders' message.-newtype Headers =-    Headers { -- | list of block headers with transaction count-              headersList :: [BlockHeaderCount]-            }-    deriving (Eq, Show, Generic, NFData)--instance Serialize Headers where--    get = Headers <$> (repList =<< get)-      where-        repList (VarInt c) = replicateM (fromIntegral c) action-        action = liftM2 (,) get get--    put (Headers xs) = do-        putVarInt $ length xs-        forM_ xs $ \(a,b) -> put a >> put b---- | Decode the compact number used in the difficulty target of a block.------ The compact format is a representation of a whole number \(N\) using an--- unsigned 32-bit number similar to a floating point format. The most--- significant 8 bits are the unsigned exponent of base 256. This exponent can--- be thought of as the number of bytes of \(N\). The lower 23 bits are the--- mantissa. Bit number 24 represents the sign of \(N\).------ \[--- N = -1^{sign} \times mantissa \times 256^{exponent-3}--- \]-decodeCompact :: Word32 -> (Integer, Bool) -- ^ true means overflow-decodeCompact nCompact = (if neg then res * (-1) else res, over)-  where-    nSize :: Int-    nSize = fromIntegral nCompact `shiftR` 24-    nWord' :: Word32-    nWord' = nCompact .&. 0x007fffff-    nWord :: Word32-    nWord | nSize <= 3 = nWord' `shiftR` (8 * (3 - nSize))-          | otherwise = nWord'-    res :: Integer-    res | nSize <= 3 = fromIntegral nWord-        | otherwise = fromIntegral nWord `shiftL` (8 * (nSize - 3))-    neg = nWord /= 0 && (nCompact .&. 0x00800000) /= 0-    over = nWord /= 0 && (nSize > 34 ||-                          nWord > 0xff && nSize > 33 ||-                          nWord > 0xffff && nSize > 32)---- | Encode an 'Integer' to the compact number format used in the difficulty--- target of a block.-encodeCompact :: Integer-              -> Word32-encodeCompact i = nCompact-  where-    i' = abs i-    neg = i < 0-    nSize' :: Int-    nSize' = let f 0 = 0-                 f n = 1 + f (n `shiftR` 8)-             in f i'-    nCompact''' :: Word32-    nCompact'''-        | nSize' <= 3 = fromIntegral $ (low64 .&. i') `shiftL` (8 * (3 - nSize'))-        | otherwise = fromIntegral $ low64 .&. (i' `shiftR` (8 * (nSize' - 3)))-    nCompact'' :: Word32-    nSize :: Int-    (nCompact'', nSize)-        | nCompact''' .&. 0x00800000 /= 0 = (nCompact''' `shiftR` 8, nSize' + 1)-        | otherwise = (nCompact''', nSize')-    nCompact' :: Word32-    nCompact' = nCompact'' .|. (fromIntegral nSize `shiftL` 24)-    nCompact :: Word32-    nCompact | neg && (nCompact' .&. 0x007fffff /= 0) = nCompact' .|. 0x00800000-             | otherwise = nCompact'-    low64 :: Integer-    low64 = 0xffffffffffffffff
− src/Network/Haskoin/Block/Headers.hs
@@ -1,741 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE FlexibleContexts  #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE LambdaCase        #-}-{-# LANGUAGE RecordWildCards   #-}-{-|-Module      : Network.Haskoin.Block.Headers-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Block chain header synchronization and proof-of-work consensus functions.--}-module Network.Haskoin.Block.Headers-    ( BlockNode(..)-    , BlockHeaders(..)-    , BlockWork-    , genesisNode-    , genesisBlock-    , isGenesis-    , chooseBest-      -- * Header Chain Storage Functions-    , parentBlock-    , getParents-    , getAncestor-    , splitPoint-    , connectBlocks-    , connectBlock-    , blockLocator-      -- * In-Memory Header Chain Store-    , HeaderMemory(..)-    , ShortBlockHash-    , BlockMap-    , shortBlockHash-    , initialChain-    , genesisMap-      -- * Helper Functions-    , appendBlocks-    , validBlock-    , validCP-    , afterLastCP-    , bip34-    , validVersion-    , lastNoMinDiff-    , nextWorkRequired-    , nextEdaWorkRequired-    , nextDaaWorkRequired-    , computeTarget-    , getSuitableBlock-    , nextPowWorkRequired-    , calcNextWork-    , isValidPOW-    , blockPOW-    , headerWork-    , diffInterval-    , blockLocatorNodes-    , mineBlock-    , computeSubsidy-    , ) where--import           Control.Applicative                ((<|>))-import           Control.DeepSeq-import           Control.Monad                      (guard, unless, when)-import           Control.Monad.Except               (ExceptT (..), runExceptT,-                                                     throwError)-import           Control.Monad.State.Strict         as State (StateT, get, gets,-                                                              lift, modify)-import           Control.Monad.Trans.Maybe-import           Data.Bits                          (shiftL, shiftR, (.&.))-import qualified Data.ByteString                    as B-import           Data.ByteString.Short              (ShortByteString, fromShort,-                                                     toShort)-import           Data.Function                      (on)-import           Data.Hashable-import           Data.HashMap.Strict                (HashMap)-import qualified Data.HashMap.Strict                as HashMap-import           Data.List                          (sort, sortBy)-import           Data.Maybe                         (fromMaybe, listToMaybe)-import           Data.Serialize                     as S (Serialize (..),-                                                          decode, encode, get,-                                                          put)-import           Data.Serialize.Get                 as S-import           Data.Serialize.Put                 as S-import           Data.Typeable                      (Typeable)-import           Data.Word                          (Word32, Word64)-import           GHC.Generics                       (Generic)-import           Network.Haskoin.Block.Common-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto-import           Network.Haskoin.Transaction.Common-import           Network.Haskoin.Util---- | Short version of the block hash. Uses the good end of the hash (the part--- that doesn't have a long string of zeroes).-type ShortBlockHash = Word64---- | Memory-based map to a serialized 'BlockNode' data structure.--- 'ShortByteString' is used to avoid memory fragmentation and make the data--- structure compact.-type BlockMap = HashMap ShortBlockHash ShortByteString---- | Represents accumulated work in the block chain so far.-type BlockWork = Integer---- | Data structure representing a block header and its position in the--- block chain.-data BlockNode-    -- | non-Genesis block header-    = BlockNode { nodeHeader :: !BlockHeader-                , nodeHeight :: !BlockHeight-        -- | accumulated work so far-                , nodeWork   :: !BlockWork-        -- | akip magic block hash-                , nodeSkip   :: !BlockHash }-    -- | Genesis block header-    | GenesisNode { nodeHeader :: !BlockHeader-                  , nodeHeight :: !BlockHeight-                  , nodeWork   :: !BlockWork }-    deriving (Show, Read, Generic, Hashable, NFData)--instance Serialize BlockNode where-    get = do-        nodeHeader <- S.get-        nodeHeight <- getWord32le-        nodeWork <- S.get-        if nodeHeight == 0-            then return GenesisNode {..}-            else do-                nodeSkip <- S.get-                return BlockNode {..}-    put bn = do-        put $ nodeHeader bn-        putWord32le $ nodeHeight bn-        put $ nodeWork bn-        case bn of-            GenesisNode {} -> return ()-            BlockNode {}   -> put $ nodeSkip bn--instance Eq BlockNode where-    (==) = (==) `on` nodeHeader--instance Ord BlockNode where-    compare = compare `on` nodeHeight---- | Memory-based header tree.-data HeaderMemory = HeaderMemory-    { memoryHeaderMap  :: !BlockMap-    , memoryBestHeader :: !BlockNode-    } deriving (Eq, Typeable, Show, Read, Generic, Hashable, NFData)---- | Typeclass for block header chain storage monad.-class Monad m => BlockHeaders m where-    -- | Add a new 'BlockNode' to the chain. Does not validate.-    addBlockHeader :: BlockNode -> m ()-    -- | Get a 'BlockNode' associated with a 'BlockHash'.-    getBlockHeader :: BlockHash -> m (Maybe BlockNode)-    -- | Locate the 'BlockNode' for the highest block in the chain-    getBestBlockHeader :: m BlockNode-    -- | Set the highest block in the chain.-    setBestBlockHeader :: BlockNode -> m ()-    -- | Add a continuous bunch of block headers the chain. Does not validate.-    addBlockHeaders :: [BlockNode] -> m ()-    addBlockHeaders = mapM_ addBlockHeader--instance Monad m => BlockHeaders (StateT HeaderMemory m) where-    addBlockHeader = modify . addBlockHeaderMemory-    getBlockHeader bh = getBlockHeaderMemory bh <$> State.get-    getBestBlockHeader = gets memoryBestHeader-    setBestBlockHeader bn = modify $ \s -> s { memoryBestHeader = bn }---- | Initialize memory-based chain.-initialChain :: Network -> HeaderMemory-initialChain net = HeaderMemory-    { memoryHeaderMap = genesisMap net-    , memoryBestHeader = genesisNode net-    }---- | Initialize map for memory-based chain.-genesisMap :: Network -> BlockMap-genesisMap net =-    HashMap.singleton-        (shortBlockHash (headerHash (getGenesisHeader net)))-        (toShort (encode (genesisNode net)))---- | Add block header to memory block map.-addBlockHeaderMemory :: BlockNode -> HeaderMemory -> HeaderMemory-addBlockHeaderMemory bn s@HeaderMemory{..} =-    let bm' = addBlockToMap bn memoryHeaderMap-    in s { memoryHeaderMap = bm' }---- | Get block header from memory block map.-getBlockHeaderMemory :: BlockHash -> HeaderMemory -> Maybe BlockNode-getBlockHeaderMemory bh HeaderMemory {..} = do-    bs <- shortBlockHash bh `HashMap.lookup` memoryHeaderMap-    eitherToMaybe . decode $ fromShort bs---- | Calculate short block hash taking eight non-zero bytes from the 16-byte--- hash. This function will take the bytes that are not on the zero-side of the--- hash, making colissions between short block hashes difficult.-shortBlockHash :: BlockHash -> ShortBlockHash-shortBlockHash = either error id . decode . B.take 8 . encode---- | Add a block to memory-based block map.-addBlockToMap :: BlockNode -> BlockMap -> BlockMap-addBlockToMap node =-    HashMap.insert-    (shortBlockHash $ headerHash $ nodeHeader node)-    (toShort $ encode node)---- | Get the ancestor of the provided 'BlockNode' at the specified--- 'BlockHeight'.-getAncestor :: BlockHeaders m-            => BlockHeight-            -> BlockNode-            -> m (Maybe BlockNode)-getAncestor height node-    | height > nodeHeight node = return Nothing-    | otherwise = go node-  where-    e1 = error "Could not get skip header"-    e2 = error "Could not get previous block header"-    go walk-        | nodeHeight walk > height =-            let heightSkip = skipHeight (nodeHeight walk)-                heightSkipPrev = skipHeight (nodeHeight walk - 1)-             in if not (isGenesis walk) &&-                   (heightSkip == height ||-                    (heightSkip > height &&-                     not-                         (heightSkipPrev < heightSkip - 2 &&-                          heightSkipPrev >= height)))-                    then do-                        walk' <- fromMaybe e1 <$> getBlockHeader (nodeSkip walk)-                        go walk'-                    else do-                        walk' <--                            fromMaybe e2 <$>-                            getBlockHeader (prevBlock (nodeHeader walk))-                        go walk'-        | otherwise = return $ Just walk---- | Is the provided 'BlockNode' the Genesis block?-isGenesis :: BlockNode -> Bool-isGenesis GenesisNode{} = True-isGenesis BlockNode{}   = False---- | Build the genesis 'BlockNode' for the supplied 'Network'.-genesisNode :: Network -> BlockNode-genesisNode net =-    GenesisNode-        { nodeHeader = getGenesisHeader net-        , nodeHeight = 0-        , nodeWork = headerWork (getGenesisHeader net)-        }---- | Validate a list of continuous block headers and import them to the--- block chain. Return 'Left' on failure with error information.-connectBlocks :: BlockHeaders m-              => Network-              -> Timestamp       -- ^ current time-              -> [BlockHeader]-              -> m (Either String [BlockNode])-connectBlocks _ _ [] = return $ Right []-connectBlocks net t bhs@(bh:_) =-    runExceptT $ do-        unless (chained bhs) $-            throwError "Blocks to connect do not form a chain"-        par <--            maybeToExceptT-                "Could not get parent block"-                (MaybeT (parentBlock bh))-        pars <- lift $ getParents 10 par-        bb <- lift getBestBlockHeader-        go par [] bb par pars bhs >>= \case-            bns@(bn:_) -> do-                lift $ addBlockHeaders bns-                let bb' = chooseBest bn bb-                when (bb' /= bb) $ lift $ setBestBlockHeader bb'-                return bns-            _ -> undefined-  where-    chained (h1:h2:hs) = headerHash h1 == prevBlock h2 && chained (h2 : hs)-    chained _          = True-    skipit lbh ls par-        | sh == nodeHeight lbh = return lbh-        | sh < nodeHeight lbh = do-            skM <- lift $ getAncestor sh lbh-            case skM of-                Just sk -> return sk-                Nothing ->-                    throwError $-                    "BUG: Could not get skip for block " ++-                    show (headerHash $ nodeHeader par)-        | otherwise = do-            let sn = ls !! fromIntegral (nodeHeight par - sh)-            when (nodeHeight sn /= sh) $-                throwError "BUG: Node height not right in skip"-            return sn-      where-        sh = skipHeight (nodeHeight par + 1)-    go _ acc _ _ _ [] = return acc-    go lbh acc bb par pars (h:hs) = do-        sk <- skipit lbh acc par-        bn <- ExceptT . return $ validBlock net t bb par pars h sk-        go lbh (bn : acc) (chooseBest bn bb) bn (take 10 $ par : pars) hs---- | Block's parent. If the block header is in the store, its parent must also--- be there. No block header get deleted or pruned from the store.-parentBlock :: BlockHeaders m-            => BlockHeader-            -> m (Maybe BlockNode)-parentBlock bh = getBlockHeader (prevBlock bh)---- | Validate and connect single block header to the block chain. Return 'Left' if fails--- to be validated.-connectBlock ::-       BlockHeaders m-    => Network-    -> Timestamp -- ^ current time-    -> BlockHeader-    -> m (Either String BlockNode)-connectBlock net t bh =-    runExceptT $ do-        par <--            maybeToExceptT-                "Could not get parent block"-                (MaybeT (parentBlock bh))-        pars <- lift $ getParents 10 par-        skM <- lift $ getAncestor (skipHeight (nodeHeight par + 1)) par-        sk <--            case skM of-                Just sk -> return sk-                Nothing ->-                    throwError $-                    "BUG: Could not get skip for block " ++-                    show (headerHash $ nodeHeader par)-        bb <- lift getBestBlockHeader-        bn <- ExceptT . return $ validBlock net t bb par pars bh sk-        let bb' = chooseBest bb bn-        lift $ addBlockHeader bn-        when (bb /= bb') . lift $ setBestBlockHeader bb'-        return bn---- | Validate this block header. Build a 'BlockNode' if successful.-validBlock :: Network-           -> Timestamp     -- ^ current time-           -> BlockNode     -- ^ best block-           -> BlockNode     -- ^ immediate parent-           -> [BlockNode]   -- ^ 10 parents above-           -> BlockHeader   -- ^ header to validate-           -> BlockNode     -- ^ skip node (black magic)-           -> Either String BlockNode-validBlock net t bb par pars bh sk = do-    let mt = medianTime . map (blockTimestamp . nodeHeader) $ par : pars-        nt = blockTimestamp bh-        hh = headerHash bh-        nv = blockVersion bh-        ng = nodeHeight par + 1-        aw = nodeWork par + headerWork bh-    unless (isValidPOW net bh) $-        Left $ "Proof of work failed: " ++ show (headerHash bh)-    unless (nt <= t + 2 * 60 * 60) $-        Left $ "Invalid header timestamp: " ++ show nt-    unless (nt >= mt) $-        Left $ "Block timestamp too early: " ++ show nt-    unless (afterLastCP net (nodeHeight bb) ng) $-        Left $ "Rewriting pre-checkpoint chain: " ++ show ng-    unless (validCP net ng hh) $-        Left $ "Rejected checkpoint: " ++ show ng-    unless (bip34 net ng hh) $-        Left $ "Rejected BIP-34 block: " ++ show hh-    unless (validVersion net ng nv) $-        Left $ "Invalid block version: " ++ show nv-    return BlockNode { nodeHeader = bh-                     , nodeHeight = ng-                     , nodeWork = aw-                     , nodeSkip = headerHash $ nodeHeader sk-                     }---- | Return the median of all provided timestamps. Can be unsorted. Error on--- empty list.-medianTime :: [Timestamp] -> Timestamp-medianTime ts-    | null ts = error "Cannot compute median time of empty header list"-    | otherwise = sort ts !! (length ts `div` 2)---- | Calculate the height of the skip (magic) block that corresponds to the--- given height. The block hash of the ancestor at that height will be placed on--- the 'BlockNode' structure to help locate ancestors at any height quickly.-skipHeight :: BlockHeight -> BlockHeight-skipHeight height-    | height < 2 = 0-    | height .&. 1 /= 0 = invertLowestOne (invertLowestOne $ height - 1) + 1-    | otherwise = invertLowestOne height---- | Part of the skip black magic calculation.-invertLowestOne :: BlockHeight -> BlockHeight-invertLowestOne height = height .&. (height - 1)---- | Get a number of parents for the provided block.-getParents :: BlockHeaders m-           => Int-           -> BlockNode-           -> m [BlockNode]   -- ^ starts from immediate parent-getParents = getpars []-  where-    getpars acc 0 _ = return $ reverse acc-    getpars acc _ GenesisNode{} = return $ reverse acc-    getpars acc n BlockNode{..} = do-        parM <- getBlockHeader $ prevBlock nodeHeader-        case parM of-            Just bn -> getpars (bn : acc) (n - 1) bn-            Nothing -> error "BUG: All non-genesis blocks should have a parent"---- | Verify that checkpoint location is valid.-validCP :: Network-        -> BlockHeight  -- ^ new child height-        -> BlockHash    -- ^ new child hash-        -> Bool-validCP net height newChildHash =-    case lookup height (getCheckpoints net) of-        Just cpHash -> cpHash == newChildHash-        Nothing     -> True---- | New block height above the last checkpoint imported. Used to prevent a--- reorg below the highest checkpoint that was already imported.-afterLastCP :: Network-            -> BlockHeight  -- ^ best height-            -> BlockHeight  -- ^ new imported block height-            -> Bool-afterLastCP net bestHeight newChildHeight =-    case lM of-        Just l  -> l < newChildHeight-        Nothing -> True-  where-    lM =-        listToMaybe . reverse $-        [c | (c, _) <- getCheckpoints net, c <= bestHeight]---- | This block should be at least version 2 (BIP34). Block height must be--- included in the coinbase transaction to prevent non-unique transaction--- hashes.-bip34 :: Network-      -> BlockHeight  -- ^ new child height-      -> BlockHash    -- ^ new child hash-      -> Bool-bip34 net height hsh-    | fst (getBip34Block net) == 0 = True-    | fst (getBip34Block net) == height = snd (getBip34Block net) == hsh-    | otherwise = True---- | Check if the provided block height and version are valid.-validVersion :: Network-             -> BlockHeight  -- ^ new child height-             -> Word32       -- ^ new child version-             -> Bool-validVersion net height version-    | version < 2 = height < fst (getBip34Block net)-    | version < 3 = height < getBip66Height net-    | version < 4 = height < getBip65Height net-    | otherwise = True---- | Find last block with normal, as opposed to minimum difficulty (for test--- networks).-lastNoMinDiff :: BlockHeaders m => Network -> BlockNode -> m BlockNode-lastNoMinDiff net bn@BlockNode {..} = do-    let i = nodeHeight `mod` diffInterval net /= 0-        c = encodeCompact (getPowLimit net)-        l = blockBits nodeHeader == c-        e1 =-            error $-            "Could not get block header for parent of " ++-            show (headerHash nodeHeader)-    if i && l-        then do-            bn' <- fromMaybe e1 <$> getBlockHeader (prevBlock nodeHeader)-            lastNoMinDiff net bn'-        else return bn--lastNoMinDiff _ bn@GenesisNode{} = return bn---- | Returns the work required on a block header given the previous block. This--- coresponds to @bitcoind@ function @GetNextWorkRequired@ in @main.cpp@.-nextWorkRequired :: BlockHeaders m-                 => Network-                 -> BlockNode-                 -> BlockHeader-                 -> m Word32-nextWorkRequired net par bh = do-    let mf = daa <|> eda <|> pow-    case mf of-        Just f -> f net par bh-        Nothing ->-            error-                "Could not get an appropriate difficulty calculation algorithm"-  where-    daa = getDaaBlockHeight net >>= \daaHeight -> do-        guard (nodeHeight par + 1 >= daaHeight)-        return nextDaaWorkRequired-    eda = getEdaBlockHeight net >>= \edaHeight -> do-        guard (nodeHeight par + 1 >= edaHeight)-        return nextEdaWorkRequired-    pow = return nextPowWorkRequired---- | Find out the next amount of work required according to the Emergency--- Difficulty Adjustment (EDA) algorithm from Bitcoin Cash.-nextEdaWorkRequired ::-       BlockHeaders m => Network -> BlockNode -> BlockHeader -> m Word32-nextEdaWorkRequired net par bh-    | nodeHeight par + 1 `mod` diffInterval net == 0 =-        nextWorkRequired net par bh-    | minDifficulty = return (encodeCompact (getPowLimit net))-    | blockBits (nodeHeader par) == encodeCompact (getPowLimit net) =-        return (encodeCompact (getPowLimit net))-    | otherwise = do-        par6 <- fromMaybe e1 <$> getAncestor (nodeHeight par - 6) par-        pars <- getParents 10 par-        pars6 <- getParents 10 par6-        let par6med =-                medianTime $ map (blockTimestamp . nodeHeader) (par6 : pars6)-            parmed = medianTime $ map (blockTimestamp . nodeHeader) (par : pars)-            mtp6 = parmed - par6med-        if mtp6 < 12 * 3600-            then return $ blockBits (nodeHeader par)-            else return $-                 let (diff, _) = decodeCompact (blockBits (nodeHeader par))-                     ndiff = diff + (diff `shiftR` 2)-                  in if getPowLimit net > ndiff-                         then encodeCompact (getPowLimit net)-                         else encodeCompact ndiff-  where-    minDifficulty =-        blockTimestamp bh >-        blockTimestamp (nodeHeader par) + getTargetSpacing net * 2-    e1 = error "Could not get seventh ancestor of block"---- | Find the next amount of work required according to the Difficulty--- Adjustment Algorithm (DAA) from Bitcoin Cash.-nextDaaWorkRequired ::-       BlockHeaders m => Network -> BlockNode -> BlockHeader -> m Word32-nextDaaWorkRequired net par bh-    | minDifficulty = return (encodeCompact (getPowLimit net))-    | otherwise = do-        let height = nodeHeight par-        unless (height >= diffInterval net) $-            error "Block height below difficulty interval"-        l <- getSuitableBlock par-        par144 <- fromMaybe e1 <$> getAncestor (height - 144) par-        f <- getSuitableBlock par144-        let nextTarget = computeTarget net f l-        if nextTarget > getPowLimit net-            then return $ encodeCompact (getPowLimit net)-            else return $ encodeCompact nextTarget-  where-    e1 = error "Cannot get ancestor at parent - 144 height"-    minDifficulty =-        blockTimestamp bh >-        blockTimestamp (nodeHeader par) + getTargetSpacing net * 2---- | Compute Bitcoin Cash DAA target for a new block.-computeTarget :: Network -> BlockNode -> BlockNode -> Integer-computeTarget net f l =-    let work = (nodeWork l - nodeWork f) * fromIntegral (getTargetSpacing net)-        actualTimespan =-            blockTimestamp (nodeHeader l) - blockTimestamp (nodeHeader f)-        actualTimespan'-            | actualTimespan > 288 * getTargetSpacing net =-                288 * getTargetSpacing net-            | actualTimespan < 72 * getTargetSpacing net =-                72 * getTargetSpacing net-            | otherwise = actualTimespan-        work' = work `div` fromIntegral actualTimespan'-     in 2 ^ (256 :: Integer) `div` work'---- | Get suitable block for Bitcoin Cash DAA computation.-getSuitableBlock :: BlockHeaders m => BlockNode -> m BlockNode-getSuitableBlock par = do-    unless (nodeHeight par >= 3) $ error "Block height is less than three"-    blocks <- (par :) <$> getParents 2 par-    return $ sortBy (compare `on` blockTimestamp . nodeHeader) blocks !! 1---- | Returns the work required on a block header given the previous block. This--- coresponds to bitcoind function GetNextWorkRequired in main.cpp.-nextPowWorkRequired ::-       BlockHeaders m => Network -> BlockNode -> BlockHeader -> m Word32-nextPowWorkRequired net par bh-    | nodeHeight par + 1 `mod` diffInterval net /= 0 =-        if getAllowMinDifficultyBlocks net-            then if ht > pt + delta-                     then return $ encodeCompact (getPowLimit net)-                     else do-                         d <- lastNoMinDiff net par-                         return $ blockBits $ nodeHeader d-            else return $ blockBits $ nodeHeader par-    | otherwise = do-        let rh = nodeHeight par - (diffInterval net - 1)-        a <- fromMaybe e1 <$> getAncestor rh par-        let t = blockTimestamp $ nodeHeader a-        return $ calcNextWork net (nodeHeader par) t-  where-    e1 = error "Could not get ancestor for block header"-    pt = blockTimestamp $ nodeHeader par-    ht = blockTimestamp bh-    delta = getTargetSpacing net * 2---- | Computes the work required for the first block in a new retarget period.-calcNextWork :: Network-             -> BlockHeader  -- ^ last block in previous retarget (parent)-             -> Timestamp    -- ^ timestamp of first block in previous retarget-             -> Word32-calcNextWork net header time-    | getPowNoRetargetting net = blockBits header-    | new > getPowLimit net = encodeCompact (getPowLimit net)-    | otherwise = encodeCompact new-  where-    s = blockTimestamp header - time-    n | s < getTargetTimespan net `div` 4 = getTargetTimespan net `div` 4-      | s > getTargetTimespan net * 4 = getTargetTimespan net * 4-      | otherwise = s-    l = fst $ decodeCompact $ blockBits header-    new = l * fromIntegral n `div` fromIntegral (getTargetTimespan net)---- | Returns True if the difficulty target (bits) of the header is valid and the--- proof of work of the header matches the advertised difficulty target. This--- function corresponds to the function @CheckProofOfWork@ from @bitcoind@ in--- @main.cpp@.-isValidPOW :: Network -> BlockHeader -> Bool-isValidPOW net h-    | target <= 0 || over || target > getPowLimit net = False-    | otherwise = blockPOW (headerHash h) <= fromIntegral target-  where-    (target, over) = decodeCompact $ blockBits h---- | Returns the proof of work of a block header hash as an 'Integer' number.-blockPOW :: BlockHash -> Integer-blockPOW =  bsToInteger . B.reverse . encode---- | Returns the work represented by this block. Work is defined as the number--- of tries needed to solve a block in the average case with respect to the--- target.-headerWork :: BlockHeader -> Integer-headerWork bh = largestHash `div` (target + 1)-  where-    target      = fst $ decodeCompact $ blockBits bh-    largestHash = 1 `shiftL` 256---- | Number of blocks on average between difficulty cycles (2016 blocks).-diffInterval :: Network -> Word32-diffInterval net = getTargetTimespan net `div` getTargetSpacing net---- | Compare two blocks to get the best.-chooseBest :: BlockNode -> BlockNode -> BlockNode-chooseBest b1 b2 | nodeWork b1 == nodeWork b2 =-                       if nodeHeight b1 >= nodeHeight b2-                       then b1-                       else b2-                 | nodeWork b1 > nodeWork b2 = b1-                 | otherwise = b2---- | Get list of blocks for a block locator.-blockLocatorNodes :: BlockHeaders m => BlockNode -> m [BlockNode]-blockLocatorNodes best =-    reverse <$> go [] best 1-  where-    e1 = error "Could not get ancestor"-    go loc bn n =-        let loc' = bn : loc-            n' = if length loc' > 10-                 then n * 2-                 else 1-        in if nodeHeight bn < n'-           then do a <- fromMaybe e1 <$> getAncestor 0 bn-                   return $ a : loc'-           else do let h = nodeHeight bn - n'-                   bn' <- fromMaybe e1 <$> getAncestor h bn-                   go loc' bn' n'---- | Get block locator.-blockLocator :: BlockHeaders m => BlockNode -> m BlockLocator-blockLocator bn = map (headerHash . nodeHeader) <$> blockLocatorNodes bn---- | Become rich beyond your wildest dreams.-mineBlock :: Network -> Word32 -> BlockHeader -> BlockHeader-mineBlock net seed h =-    head-        [ j-        | i <- (+ seed) <$> [0 .. maxBound]-        , let j = h {bhNonce = i}-        , isValidPOW net j-        ]---- | Generate and append new blocks (mining). Only practical in regtest network.-appendBlocks ::-       Network-    -> Word32 -- ^ random seed-    -> BlockHeader-    -> Int-    -> [BlockHeader]-appendBlocks _ _ _ 0 = []-appendBlocks net seed bh i =-    bh' : appendBlocks net seed bh' (i - 1)-  where-    bh' = mineBlock net seed bh-        { prevBlock = headerHash bh-          -- Just to make it different in every header-        , merkleRoot = sha256 $ encode seed-        }---- | Find the last common block ancestor between provided block headers.-splitPoint :: BlockHeaders m => BlockNode -> BlockNode -> m BlockNode-splitPoint l r = do-    let h = min (nodeHeight l) (nodeHeight r)-    ll <- fromMaybe e <$> getAncestor h l-    lr <- fromMaybe e <$> getAncestor h r-    f ll lr-  where-    e = error "BUG: Could not get ancestor at lowest height"-    f ll lr =-        if ll == lr-            then return lr-            else do-                let h = nodeHeight ll - 1-                pl <- fromMaybe e <$> getAncestor h ll-                pr <- fromMaybe e <$> getAncestor h lr-                f pl pr---- | Generate the entire Genesis block for 'Network'.-genesisBlock :: Network -> Block-genesisBlock net = Block (getGenesisHeader net) [genesisTx]---- | Compute block subsidy at particular height.-computeSubsidy :: Network -> BlockHeight -> Word64-computeSubsidy net height =-    let halvings = height `div` getHalvingInterval net-        ini = 50 * 100 * 1000 * 1000-     in if halvings >= 64-            then 0-            else ini `shiftR` fromIntegral halvings
− src/Network/Haskoin/Block/Merkle.hs
@@ -1,256 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric  #-}-{-|-Module      : Network.Haskoin.Block.Merkle-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Function to deal with Merkle trees inside blocks.--}-module Network.Haskoin.Block.Merkle-    ( MerkleBlock(..)-    , MerkleRoot-    , FlagBits-    , PartialMerkleTree-    , buildMerkleRoot-    , merkleBlockTxs-    , testMerkleRoot-    -- * Helper functions-    , buildPartialMerkle-    , decodeMerkleFlags-    , encodeMerkleFlags-    , calcTreeHeight-    , calcTreeWidth-    , hash2-    , calcHash-    , traverseAndBuild-    , traverseAndExtract-    , extractMatches-    , splitIn-    , boolsToWord8-    ) where--import           Control.DeepSeq-import           Control.Monad                      (forM_, replicateM, when)-import           Data.Bits-import qualified Data.ByteString                    as BS-import           Data.Either                        (isRight)-import           Data.Hashable-import           Data.Maybe-import           Data.Serialize                     (Serialize, encode, get,-                                                     put)-import           Data.Serialize.Get                 (getWord32le, getWord8)-import           Data.Serialize.Put                 (putWord32le, putWord8)-import           Data.Word                          (Word32, Word8)-import           GHC.Generics-import           Network.Haskoin.Block.Common-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Network.Common-import           Network.Haskoin.Transaction.Common---- | Hash of the block's Merkle root.-type MerkleRoot        = Hash256---- | Bits that are used to rebuild partial merkle tree transaction hash list.-type FlagBits          = [Bool]---- | Partial Merkle tree for a filtered block.-type PartialMerkleTree = [Hash256]---- | Filtered block: a block with a partial Merkle tree that only includes the--- transactions that pass a bloom filter that was negotiated.-data MerkleBlock =-    MerkleBlock {-                -- | block header-                  merkleHeader    :: !BlockHeader-                -- | total number of transactions in block-                , merkleTotalTxns :: !Word32-                -- | hashes in depth-first order-                , mHashes         :: !PartialMerkleTree-                -- | bits to rebuild partial merkle tree-                , mFlags          :: !FlagBits-                } deriving (Eq, Show, Read, Generic, Hashable, NFData)--instance Serialize MerkleBlock where--    get = do-        header <- get-        ntx    <- getWord32le-        (VarInt matchLen) <- get-        hashes <- replicateM (fromIntegral matchLen) get-        (VarInt flagLen)  <- get-        ws <- replicateM (fromIntegral flagLen) getWord8-        return $ MerkleBlock header ntx hashes (decodeMerkleFlags ws)--    put (MerkleBlock h ntx hashes flags) = do-        put h-        putWord32le ntx-        putVarInt $ length hashes-        forM_ hashes put-        let ws = encodeMerkleFlags flags-        putVarInt $ length ws-        forM_ ws putWord8---- | Unpack Merkle flags into 'FlagBits' structure.-decodeMerkleFlags :: [Word8] -> FlagBits-decodeMerkleFlags ws =-    [ b | p <- [ 0 .. length ws * 8 - 1 ]-        , b <- [ testBit (ws !! (p `div` 8)) (p `mod` 8) ]-    ]---- | Pack Merkle flags from 'FlagBits'.-encodeMerkleFlags :: FlagBits -> [Word8]-encodeMerkleFlags bs = map boolsToWord8 $ splitIn 8 bs---- | Computes the height of a Merkle tree.-calcTreeHeight :: Int -- ^ number of transactions (leaf nodes)-               -> Int -- ^ height of the merkle tree-calcTreeHeight ntx | ntx < 2 = 0-                   | even ntx  = 1 + calcTreeHeight (ntx `div` 2)-                   | otherwise = calcTreeHeight $ ntx + 1---- | Computes the width of a Merkle tree at a specific height. The transactions--- are at height 0.-calcTreeWidth :: Int -- ^ number of transactions (leaf nodes)-              -> Int -- ^ height at which we want to compute the width-              -> Int -- ^ width of the Merkle tree-calcTreeWidth ntx h = (ntx + (1 `shiftL` h) - 1) `shiftR` h---- | Computes the root of a Merkle tree from a list of leaf node hashes.-buildMerkleRoot :: [TxHash]   -- ^ transaction hashes (leaf nodes)-                -> MerkleRoot -- ^ root of the Merkle tree-buildMerkleRoot txs = calcHash (calcTreeHeight $ length txs) 0 txs---- | Concatenate and compute double SHA256.-hash2 :: Hash256 -> Hash256 -> Hash256-hash2 a b = doubleSHA256 $ encode a `BS.append` encode b---- | Computes the hash of a specific node in a Merkle tree.-calcHash :: Int       -- ^ height of the node-         -> Int       -- ^ position of the node (0 for the leftmost node)-         -> [TxHash]  -- ^ transaction hashes (leaf nodes)-         -> Hash256   -- ^ hash of the node at the specified position-calcHash height pos txs-    | height < 0 || pos < 0 = error "calcHash: Invalid parameters"-    | height == 0 = getTxHash $ txs !! pos-    | otherwise = hash2 left right-  where-    left = calcHash (height-1) (pos*2) txs-    right | pos*2+1 < calcTreeWidth (length txs) (height-1) =-                calcHash (height-1) (pos*2+1) txs-          | otherwise = left---- | Build a partial Merkle tree. Provide a list of tuples with all transaction--- hashes in the block, and whether the transaction is to be included in the--- partial tree. Returns a flag bits structure and the computed partial Merkle--- tree.-buildPartialMerkle ::-       [(TxHash, Bool)] -- ^ transaction hash and whether to include-    -> (FlagBits, PartialMerkleTree) -- ^ flag bits and partial Merkle tree-buildPartialMerkle hs = traverseAndBuild (calcTreeHeight $ length hs) 0 hs---- | Helper function to build partial Merkle tree. Used by 'buildPartialMerkle'--- above.-traverseAndBuild ::-       Int -> Int -> [(TxHash, Bool)] -> (FlagBits, PartialMerkleTree)-traverseAndBuild height pos txs-    | height < 0 || pos < 0 = error "traverseAndBuild: Invalid parameters"-    | height == 0 || not match = ([match], [calcHash height pos t])-    | otherwise = (match : lb ++ rb, lh ++ rh)-  where-    t = map fst txs-    s = pos `shiftL` height-    e = min (length txs) $ (pos + 1) `shiftL` height-    match = any snd $ take (e - s) $ drop s txs-    (lb, lh) = traverseAndBuild (height - 1) (pos * 2) txs-    (rb, rh)-        | (pos * 2 + 1) < calcTreeWidth (length txs) (height - 1) =-            traverseAndBuild (height - 1) (pos * 2 + 1) txs-        | otherwise = ([], [])---- | Helper function to extract transaction hashes from partial Merkle tree.-traverseAndExtract :: Int -> Int -> Int -> FlagBits -> PartialMerkleTree-                   -> Maybe (MerkleRoot, [TxHash], Int, Int)-traverseAndExtract height pos ntx flags hashes-    | null flags               = Nothing-    | height == 0 || not match = leafResult-    | isNothing leftM          = Nothing-    | (pos*2+1) >= calcTreeWidth ntx (height-1) =-        Just (hash2 lh lh, lm, lcf+1, lch)-    | isNothing rightM         = Nothing-    | otherwise =-        Just (hash2 lh rh, lm ++ rm, lcf+rcf+1, lch+rch)-  where-    leafResult-        | null hashes = Nothing-        | otherwise = Just (h, [ TxHash h | height == 0 && match ], 1, 1)-    (match:fs) = flags-    (h:_)     = hashes-    leftM  = traverseAndExtract (height-1) (pos*2) ntx fs hashes-    (lh,lm,lcf,lch) = fromMaybe e leftM-    rightM = traverseAndExtract (height-1) (pos*2+1) ntx-                (drop lcf fs) (drop lch hashes)-    (rh,rm,rcf,rch) = fromMaybe e rightM-    e = error "traverseAndExtract: unexpected error extracting a Maybe value"---- | Extracts the matching hashes from a partial merkle tree. This will return--- the list of transaction hashes that have been included (set to true) in--- a call to 'buildPartialMerkle'.-extractMatches :: Network-               -> FlagBits-               -> PartialMerkleTree-               -> Int -- ^ number of transaction at height 0 (leaf nodes)-               -> Either String (MerkleRoot, [TxHash])-               -- ^ Merkle root and list of matching transaction hashes-extractMatches net flags hashes ntx-    | ntx == 0 = Left-        "extractMatches: number of transactions can not be 0"-    | ntx > getMaxBlockSize net `div` 60 = Left-        "extractMatches: number of transactions excessively high"-    | length hashes > ntx = Left-        "extractMatches: More hashes provided than the number of transactions"-    | length flags < length hashes = Left-        "extractMatches: At least one bit per node and one bit per hash"-    | isNothing resM = Left-        "extractMatches: traverseAndExtract failed"-    | (nBitsUsed+7) `div` 8 /= (length flags+7) `div` 8 = Left-        "extractMatches: All bits were not consumed"-    | nHashUsed /= length hashes = Left $-        "extractMatches: All hashes were not consumed: " ++ show nHashUsed-    | otherwise = return (merkRoot, matches)-  where-    resM = traverseAndExtract (calcTreeHeight ntx) 0 ntx flags hashes-    (merkRoot, matches, nBitsUsed, nHashUsed) = fromMaybe e resM-    e = error "extractMatches: unexpected error extracting a Maybe value"---- | Helper function to split a list in chunks 'Int' length. Last chunk may be--- smaller.-splitIn :: Int -> [a] -> [[a]]-splitIn _ [] = []-splitIn c xs = xs1 : splitIn c xs2-  where-    (xs1, xs2) = splitAt c xs---- | Pack up to eight bools in a byte.-boolsToWord8 :: [Bool] -> Word8-boolsToWord8 [] = 0-boolsToWord8 xs = foldl setBit 0 (map snd $ filter fst $ zip xs [0..7])---- | Get matching transactions from Merkle block.-merkleBlockTxs :: Network -> MerkleBlock -> Either String [TxHash]-merkleBlockTxs net b =-    let flags = mFlags b-        hs = mHashes b-        n = fromIntegral $ merkleTotalTxns b-        merkle = merkleRoot $ merkleHeader b-    in do (root, ths) <- extractMatches net flags hs n-          when (root /= merkle) $ Left "merkleBlockTxs: Merkle root incorrect"-          return ths---- | Check if Merkle block root is valid against the block header.-testMerkleRoot :: Network -> MerkleBlock -> Bool-testMerkleRoot net = isRight . merkleBlockTxs net
− src/Network/Haskoin/Constants.hs
@@ -1,540 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Constants-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Network constants for various networks, including Bitcoin SegWit (BTC), Bitcoin-Cash (BCH), and corresponding public test and private regression test networks.--}-module Network.Haskoin.Constants-    ( Network(..)-    , btc-    , btcTest-    , btcRegTest-    , bch-    , bchTest-    , bchRegTest-    , allNets-    , netByName-    , netByIdent-    ) where--import           Control.DeepSeq-import           Data.ByteString              (ByteString)-import           Data.List-import           Data.Maybe-import           Data.Serialize-import           Data.String-import           Data.Text                    (Text)-import           Data.Version-import           Data.Word                    (Word32, Word64, Word8)-import           GHC.Generics                 (Generic)-import           Network.Haskoin.Block.Common-import           Paths_haskoin_core-import           Text.Read---- | Version of Haskoin Core package.-versionString :: IsString a => a-versionString = fromString (showVersion version)---- | Constants for network.-data Network = Network-    { -- | lowercase alphanumeric and dashes-      getNetworkName              :: !String-      -- | network Haskell identifier-    , getNetworkIdent             :: !String-      -- | prefix for 'Base58' P2PKH addresses-    , getAddrPrefix               :: !Word8-      -- | prefix for 'Base58' P2SH addresses-    , getScriptPrefix             :: !Word8-      -- | prefix for WIF private key-    , getSecretPrefix             :: !Word8-      -- | prefix for extended public key-    , getExtPubKeyPrefix          :: !Word32-      -- | prefix for extended private key-    , getExtSecretPrefix          :: !Word32-      -- | network magic-    , getNetworkMagic             :: !Word32-      -- | genesis block header-    , getGenesisHeader            :: !BlockHeader-      -- | maximum block size in bytes-    , getMaxBlockSize             :: !Int-      -- | maximum amount of satoshi-    , getMaxSatoshi               :: !Word64-      -- | user agent string-    , getHaskoinUserAgent         :: !ByteString-      -- | default port for P2P connections-    , getDefaultPort              :: !Int-      -- | allow min difficulty blocks (testnet)-    , getAllowMinDifficultyBlocks :: !Bool-      -- | do not retarget difficulty (regtest)-    , getPowNoRetargetting        :: !Bool-      -- | proof-of-work target higest possible value-    , getPowLimit                 :: !Integer-      -- | block at which BIP34 activates-    , getBip34Block               :: !(BlockHeight, BlockHash)-      -- | block at which BIP65 activates-    , getBip65Height              :: !BlockHeight-      -- | block at which BIP66 activates-    , getBip66Height              :: !BlockHeight-      -- | time between difficulty retargets-    , getTargetTimespan           :: !Word32-      -- | time between blocks-    , getTargetSpacing            :: !Word32-      -- | checkpoints-    , getCheckpoints              :: ![(BlockHeight, BlockHash)]-      -- | BIP44 derivation path root-    , getBip44Coin                :: !Word32-      -- | peer-to-peer network seeds-    , getSeeds                    :: ![String]-      -- | fork id for replay protection-    , getSigHashForkId            :: !(Maybe Word32)-      -- | EDA start block height-    , getEdaBlockHeight           :: !(Maybe Word32)-      -- | DAA start block height-    , getDaaBlockHeight           :: !(Maybe Word32)-      -- | segregated witness active-    , getSegWit                   :: !Bool-      -- | 'CashAddr' prefix (for Bitcoin Cash)-    , getCashAddrPrefix           :: !(Maybe Text)-      -- | 'Bech32' prefix (for SegWit network)-    , getBech32Prefix             :: !(Maybe Text)-      -- | Replace-By-Fee (BIP-125)-    , getReplaceByFee             :: !Bool-      -- | Subsidy halving interval-    , getHalvingInterval          :: !Word32-    } deriving (Eq, Generic, NFData)--instance Serialize Network where-    put net =-        putWord32be $ getNetworkMagic net-    get = do-        magic <- getWord32be-        case find ((== magic) . getNetworkMagic) allNets of-            Nothing  -> fail $ "Network magic unknown: " <> show magic-            Just net -> return net--instance Show Network where-    show = getNetworkIdent--instance Read Network where-    readPrec = do-        Ident str <- lexP-        maybe pfail return (netByIdent str)--instance IsString Network where-    fromString = fromMaybe (error "Network name invalid") . netByName---- | Query known networks by name.-netByName :: String -> Maybe Network-netByName str = find ((== str) . getNetworkName) allNets---- | Query known networks by Haskell identifier.-netByIdent :: String -> Maybe Network-netByIdent str = find ((== str) . getNetworkIdent) allNets---- | Bitcoin SegWit network. Symbol: BTC.-btc :: Network-btc =-    Network-    { getNetworkName = "btc"-    , getNetworkIdent = "btc"-    , getAddrPrefix = 0-    , getScriptPrefix = 5-    , getSecretPrefix = 128-    , getExtPubKeyPrefix = 0x0488b21e-    , getExtSecretPrefix = 0x0488ade4-    , getNetworkMagic = 0xf9beb4d9-    , getGenesisHeader =-          BlockHeader-              0x01-              "0000000000000000000000000000000000000000000000000000000000000000"-              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"-              1231006505-              0x1d00ffff-              2083236893-            -- Hash 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f-    , getMaxBlockSize = 1000000-    , getMaxSatoshi = 2100000000000000-    , getHaskoinUserAgent =-          "/haskoin-btc:" <> versionString <> "/"-    , getDefaultPort = 8333-    , getAllowMinDifficultyBlocks = False-    , getPowNoRetargetting = False-    , getPowLimit =-          0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff-    , getBip34Block =-          ( 227931-          , "000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8")-    , getBip65Height = 388381-    , getBip66Height = 363725-    , getTargetTimespan = 14 * 24 * 60 * 60-    , getTargetSpacing = 10 * 60-    , getCheckpoints =-          [ ( 11111-            , "0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")-          , ( 33333-            , "000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")-          , ( 74000-            , "0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")-          , ( 105000-            , "00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")-          , ( 134444-            , "00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")-          , ( 168000-            , "000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")-          , ( 193000-            , "000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")-          , ( 210000-            , "000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")-          , ( 216116-            , "00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")-          , ( 225430-            , "00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")-          , ( 250000-            , "000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")-          , ( 279000-            , "0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")-          , ( 295000-            , "00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")-          ]-    , getSeeds =-          [ "seed.bitcoin.sipa.be" -- Pieter Wuille-          , "dnsseed.bluematt.me" -- Matt Corallo-          , "dnsseed.bitcoin.dashjr.org" -- Luke Dashjr-          , "seed.bitcoinstats.com" -- Chris Decker-          , "seed.bitcoin.jonasschnelli.ch" -- Jonas Schnelli-          , "seed.btc.petertodd.org" -- Peter Todd-          , "seed.bitcoin.sprovoost.nl" -- Sjors Provoost-          ]-    , getBip44Coin = 0-    , getSigHashForkId = Nothing-    , getEdaBlockHeight = Nothing-    , getDaaBlockHeight = Nothing-    , getSegWit = True-    , getCashAddrPrefix = Nothing-    , getBech32Prefix = Just "bc"-    , getReplaceByFee = True-    , getHalvingInterval = 210000-    }---- | Testnet for Bitcoin SegWit network.-btcTest :: Network-btcTest =-    Network-    { getNetworkName = "btctest"-    , getNetworkIdent = "btcTest"-    , getAddrPrefix = 111-    , getScriptPrefix = 196-    , getSecretPrefix = 239-    , getExtPubKeyPrefix = 0x043587cf-    , getExtSecretPrefix = 0x04358394-    , getNetworkMagic = 0x0b110907-    , getGenesisHeader =-          BlockHeader-              0x01-              "0000000000000000000000000000000000000000000000000000000000000000"-              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"-              1296688602-              486604799-              414098458-            -- Hash 000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943-    , getMaxBlockSize = 1000000-    , getMaxSatoshi = 2100000000000000-    , getHaskoinUserAgent = "/haskoin-btc-test:" <> versionString <> "/"-    , getDefaultPort = 18333-    , getAllowMinDifficultyBlocks = True-    , getPowNoRetargetting = False-    , getPowLimit =-          0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff-    , getBip34Block =-          ( 21111-          , "0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8")-    , getBip65Height = 581885-    , getBip66Height = 330776-    , getTargetTimespan = 14 * 24 * 60 * 60-    , getTargetSpacing = 10 * 60-    , getCheckpoints =-          [ ( 546-            , "000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")-          ]-    , getSeeds =-          [ "testnet-seed.bitcoin.jonasschnelli.ch"-          , "seed.tbtc.petertodd.org"-          , "seed.testnet.bitcoin.sprovoost.nl"-          , "testnet-seed.bluematt.me"-          ]-    , getBip44Coin = 1-    , getSigHashForkId = Nothing-    , getEdaBlockHeight = Nothing-    , getDaaBlockHeight = Nothing-    , getSegWit = True-    , getCashAddrPrefix = Nothing-    , getBech32Prefix = Just "tb"-    , getReplaceByFee = True-    , getHalvingInterval = 210000-    }---- | RegTest for Bitcoin SegWit network.-btcRegTest :: Network-btcRegTest =-    Network-    { getNetworkName = "btcreg"-    , getNetworkIdent = "btcRegTest"-    , getAddrPrefix = 111-    , getScriptPrefix = 196-    , getSecretPrefix = 239-    , getExtPubKeyPrefix = 0x043587cf-    , getExtSecretPrefix = 0x04358394-    , getNetworkMagic = 0xfabfb5da-    , getGenesisHeader =-          BlockHeader-           -- 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206-              0x01-              "0000000000000000000000000000000000000000000000000000000000000000"-              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"-              1296688602-              0x207fffff-              2-    , getMaxBlockSize = 1000000-    , getMaxSatoshi = 2100000000000000-    , getHaskoinUserAgent = "/haskoin-btc-regtest:" <> versionString <> "/"-    , getDefaultPort = 18444-    , getAllowMinDifficultyBlocks = True-    , getPowNoRetargetting = True-    , getPowLimit =-          0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff-    , getBip34Block =-          ( 100000000-          , "0000000000000000000000000000000000000000000000000000000000000000")-    , getBip65Height = 1351-    , getBip66Height = 1251-    , getTargetTimespan = 14 * 24 * 60 * 60-    , getTargetSpacing = 10 * 60-    , getCheckpoints = []-    , getSeeds = ["localhost"]-    , getBip44Coin = 1-    , getSigHashForkId = Nothing-    , getEdaBlockHeight = Nothing-    , getDaaBlockHeight = Nothing-    , getSegWit = True-    , getCashAddrPrefix = Nothing-    , getBech32Prefix = Just "bcrt"-    , getReplaceByFee = True-    , getHalvingInterval = 150-    }---- | Bitcoin Cash network. Symbol: BCH.-bch :: Network-bch =-    Network-    { getNetworkName = "bch"-    , getNetworkIdent = "bch"-    , getAddrPrefix = 0-    , getScriptPrefix = 5-    , getSecretPrefix = 128-    , getExtPubKeyPrefix = 0x0488b21e-    , getExtSecretPrefix = 0x0488ade4-    , getNetworkMagic = 0xe3e1f3e8-    , getGenesisHeader =-          BlockHeader-              0x01-              "0000000000000000000000000000000000000000000000000000000000000000"-              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"-              1231006505-              0x1d00ffff-              2083236893-            -- Hash 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f-    , getMaxBlockSize = 32000000-    , getMaxSatoshi = 2100000000000000-    , getHaskoinUserAgent = "/haskoin-bch:" <> versionString <> "/"-    , getDefaultPort = 8333-    , getAllowMinDifficultyBlocks = False-    , getPowNoRetargetting = False-    , getPowLimit =-          0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff-    , getBip34Block =-          ( 227931-          , "000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8")-    , getBip65Height = 388381-    , getBip66Height = 363725-    , getTargetTimespan = 14 * 24 * 60 * 60-    , getTargetSpacing = 10 * 60-    , getCheckpoints =-          [ ( 11111-            , "0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")-          , ( 33333-            , "000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")-          , ( 74000-            , "0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")-          , ( 105000-            , "00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")-          , ( 134444-            , "00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")-          , ( 168000-            , "000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")-          , ( 193000-            , "000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")-          , ( 210000-            , "000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")-          , ( 216116-            , "00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")-          , ( 225430-            , "00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")-          , ( 250000-            , "000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")-          , ( 279000-            , "0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")-          , ( 295000-            , "00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")-            -- UAHF fork block.-          , ( 478559-            , "000000000000000000651ef99cb9fcbe0dadde1d424bd9f15ff20136191a5eec")-            -- Nov, 13 DAA activation block.-          , ( 504031-            , "0000000000000000011ebf65b60d0a3de80b8175be709d653b4c1a1beeb6ab9c")-          ]-    , getSeeds =-          [ "seed.bitcoinabc.org"-          , "seed-abc.bitcoinforks.org"-          , "btccash-seeder.bitcoinunlimited.info"-          , "seed.bitprim.org"-          , "seed.deadalnix.me"-          , "seeder.criptolayer.net"-          ]-    , getBip44Coin = 145-    , getSigHashForkId = Just 0-    , getEdaBlockHeight = Just 478559-    , getDaaBlockHeight = Just 404031-    , getSegWit = False-    , getCashAddrPrefix = Just "bitcoincash"-    , getBech32Prefix = Nothing-    , getReplaceByFee = False-    , getHalvingInterval = 210000-    }---- | Testnet for Bitcoin Cash network.-bchTest :: Network-bchTest =-    Network-    { getNetworkName = "bchtest"-    , getNetworkIdent = "bchTest"-    , getAddrPrefix = 111-    , getScriptPrefix = 196-    , getSecretPrefix = 239-    , getExtPubKeyPrefix = 0x043587cf-    , getExtSecretPrefix = 0x04358394-    , getNetworkMagic = 0xf4e5f3f4-    , getGenesisHeader =-          BlockHeader-              0x01-              "0000000000000000000000000000000000000000000000000000000000000000"-              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"-              1296688602-              486604799-              414098458-            -- Hash 000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943-    , getMaxBlockSize = 32000000-    , getMaxSatoshi = 2100000000000000-    , getHaskoinUserAgent = "/haskoin-bch-test:" <> versionString <> "/"-    , getDefaultPort = 18333-    , getAllowMinDifficultyBlocks = True-    , getPowNoRetargetting = False-    , getPowLimit =-          0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff-    , getBip34Block =-          ( 21111-          , "0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8")-    , getBip65Height = 581885-    , getBip66Height = 330776-    , getTargetTimespan = 14 * 24 * 60 * 60-    , getTargetSpacing = 10 * 60-    , getCheckpoints =-          [ ( 546-            , "000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")-            -- UAHF fork block.-          , ( 1155876-            , "00000000000e38fef93ed9582a7df43815d5c2ba9fd37ef70c9a0ea4a285b8f5")-            -- Nov, 13. DAA activation block.-          , ( 1188697-            , "0000000000170ed0918077bde7b4d36cc4c91be69fa09211f748240dabe047fb")-          ]-    , getSeeds =-          [ "testnet-seed.bitcoinabc.org"-          , "testnet-seed-abc.bitcoinforks.org"-          , "testnet-seed.bitprim.org"-          , "testnet-seed.deadalnix.me"-          , "testnet-seeder.criptolayer.net"-          ]-    , getBip44Coin = 1-    , getSigHashForkId = Just 0-    , getEdaBlockHeight = Just 1155876-    , getDaaBlockHeight = Just 1188697-    , getSegWit = False-    , getCashAddrPrefix = Just "bchtest"-    , getBech32Prefix = Nothing-    , getReplaceByFee = False-    , getHalvingInterval = 210000-    }---- | RegTest for Bitcoin Cash network.-bchRegTest :: Network-bchRegTest =-    Network-    { getNetworkName = "bchreg"-    , getNetworkIdent = "bchRegTest"-    , getAddrPrefix = 111-    , getScriptPrefix = 196-    , getSecretPrefix = 239-    , getExtPubKeyPrefix = 0x043587cf-    , getExtSecretPrefix = 0x04358394-    , getNetworkMagic = 0xdab5bffa-    , getGenesisHeader =-          BlockHeader-           -- 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206-              0x01-              "0000000000000000000000000000000000000000000000000000000000000000"-              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"-              1296688602-              0x207fffff-              2-    , getMaxBlockSize = 1000000-    , getMaxSatoshi = 2100000000000000-    , getHaskoinUserAgent = "/haskoin-bch-regtest:" <> versionString <> "/"-    , getDefaultPort = 18444-    , getAllowMinDifficultyBlocks = True-    , getPowNoRetargetting = True-    , getPowLimit =-          0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff-    , getBip34Block =-          ( 100000000-          , "0000000000000000000000000000000000000000000000000000000000000000")-    , getBip65Height = 1351-    , getBip66Height = 1251-    , getTargetTimespan = 14 * 24 * 60 * 60-    , getTargetSpacing = 10 * 60-    , getCheckpoints =-          [ ( 0-            , "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")-          ]-    , getSeeds = ["localhost"]-    , getBip44Coin = 1-    , getSigHashForkId = Just 0-    , getEdaBlockHeight = Nothing-    , getDaaBlockHeight = Just 0-    , getSegWit = False-    , getCashAddrPrefix = Just "bchreg"-    , getBech32Prefix = Nothing-    , getReplaceByFee = False-    , getHalvingInterval = 150-    }---- | List of all networks supported by this library.-allNets :: [Network]-allNets = [btc, bch, btcTest, bchTest, btcRegTest, bchRegTest]
− src/Network/Haskoin/Crypto.hs
@@ -1,22 +0,0 @@-{-|-Module      : Network.Haskoin.Crypto-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Hashing functions and ECDSA signatures.--}-module Network.Haskoin.Crypto-    ( -- * Hashes-      module Hash-      -- * Signatures-    , module Signature-      -- * Secp256k1 (re-exported)-    , module Secp256k1-    ) where--import           Crypto.Secp256k1                 as Secp256k1-import           Network.Haskoin.Crypto.Hash      as Hash-import           Network.Haskoin.Crypto.Signature as Signature
− src/Network/Haskoin/Crypto/Hash.hs
@@ -1,202 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DeriveAnyClass #-}-{-|-Module      : Network.Haskoin.Crypto.Hash-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Hashing functions and corresponding data types. Uses functions from the-cryptonite library.--}-module Network.Haskoin.Crypto.Hash-    ( -- * Hashes-      Hash512(getHash512)-    , Hash256(getHash256)-    , Hash160(getHash160)-    , CheckSum32(getCheckSum32)-    , sha512-    , sha256-    , ripemd160-    , sha1-    , doubleSHA256-    , addressHash-    , checkSum32-    , hmac512-    , hmac256-    , split512-    , join512-    ) where--import           Control.DeepSeq-import           Crypto.Hash             (RIPEMD160 (..), SHA1 (..),-                                          SHA256 (..), SHA512 (..), hashWith)-import           Crypto.MAC.HMAC         (HMAC, hmac)-import           Data.ByteArray          (ByteArrayAccess)-import qualified Data.ByteArray          as BA-import           Data.ByteString         (ByteString)-import qualified Data.ByteString         as BS-import           Data.ByteString.Short   (ShortByteString)-import qualified Data.ByteString.Short   as BSS-import           Data.Either             (fromRight)-import           Data.Hashable           (Hashable)-import           Data.Serialize          (Serialize (..), decode)-import qualified Data.Serialize.Get      as Get-import qualified Data.Serialize.Put      as Put-import           Data.String             (IsString, fromString)-import           Data.String.Conversions (cs)-import           Data.Word               (Word32)-import           GHC.Generics            (Generic)-import           Network.Haskoin.Util-import           Text.Read               as R---- | 'Word32' wrapped for type-safe 32-bit checksums.-newtype CheckSum32 = CheckSum32-    { getCheckSum32 :: Word32-    } deriving (Eq, Ord, Serialize, Show, Read, Hashable, Generic, NFData)---- | Type for 512-bit hashes.-newtype Hash512 = Hash512 { getHash512 :: ShortByteString }-    deriving (Eq, Ord, Hashable, Generic, NFData)---- | Type for 256-bit hashes.-newtype Hash256 = Hash256 { getHash256 :: ShortByteString }-    deriving (Eq, Ord, Hashable, Generic, NFData)---- | Type for 160-bit hashes.-newtype Hash160 = Hash160 { getHash160 :: ShortByteString }-    deriving (Eq, Ord, Hashable, Generic, NFData)--instance Show Hash512 where-    showsPrec _ = shows . encodeHex . BSS.fromShort . getHash512--instance Read Hash512 where-    readPrec = do-        R.String str <- lexP-        maybe pfail return $ Hash512 . BSS.toShort <$> decodeHex (cs str)--instance Show Hash256 where-    showsPrec _ = shows . encodeHex . BSS.fromShort . getHash256--instance Read Hash256 where-    readPrec = do-        R.String str <- lexP-        maybe pfail return $ Hash256 . BSS.toShort <$> decodeHex (cs str)--instance Show Hash160 where-    showsPrec _ = shows . encodeHex . BSS.fromShort . getHash160--instance Read Hash160 where-    readPrec = do-        R.String str <- lexP-        maybe pfail return $ Hash160 . BSS.toShort <$> decodeHex (cs str)--instance IsString Hash512 where-    fromString str =-        case decodeHex $ cs str of-            Nothing -> e-            Just bs ->-                case BS.length bs of-                    64 -> Hash512 (BSS.toShort bs)-                    _  -> e-      where-        e = error "Could not decode hash from hex string"--instance Serialize Hash512 where-    get = Hash512 <$> Get.getShortByteString 64-    put = Put.putShortByteString . getHash512--instance IsString Hash256 where-    fromString str =-        case decodeHex $ cs str of-            Nothing -> e-            Just bs ->-                case BS.length bs of-                    32 -> Hash256 (BSS.toShort bs)-                    _  -> e-      where-        e = error "Could not decode hash from hex string"--instance Serialize Hash256 where-    get = Hash256 <$> Get.getShortByteString 32-    put = Put.putShortByteString . getHash256--instance IsString Hash160 where-    fromString str =-        case decodeHex $ cs str of-            Nothing -> e-            Just bs ->-                case BS.length bs of-                    20 -> Hash160 (BSS.toShort bs)-                    _  -> e-      where-        e = error "Could not decode hash from hex string"--instance Serialize Hash160 where-    get = Hash160 <$> Get.getShortByteString 20-    put = Put.putShortByteString . getHash160---- | Calculate SHA512 hash.-sha512 :: ByteArrayAccess b => b -> Hash512-sha512 = Hash512 . BSS.toShort . BA.convert . hashWith SHA512---- | Calculate SHA256 hash.-sha256 :: ByteArrayAccess b => b -> Hash256-sha256 = Hash256 . BSS.toShort . BA.convert . hashWith SHA256---- | Calculate RIPEMD160 hash.-ripemd160 :: ByteArrayAccess b => b -> Hash160-ripemd160 = Hash160 . BSS.toShort . BA.convert . hashWith RIPEMD160---- | Claculate SHA1 hash.-sha1 :: ByteArrayAccess b => b -> Hash160-sha1 = Hash160 . BSS.toShort . BA.convert . hashWith SHA1---- | Compute two rounds of SHA-256.-doubleSHA256 :: ByteArrayAccess b => b -> Hash256-doubleSHA256 =-    Hash256 . BSS.toShort . BA.convert . hashWith SHA256 . hashWith SHA256---- | Compute SHA-256 followed by RIPMED-160.-addressHash :: ByteArrayAccess b => b -> Hash160-addressHash =-    Hash160 . BSS.toShort . BA.convert . hashWith RIPEMD160 . hashWith SHA256--{- CheckSum -}---- | Computes a 32 bit checksum.-checkSum32 :: ByteArrayAccess b => b -> CheckSum32-checkSum32 = fromRight (error "Could not decode bytes as CheckSum32")-             . decode-             . BS.take 4-             . BA.convert-             . hashWith SHA256-             . hashWith SHA256--{- HMAC -}---- | Computes HMAC over SHA-512.-hmac512 :: ByteString -> ByteString -> Hash512-hmac512 key msg =-    Hash512 $ BSS.toShort $ BA.convert (hmac key msg :: HMAC SHA512)---- | Computes HMAC over SHA-256.-hmac256 :: (ByteArrayAccess k, ByteArrayAccess m) => k -> m -> Hash256-hmac256 key msg =-    Hash256 $ BSS.toShort $ BA.convert (hmac key msg :: HMAC SHA256)---- | Split a 'Hash512' into a pair of 'Hash256'.-split512 :: Hash512 -> (Hash256, Hash256)-split512 h =-    (Hash256 (BSS.toShort a), Hash256 (BSS.toShort b))-  where-    (a, b) = BS.splitAt 32 . BSS.fromShort $ getHash512 h---- | Join a pair of 'Hash256' into a 'Hash512'.-join512 :: (Hash256, Hash256) -> Hash512-join512 (a, b) =-    Hash512 .-    BSS.toShort $-        BSS.fromShort (getHash256 a) `BS.append` BSS.fromShort (getHash256 b)
− src/Network/Haskoin/Crypto/Signature.hs
@@ -1,90 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Crypto.Signature-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--ECDSA signatures using secp256k1 curve. Uses functions from upstream secp256k1-library.--}-module Network.Haskoin.Crypto.Signature-    ( putSig-    , getSig-    , signHash-    , verifyHashSig-    , isCanonicalHalfOrder-    , decodeStrictSig-    , exportSig-    ) where--import           Control.Monad               (guard, unless, when)-import           Crypto.Secp256k1-import           Data.ByteString             (ByteString)-import qualified Data.ByteString             as BS-import           Data.ByteString.Short       (toShort)-import           Data.Maybe                  (fromMaybe)-import           Data.Serialize              as S-import           Data.Serialize.Put          (Putter, putByteString)-import           Network.Haskoin.Crypto.Hash-import           Numeric                     (showHex)---- | Convert 256-bit hash into a 'Msg' for signing or verification.-hashToMsg :: Hash256 -> Msg-hashToMsg =-    fromMaybe e . msg . encode-  where-    e = error "Could not convert 32-byte hash to secp256k1 message"---- | Sign a 256-bit hash using secp256k1 elliptic curve.-signHash :: SecKey -> Hash256 -> Sig-signHash k = signMsg k . hashToMsg---- | Verify an ECDSA signature for a 256-bit hash.-verifyHashSig :: Hash256 -> Sig -> PubKey -> Bool-verifyHashSig h s p =-    verifySig p g m-  where-    (g, _) = normalizeSig s-    m = hashToMsg h---- | Deserialize an ECDSA signature as commonly encoded in Bitcoin.-getSig :: Get Sig-getSig = do-        l <- lookAhead $ do-            t <- getWord8-            -- 0x30 is DER sequence type-            unless (t == 0x30) $ fail $-                "Bad DER identifier byte 0x" ++ showHex t ". Expecting 0x30"-            l <- getWord8-            when (l == 0x00) $ fail "Indeterminate form unsupported"-            when (l >= 0x80) $ fail "Multi-octect length not supported"-            return $ fromIntegral l-        bs <- getByteString $ l + 2-        case decodeStrictSig bs of-            Just s  -> return s-            Nothing -> fail "Invalid signature"---- | Serialize an ECDSA signature for Bitcoin use.-putSig :: Putter Sig-putSig s = putByteString $ exportSig s---- | Is canonical half order.-isCanonicalHalfOrder :: Sig -> Bool-isCanonicalHalfOrder = not . snd . normalizeSig---- | Decode signature strictly.-decodeStrictSig :: ByteString -> Maybe Sig-decodeStrictSig bs = do-    g <- importSig bs-    let compact = exportCompactSig g-    -- <http://www.secg.org/sec1-v2.pdf Section 4.1.4>-    -- 4.1.4.1 (r and s can not be zero)-    guard $ getCompactSigR compact /= zero-    guard $ getCompactSigS compact /= zero-    guard $ isCanonicalHalfOrder g-    return g-  where-    zero = toShort $ BS.replicate 32 0
− src/Network/Haskoin/Keys.hs
@@ -1,18 +0,0 @@-{-|-Module      : Network.Haskoin.Keys-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--ECDSA private and public keys, extended keys (BIP-32) and mnemonic sentences-(BIP-39).--}-module Network.Haskoin.Keys-    ( module X-    ) where--import           Network.Haskoin.Keys.Common   as X-import           Network.Haskoin.Keys.Extended as X-import           Network.Haskoin.Keys.Mnemonic as X
− src/Network/Haskoin/Keys/Common.hs
@@ -1,134 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE FlexibleContexts  #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Keys.Common-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--ECDSA private and public key functions.--}-module Network.Haskoin.Keys.Common-    ( -- * Public & Private Keys-      PubKeyI(..)-    , SecKeyI(..)-    , exportPubKey-    , importPubKey-    , wrapPubKey-    , derivePubKeyI-    , wrapSecKey-    , fromMiniKey-    , tweakPubKey-    , tweakSecKey-    , secKeyPut-    , secKeyGet-    , getSecKey-    , secKey-    ) where--import           Control.Applicative         ((<|>))-import           Control.DeepSeq-import           Control.Monad               (guard, mzero, (<=<))-import           Crypto.Secp256k1-import           Data.Aeson                  (FromJSON, ToJSON, Value (String),-                                              parseJSON, toJSON, withText)-import           Data.ByteString             (ByteString)-import qualified Data.ByteString             as BS-import           Data.Hashable-import           Data.Maybe                  (fromMaybe)-import           Data.Serialize              (Serialize, decode, encode, get,-                                              put)-import           Data.Serialize.Get          (Get, getByteString)-import           Data.Serialize.Put          (Putter, putByteString)-import           Data.String                 (IsString, fromString)-import           Data.String.Conversions     (cs)-import           GHC.Generics                (Generic)-import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Util---- | Elliptic curve public key type with expected serialized compression flag.-data PubKeyI = PubKeyI-    { pubKeyPoint      :: !PubKey-    , pubKeyCompressed :: !Bool-    } deriving (Generic, Eq, Show, Read, Hashable, NFData)--instance IsString PubKeyI where-    fromString str =-        fromMaybe e $ eitherToMaybe . decode <=< decodeHex $ cs str-      where-        e = error "Could not decode public key"--instance ToJSON PubKeyI where-    toJSON = String . encodeHex . encode--instance FromJSON PubKeyI where-    parseJSON = withText "PubKeyI" $-        maybe mzero return . (eitherToMaybe . decode =<<) . decodeHex--instance Serialize PubKeyI where-    get = c <|> u-      where-        c = do-            bs <- getByteString 33-            guard $ BS.head bs `BS.elem` BS.pack [0x02, 0x03]-            maybe mzero return $ PubKeyI <$> importPubKey bs <*> pure True-        u = do-            bs <- getByteString 65-            guard $ BS.head bs == 0x04-            maybe mzero return $ PubKeyI <$> importPubKey bs <*> pure False--    put pk = putByteString $ exportPubKey (pubKeyCompressed pk) (pubKeyPoint pk)---- | Wrap a public key from secp256k1 library adding information about compression.-wrapPubKey :: Bool -> PubKey -> PubKeyI-wrapPubKey c p = PubKeyI p c---- | Derives a public key from a private key. This function will preserve--- compression flag.-derivePubKeyI :: SecKeyI -> PubKeyI-derivePubKeyI (SecKeyI d c) = PubKeyI (derivePubKey d) c---- | Tweak a public key.-tweakPubKey :: PubKey -> Hash256 -> Maybe PubKey-tweakPubKey p h = tweakAddPubKey p =<< tweak (encode h)---- | Elliptic curve private key type with expected public key compression--- information. Compression information is stored in private key WIF formats and--- needs to be preserved to generate the correct address from the corresponding--- public key.-data SecKeyI = SecKeyI-    { secKeyData       :: !SecKey-    , secKeyCompressed :: !Bool-    } deriving (Eq, Show, Read, Generic, NFData)---- | Wrap private key with corresponding public key compression flag.-wrapSecKey :: Bool -> SecKey -> SecKeyI-wrapSecKey c d = SecKeyI d c---- | Deserialize 'SecKey'.-secKeyGet :: Get SecKey-secKeyGet = do-    bs <- getByteString 32-    maybe (fail "invalid private key") return (secKey bs)---- | Serialize 'SecKey'.-secKeyPut :: Putter SecKey-secKeyPut = putByteString . getSecKey---- | Decode Casascius mini private keys (22 or 30 characters).-fromMiniKey :: ByteString -> Maybe SecKeyI-fromMiniKey bs = do-    guard checkShortKey-    wrapSecKey False <$> secKey (encode (sha256 bs))-  where-    checkHash = encode $ sha256 $ bs `BS.append` "?"-    checkShortKey = BS.length bs `elem` [22, 30] && BS.head checkHash == 0x00---- | Tweak a private key.-tweakSecKey :: SecKey -> Hash256 -> Maybe SecKey-tweakSecKey key h = tweakAddSecKey key =<< tweak (encode h)
− src/Network/Haskoin/Keys/Extended.hs
@@ -1,913 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs             #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Keys.Extended-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--BIP-32 extended keys.--}-module Network.Haskoin.Keys.Extended-    (-      -- * Extended Keys-      XPubKey(..)-    , XPrvKey(..)-    , ChainCode-    , KeyIndex-    , Fingerprint-    , DerivationException(..)-    , makeXPrvKey-    , deriveXPubKey-    , prvSubKey-    , pubSubKey-    , hardSubKey-    , xPrvIsHard-    , xPubIsHard-    , xPrvChild-    , xPubChild-    , xPubID-    , xPrvID-    , xPubFP-    , xPrvFP-    , xPubAddr-    , xPubWitnessAddr-    , xPubCompatWitnessAddr-    , xPubExport-    , xPubToJSON-    , xPubFromJSON-    , xPrvExport-    , xPrvToJSON-    , xPrvFromJSON-    , xPubImport-    , xPrvImport-    , xPrvWif-    , putXPrvKey-    , putXPubKey-    , getXPrvKey-    , getXPubKey--      -- ** Helper Functions-    , prvSubKeys-    , pubSubKeys-    , hardSubKeys-    , deriveAddr-    , deriveWitnessAddr-    , deriveCompatWitnessAddr-    , deriveAddrs-    , deriveWitnessAddrs-    , deriveCompatWitnessAddrs-    , deriveMSAddr-    , deriveMSAddrs-    , cycleIndex--      -- ** Derivation Paths-    , DerivPathI(..)-    , AnyDeriv, HardDeriv, SoftDeriv-    , HardOrAny-    , AnyOrSoft-    , DerivPath-    , HardPath-    , SoftPath-    , Bip32PathIndex (..)-    , derivePath-    , derivePubPath-    , toHard-    , toSoft-    , toGeneric-    , (++/)-    , pathToStr-    , listToPath-    , pathToList--      -- *** Derivation Path Parser-    , XKey(..)-    , ParsedPath(..)-    , parsePath-    , parseHard-    , parseSoft-    , applyPath-    , derivePathAddr-    , derivePathAddrs-    , derivePathMSAddr-    , derivePathMSAddrs-    , concatBip32Segments-    ) where--import           Control.Applicative-import           Control.DeepSeq-import           Control.Exception              (Exception, throw)-import           Control.Monad                  (guard, mzero, unless, (<=<))-import           Crypto.Secp256k1-import           Data.Aeson                     as A (FromJSON, ToJSON,-                                                      Value (String), parseJSON,-                                                      toJSON, withText)-import           Data.Aeson.Types               (Parser)-import           Data.Bits                      (clearBit, setBit, testBit)-import           Data.ByteString                (ByteString)-import qualified Data.ByteString                as B-import           Data.Either                    (fromRight)-import           Data.List                      (foldl')-import           Data.List.Split                (splitOn)-import           Data.Maybe                     (fromMaybe)-import           Data.Monoid                    ((<>))-import           Data.Serialize                 as S (Serialize, decode, encode,-                                                      get, put)-import           Data.Serialize.Get             (Get, getWord32be, getWord8,-                                                 runGet)-import           Data.Serialize.Put             (Putter, putWord32be, putWord8,-                                                 runPut)-import           Data.String                    (IsString, fromString)-import           Data.String.Conversions        (cs)-import           Data.Typeable                  (Typeable)-import           Data.Word                      (Word32, Word8)-import           GHC.Generics                   (Generic)-import           Network.Haskoin.Address-import           Network.Haskoin.Address.Base58-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Keys.Common-import           Network.Haskoin.Script-import           Network.Haskoin.Util-import           Text.Read                      as R-import           Text.Read.Lex----- | A derivation exception is thrown in the very unlikely event that a--- derivation is invalid.-newtype DerivationException = DerivationException String-    deriving (Eq, Read, Show, Typeable, Generic, NFData)--instance Exception DerivationException---- | Chain code as specified in BIP-32.-type ChainCode = Hash256---- | Index of key as specified in BIP-32.-type KeyIndex = Word32---- | Fingerprint of parent-type Fingerprint = Word32---- | Data type representing an extended BIP32 private key. An extended key--- is a node in a tree of key derivations. It has a depth in the tree, a--- parent node and an index to differentiate it from other siblings.-data XPrvKey = XPrvKey-    { xPrvDepth  :: !Word8       -- ^ depth in the tree-    , xPrvParent :: !Fingerprint -- ^ fingerprint of parent-    , xPrvIndex  :: !KeyIndex    -- ^ derivation index-    , xPrvChain  :: !ChainCode   -- ^ chain code-    , xPrvKey    :: !SecKey      -- ^ private key of this node-    } deriving (Generic, Eq, Show, Read, NFData)--xPrvToJSON :: Network -> XPrvKey -> Value-xPrvToJSON net = A.String . xPrvExport net---- | Data type representing an extended BIP32 public key.-data XPubKey = XPubKey-    { xPubDepth  :: !Word8     -- ^ depth in the tree-    , xPubParent :: !Fingerprint    -- ^ fingerprint of parent-    , xPubIndex  :: !KeyIndex  -- ^ derivation index-    , xPubChain  :: !ChainCode -- ^ chain code-    , xPubKey    :: !PubKey    -- ^ public key of this node-    } deriving (Generic, Eq, Show, Read, NFData)----- | Decode an extended public key from a JSON string-xPubFromJSON :: Network -> Value -> Parser XPubKey-xPubFromJSON net =-    withText "xpub" $ \t ->-        case xPubImport net t of-            Nothing -> fail "could not read xpub"-            Just x  -> return x---- | Get JSON 'Value' from 'XPubKey'.-xPubToJSON :: Network -> XPubKey -> Value-xPubToJSON net = A.String . xPubExport net---- | Decode an extended private key from a JSON string-xPrvFromJSON :: Network -> Value -> Parser XPrvKey-xPrvFromJSON net =-    withText "xprv" $ \t ->-        case xPrvImport net t of-            Nothing -> fail "could not read xprv"-            Just x  -> return x---- | Build a BIP32 compatible extended private key from a bytestring. This will--- produce a root node (@depth=0@ and @parent=0@).-makeXPrvKey :: ByteString -> XPrvKey-makeXPrvKey bs =-    XPrvKey 0 0 0 c k-  where-    (p, c) = split512 $ hmac512 "Bitcoin seed" bs-    k     = fromMaybe err (secKey (encode p))-    err   = throw $ DerivationException "Invalid seed"---- | Derive an extended public key from an extended private key. This function--- will preserve the depth, parent, index and chaincode fields of the extended--- private keys.-deriveXPubKey :: XPrvKey -> XPubKey-deriveXPubKey (XPrvKey d p i c k) = XPubKey d p i c (derivePubKey k)---- | Compute a private, soft child key derivation. A private soft derivation--- will allow the equivalent extended public key to derive the public key for--- this child. Given a parent key /m/ and a derivation index /i/, this function--- will compute /m\/i/.------ Soft derivations allow for more flexibility such as read-only wallets.--- However, care must be taken not the leak both the parent extended public key--- and one of the extended child private keys as this would compromise the--- extended parent private key.-prvSubKey :: XPrvKey  -- ^ extended parent private key-          -> KeyIndex -- ^ child derivation index-          -> XPrvKey  -- ^ extended child private key-prvSubKey xkey child-    | child >= 0 && child < 0x80000000 =-        XPrvKey (xPrvDepth xkey + 1) (xPrvFP xkey) child c k-    | otherwise = error "Invalid child derivation index"-  where-    pK = xPubKey $ deriveXPubKey xkey-    m = B.append (exportPubKey True pK) (encode child)-    (a, c) = split512 $ hmac512 (encode $ xPrvChain xkey) m-    k = fromMaybe err $ tweakSecKey (xPrvKey xkey) a-    err = throw $ DerivationException "Invalid prvSubKey derivation"---- | Compute a public, soft child key derivation. Given a parent key /M/--- and a derivation index /i/, this function will compute /M\/i/.-pubSubKey :: XPubKey  -- ^ extended parent public key-          -> KeyIndex -- ^ child derivation index-          -> XPubKey  -- ^ extended child public key-pubSubKey xKey child-    | child >= 0 && child < 0x80000000 =-        XPubKey (xPubDepth xKey + 1) (xPubFP xKey) child c pK-    | otherwise = error "Invalid child derivation index"-  where-    m      = B.append (exportPubKey True (xPubKey xKey)) (encode child)-    (a, c) = split512 $ hmac512 (encode $ xPubChain xKey) m-    pK     = fromMaybe err $ tweakPubKey (xPubKey xKey) a-    err    = throw $ DerivationException "Invalid pubSubKey derivation"---- | Compute a hard child key derivation. Hard derivations can only be computed--- for private keys. Hard derivations do not allow the parent public key to--- derive the child public keys. However, they are safer as a breach of the--- parent public key and child private keys does not lead to a breach of the--- parent private key. Given a parent key /m/ and a derivation index /i/, this--- function will compute /m\/i'/.-hardSubKey :: XPrvKey  -- ^ extended parent private key-           -> KeyIndex -- ^ child derivation index-           -> XPrvKey  -- ^ extended child private key-hardSubKey xkey child-    | child >= 0 && child < 0x80000000 =-        XPrvKey (xPrvDepth xkey + 1) (xPrvFP xkey) i c k-    | otherwise = error "Invalid child derivation index"-  where-    i      = setBit child 31-    m      = B.append (bsPadPrvKey $ xPrvKey xkey) (encode i)-    (a, c) = split512 $ hmac512 (encode $ xPrvChain xkey) m-    k      = fromMaybe err $ tweakSecKey (xPrvKey xkey) a-    err    = throw $ DerivationException "Invalid hardSubKey derivation"---- | Returns true if the extended private key was derived through a hard--- derivation.-xPrvIsHard :: XPrvKey -> Bool-xPrvIsHard k = testBit (xPrvIndex k) 31---- | Returns true if the extended public key was derived through a hard--- derivation.-xPubIsHard :: XPubKey -> Bool-xPubIsHard k = testBit (xPubIndex k) 31---- | Returns the derivation index of this extended private key without the hard--- bit set.-xPrvChild :: XPrvKey -> KeyIndex-xPrvChild k = clearBit (xPrvIndex k) 31---- | Returns the derivation index of this extended public key without the hard--- bit set.-xPubChild :: XPubKey -> KeyIndex-xPubChild k = clearBit (xPubIndex k) 31---- | Computes the key identifier of an extended private key.-xPrvID :: XPrvKey -> Hash160-xPrvID = xPubID . deriveXPubKey---- | Computes the key identifier of an extended public key.-xPubID :: XPubKey -> Hash160-xPubID = ripemd160 . encode . sha256 . exportPubKey True . xPubKey---- | Computes the key fingerprint of an extended private key.-xPrvFP :: XPrvKey -> Fingerprint-xPrvFP =-    fromRight err . decode . B.take 4 . encode . xPrvID-  where-    err = error "Could not decode xPrvFP"---- | Computes the key fingerprint of an extended public key.-xPubFP :: XPubKey -> Fingerprint-xPubFP =-    fromRight err . decode . B.take 4 . encode . xPubID-  where-    err = error "Could not decode xPubFP"---- | Compute a standard P2PKH address for an extended public key.-xPubAddr :: XPubKey -> Address-xPubAddr xkey = pubKeyAddr (wrapPubKey True (xPubKey xkey))---- | Compute a SegWit P2WPKH address for an extended public key.-xPubWitnessAddr :: XPubKey -> Address-xPubWitnessAddr xkey = pubKeyWitnessAddr (wrapPubKey True (xPubKey xkey))---- | Compute a backwards-compatible SegWit P2SH-P2WPKH address for an extended--- public key.-xPubCompatWitnessAddr :: XPubKey -> Address-xPubCompatWitnessAddr xkey =-    pubKeyCompatWitnessAddr (wrapPubKey True (xPubKey xkey))---- | Exports an extended private key to the BIP32 key export format ('Base58').-xPrvExport :: Network -> XPrvKey -> Base58-xPrvExport net = encodeBase58Check . runPut . putXPrvKey net---- | Exports an extended public key to the BIP32 key export format ('Base58').-xPubExport :: Network -> XPubKey -> Base58-xPubExport net = encodeBase58Check . runPut . putXPubKey net---- | Decodes a BIP32 encoded extended private key. This function will fail if--- invalid base 58 characters are detected or if the checksum fails.-xPrvImport :: Network -> Base58 -> Maybe XPrvKey-xPrvImport net = eitherToMaybe . runGet (getXPrvKey net) <=< decodeBase58Check---- | Decodes a BIP32 encoded extended public key. This function will fail if--- invalid base 58 characters are detected or if the checksum fails.-xPubImport :: Network -> Base58 -> Maybe XPubKey-xPubImport net = eitherToMaybe . runGet (getXPubKey net) <=< decodeBase58Check---- | Export an extended private key to WIF (Wallet Import Format).-xPrvWif :: Network -> XPrvKey -> Base58-xPrvWif net xkey = toWif net (wrapSecKey True (xPrvKey xkey))---- | Parse a binary extended private key.-getXPrvKey :: Network -> Get XPrvKey-getXPrvKey net = do-        ver <- getWord32be-        unless (ver == getExtSecretPrefix net) $ fail-            "Get: Invalid version for extended private key"-        XPrvKey <$> getWord8-                <*> getWord32be-                <*> getWord32be-                <*> S.get-                <*> getPadPrvKey---- | Serialize an extended private key.-putXPrvKey :: Network -> Putter XPrvKey-putXPrvKey net k = do-        putWord32be  $ getExtSecretPrefix net-        putWord8     $ xPrvDepth k-        putWord32be  $ xPrvParent k-        putWord32be  $ xPrvIndex k-        put          $ xPrvChain k-        putPadPrvKey $ xPrvKey k---- | Parse a binary extended public key.-getXPubKey :: Network -> Get XPubKey-getXPubKey net = do-        ver <- getWord32be-        unless (ver == getExtPubKeyPrefix net) $ fail-            "Get: Invalid version for extended public key"-        XPubKey <$> getWord8-                <*> getWord32be-                <*> getWord32be-                <*> S.get-                <*> (pubKeyPoint <$> S.get)---- | Serialize an extended public key.-putXPubKey :: Network -> Putter XPubKey-putXPubKey net k = do-        putWord32be $ getExtPubKeyPrefix net-        putWord8    $ xPubDepth k-        putWord32be $ xPubParent k-        putWord32be $ xPubIndex k-        put         $ xPubChain k-        put         $ wrapPubKey True (xPubKey k)--{- Derivation helpers -}---- | Cyclic list of all private soft child key derivations of a parent key--- starting from an offset index.-prvSubKeys :: XPrvKey -> KeyIndex -> [(XPrvKey, KeyIndex)]-prvSubKeys k = map (\i -> (prvSubKey k i, i)) . cycleIndex---- | Cyclic list of all public soft child key derivations of a parent key--- starting from an offset index.-pubSubKeys :: XPubKey -> KeyIndex -> [(XPubKey, KeyIndex)]-pubSubKeys k = map (\i -> (pubSubKey k i, i)) . cycleIndex---- | Cyclic list of all hard child key derivations of a parent key starting--- from an offset index.-hardSubKeys :: XPrvKey -> KeyIndex -> [(XPrvKey, KeyIndex)]-hardSubKeys k = map (\i -> (hardSubKey k i, i)) . cycleIndex---- | Derive a standard address from an extended public key and an index.-deriveAddr :: XPubKey -> KeyIndex -> (Address, PubKey)-deriveAddr k i =-    (xPubAddr key, xPubKey key)-  where-    key = pubSubKey k i---- | Derive a SegWit P2WPKH address from an extended public key and an index.-deriveWitnessAddr :: XPubKey -> KeyIndex -> (Address, PubKey)-deriveWitnessAddr k i =-    (xPubWitnessAddr key, xPubKey key)-  where-    key = pubSubKey k i---- | Derive a backwards-compatible SegWit P2SH-P2WPKH address from an extended--- public key and an index.-deriveCompatWitnessAddr :: XPubKey -> KeyIndex -> (Address, PubKey)-deriveCompatWitnessAddr k i =-    (xPubCompatWitnessAddr key, xPubKey key)-  where-    key = pubSubKey k i---- | Cyclic list of all addresses derived from a public key starting from an--- offset index.-deriveAddrs :: XPubKey -> KeyIndex -> [(Address, PubKey, KeyIndex)]-deriveAddrs k =-    map f . cycleIndex-  where-    f i = let (a, key) = deriveAddr k i in (a, key, i)---- | Cyclic list of all SegWit P2WPKH addresses derived from a public key--- starting from an offset index.-deriveWitnessAddrs :: XPubKey -> KeyIndex -> [(Address, PubKey, KeyIndex)]-deriveWitnessAddrs k =-    map f . cycleIndex-  where-    f i = let (a, key) = deriveWitnessAddr k i in (a, key, i)---- | Cyclic list of all backwards-compatible SegWit P2SH-P2WPKH addresses--- derived from a public key starting from an offset index.-deriveCompatWitnessAddrs :: XPubKey -> KeyIndex -> [(Address, PubKey, KeyIndex)]-deriveCompatWitnessAddrs k =-    map f . cycleIndex-  where-    f i = let (a, key) = deriveCompatWitnessAddr k i in (a, key, i)---- | Derive a multisig address from a list of public keys, the number of--- required signatures /m/ and a derivation index. The derivation type is a--- public, soft derivation.-deriveMSAddr :: [XPubKey] -> Int -> KeyIndex -> (Address, RedeemScript)-deriveMSAddr keys m i = (payToScriptAddress rdm, rdm)-  where-    rdm = sortMulSig $ PayMulSig k m-    k = map (wrapPubKey True . xPubKey . flip pubSubKey i) keys---- | Cyclic list of all multisig addresses derived from a list of public keys,--- a number of required signatures /m/ and starting from an offset index. The--- derivation type is a public, soft derivation.-deriveMSAddrs :: [XPubKey] -> Int -> KeyIndex-              -> [(Address, RedeemScript, KeyIndex)]-deriveMSAddrs keys m = map f . cycleIndex-  where-    f i =-        let (a, rdm) = deriveMSAddr keys m i-         in (a, rdm, i)---- | Helper function to go through derivation indices.-cycleIndex :: KeyIndex -> [KeyIndex]-cycleIndex i-    | i == 0         = cycle [0..0x7fffffff]-    | i < 0x80000000 = cycle $ [i..0x7fffffff] ++ [0..(i-1)]-    | otherwise      = error $ "cycleIndex: invalid index " ++ show i--{- Derivation Paths -}---- | Phantom type signaling a hardened derivation path that can only be computed--- from private extended key.-data HardDeriv deriving (Generic, NFData)---- | Phantom type signaling no knowledge about derivation path: can be hardened or not.-data AnyDeriv deriving (Generic, NFData)---- | Phantom type signaling derivation path including only non-hardened paths--- that can be computed from an extended public key.-data SoftDeriv deriving (Generic, NFData)---- | Hardened derivation path. Can be computed from extended private key only.-type HardPath = DerivPathI HardDeriv---- | Any derivation path.-type DerivPath = DerivPathI AnyDeriv---- | Non-hardened derivation path can be computed from extended public key.-type SoftPath = DerivPathI SoftDeriv---- | Helper class to perform validations on a hardened derivation path.-class HardOrAny a-instance HardOrAny HardDeriv-instance HardOrAny AnyDeriv---- | Helper class to perform validations on a non-hardened derivation path.-class AnyOrSoft a-instance AnyOrSoft AnyDeriv-instance AnyOrSoft SoftDeriv---- | Data type representing a derivation path. Two constructors are provided--- for specifying soft or hard derivations. The path /\/0\/1'\/2/ for example can be--- expressed as @'Deriv' :\/ 0 :| 1 :\/ 2@. The 'HardOrAny' and 'AnyOrSoft' type--- classes are used to constrain the valid values for the phantom type /t/. If--- you mix hard '(:|)' and soft '(:\/)' paths, the only valid type for /t/ is 'AnyDeriv'.--- Otherwise, /t/ can be 'HardDeriv' if you only have hard derivation or 'SoftDeriv'--- if you only have soft derivations.------ Using this type is as easy as writing the required derivation like in these--- example:------ > Deriv :/ 0 :/ 1 :/ 2 :: SoftPath--- > Deriv :| 0 :| 1 :| 2 :: HardPath--- > Deriv :| 0 :/ 1 :/ 2 :: DerivPath-data DerivPathI t where-    (:|)  :: HardOrAny t => !(DerivPathI t) -> !KeyIndex -> DerivPathI t-    (:/)  :: AnyOrSoft t => !(DerivPathI t) -> !KeyIndex -> DerivPathI t-    Deriv :: DerivPathI t--instance NFData (DerivPathI t) where-    rnf (a :| b) = rnf a `seq` rnf b `seq` ()-    rnf (a :/ b) = rnf a `seq` rnf b `seq` ()-    rnf Deriv = ()--instance Eq (DerivPathI t) where-    (nextA :| iA) == (nextB :| iB) = iA == iB && nextA == nextB-    (nextA :/ iA) == (nextB :/ iB) = iA == iB && nextA == nextB-    Deriv         == Deriv         = True-    _             == _             = False--instance Ord (DerivPathI t) where-    -- Same hardness on each side-    (nextA :| iA) `compare` (nextB :| iB) =-        if nextA == nextB then iA `compare` iB else nextA `compare` nextB-    (nextA :/ iA) `compare` (nextB :/ iB) =-        if nextA == nextB then iA `compare` iB else nextA `compare` nextB--    -- Different hardness: hard paths are LT soft paths-    (nextA :/ iA) `compare` (nextB :| iB) =-        if nextA == nextB then LT else nextA `compare` nextB-    (nextA :| iA) `compare` (nextB :/ iB) =-        if nextA == nextB then GT else nextA `compare` nextB--    Deriv `compare` Deriv  = EQ-    Deriv `compare` _      = LT-    _     `compare` Deriv  = GT---instance Serialize DerivPath where-    get = listToPath <$> S.get-    put = put . pathToList---- | Get a list of derivation indices from a derivation path.-pathToList :: DerivPathI t -> [KeyIndex]-pathToList =-    reverse . go-  where-    go (next :| i) = setBit i 31 : go next-    go (next :/ i) = i : go next-    go _           = []---- | Convert a list of derivation indices to a derivation path.-listToPath :: [KeyIndex] -> DerivPath-listToPath =-    go . reverse-  where-    go (i:is)-        | testBit i 31 = go is :| clearBit i 31-        | otherwise    = go is :/ i-    go [] = Deriv---- | Convert a derivation path to a human-readable string.-pathToStr :: DerivPathI t -> String-pathToStr p =-    case p of-        next :| i -> concat [ pathToStr next, "/", show i, "'" ]-        next :/ i -> concat [ pathToStr next, "/", show i ]-        Deriv     -> ""---- | Turn a derivation path into a hard derivation path. Will fail if the path--- contains soft derivations.-toHard :: DerivPathI t -> Maybe HardPath-toHard p = case p of-    next :| i -> (:| i) <$> toHard next-    Deriv     -> Just Deriv-    _         -> Nothing---- | Turn a derivation path into a soft derivation path. Will fail if the path--- has hard derivations.-toSoft :: DerivPathI t -> Maybe SoftPath-toSoft p = case p of-    next :/ i -> (:/ i) <$> toSoft next-    Deriv     -> Just Deriv-    _         -> Nothing---- | Make a derivation path generic.-toGeneric :: DerivPathI t -> DerivPath-toGeneric p = case p of-    next :/ i -> toGeneric next :/ i-    next :| i -> toGeneric next :| i-    Deriv     -> Deriv---- | Append two derivation paths together. The result will be a mixed--- derivation path.-(++/) :: DerivPathI t1 -> DerivPathI t2 -> DerivPath-(++/) p1 p2 =-    go id (toGeneric p2) $ toGeneric p1-  where-    go f p = case p of-        next :/ i -> go (f . (:/ i)) $ toGeneric next-        next :| i -> go (f . (:| i)) $ toGeneric next-        _         -> f---- | Derive a private key from a derivation path-derivePath :: DerivPathI t -> XPrvKey -> XPrvKey-derivePath = go id-  where-    -- Build the full derivation function starting from the end-    go f p = case p of-        next :| i -> go (f . flip hardSubKey i) next-        next :/ i -> go (f . flip prvSubKey i) next-        _         -> f---- | Derive a public key from a soft derivation path-derivePubPath :: SoftPath -> XPubKey -> XPubKey-derivePubPath = go id-  where-    -- Build the full derivation function starting from the end-    go f p = case p of-        next :/ i -> go (f . flip pubSubKey i) next-        _         -> f--instance Show DerivPath where-    showsPrec d p = showParen (d > 10) $-        showString "DerivPath " . shows (pathToStr p)--instance Read DerivPath where-    readPrec = parens $ do-        R.Ident "DerivPath" <- lexP-        R.String str <- lexP-        maybe pfail return $ getParsedPath <$> parsePath str--instance Show HardPath where-    showsPrec d p = showParen (d > 10) $-        showString "HardPath " . shows (pathToStr p)--instance Read HardPath where-    readPrec = parens $ do-        R.Ident "HardPath" <- lexP-        R.String str <- lexP-        maybe pfail return $ parseHard str--instance Show SoftPath where-    showsPrec d p = showParen (d > 10) $-        showString "SoftPath " . shows (pathToStr p)--instance Read SoftPath where-    readPrec = parens $ do-        R.Ident "SoftPath" <- lexP-        R.String str <- lexP-        maybe pfail return $ parseSoft str--instance IsString ParsedPath where-    fromString =-        fromMaybe e . parsePath-      where-        e = error "Could not parse derivation path"--instance IsString DerivPath where-    fromString =-        getParsedPath . fromMaybe e . parsePath-      where-        e = error "Could not parse derivation path"--instance IsString HardPath where-    fromString =-        fromMaybe e . parseHard-      where-        e = error "Could not parse hard derivation path"--instance IsString SoftPath where-    fromString =-        fromMaybe e . parseSoft-      where-        e = error "Could not parse soft derivation path"--instance FromJSON ParsedPath where-    parseJSON = withText "ParsedPath" $ \str -> case parsePath $ cs str of-        Just p -> return p-        _      -> mzero--instance FromJSON DerivPath where-    parseJSON = withText "DerivPath" $ \str -> case parsePath $ cs str of-        Just p -> return $ getParsedPath p-        _      -> mzero--instance FromJSON HardPath where-    parseJSON = withText "HardPath" $ \str -> case parseHard $ cs str of-        Just p -> return p-        _      -> mzero--instance FromJSON SoftPath where-    parseJSON = withText "SoftPath" $ \str -> case parseSoft $ cs str of-        Just p -> return p-        _      -> mzero--instance ToJSON (DerivPathI t) where-    toJSON = A.String . cs . pathToStr--instance ToJSON ParsedPath where-    toJSON (ParsedPrv p)   = A.String . cs . ("m" ++) . pathToStr $ p-    toJSON (ParsedPub p)   = A.String . cs . ("M" ++) . pathToStr $ p-    toJSON (ParsedEmpty p) = A.String . cs . ("" ++) . pathToStr $ p--{- Parsing derivation paths of the form m/1/2'/3 or M/1/2'/3 -}---- | Type for parsing derivation paths of the form /m\/1\/2'\/3/ or--- /M\/1\/2'\/3/.-data ParsedPath = ParsedPrv   { getParsedPath :: !DerivPath }-                | ParsedPub   { getParsedPath :: !DerivPath }-                | ParsedEmpty { getParsedPath :: !DerivPath }-  deriving (Eq, Generic, NFData)--instance Show ParsedPath where-    showsPrec d p = showParen (d > 10) $ showString "ParsedPath " . shows f-      where-        f =-            case p of-                ParsedPrv d'   -> "m" <> pathToStr d'-                ParsedPub d'   -> "M" <> pathToStr d'-                ParsedEmpty d' -> pathToStr d'--instance Read ParsedPath where-    readPrec = parens $ do-        R.Ident "ParsedPath" <- lexP-        R.String str <- lexP-        maybe pfail return $ parsePath str---- | Parse derivation path string for extended key.--- Forms: /m\/0'\/2/, /M\/2\/3\/4/.-parsePath :: String -> Maybe ParsedPath-parsePath str = do-    res <- concatBip32Segments <$> mapM parseBip32PathIndex xs-    case x of-        "m" -> Just $ ParsedPrv res-        "M" -> Just $ ParsedPub res-        ""  -> Just $ ParsedEmpty res-        _   -> Nothing-  where-    (x : xs) = splitOn "/" str---- | Concatenate derivation path indices into a derivation path.-concatBip32Segments :: [Bip32PathIndex] -> DerivPath-concatBip32Segments = foldl' appendBip32Segment Deriv---- | Append an extra derivation path index element into an existing path.-appendBip32Segment :: DerivPath -> Bip32PathIndex  -> DerivPath-appendBip32Segment d (Bip32SoftIndex i) = d :/ i-appendBip32Segment d (Bip32HardIndex i) = d :| i---- | Parse a BIP32 derivation path index element from a string.-parseBip32PathIndex :: String -> Maybe Bip32PathIndex-parseBip32PathIndex segment = case reads segment of-    [(i, "" )] -> guard (is31Bit i) >> return (Bip32SoftIndex i)-    [(i, "'")] -> guard (is31Bit i) >> return (Bip32HardIndex i)-    _          -> Nothing---- | Type for BIP32 path index element.-data Bip32PathIndex = Bip32HardIndex KeyIndex-                    | Bip32SoftIndex KeyIndex-  deriving (Eq, Generic, NFData)--instance Show Bip32PathIndex where-    showsPrec d (Bip32HardIndex i) = showParen (d > 10) $-        showString "Bip32HardIndex " . shows i-    showsPrec d (Bip32SoftIndex i) = showParen (d > 10) $-        showString "Bip32SoftIndex " . shows i--instance Read Bip32PathIndex where-    readPrec = h <|> s-      where-        h =-            parens $ do-                R.Ident "Bip32HardIndex" <- lexP-                R.Number n <- lexP-                maybe pfail return $-                    Bip32HardIndex . fromIntegral <$> numberToInteger n-        s =-            parens $ do-                R.Ident "Bip32SoftIndex" <- lexP-                R.Number n <- lexP-                maybe pfail return $-                    Bip32SoftIndex . fromIntegral <$> numberToInteger n---- | Test whether the number could be a valid BIP32 derivation index.-is31Bit :: (Integral a) => a -> Bool-is31Bit i = i >= 0 && i < 0x80000000---- | Helper function to parse a hard path.-parseHard :: String -> Maybe HardPath-parseHard = toHard . getParsedPath <=< parsePath---- | Helper function to parse a soft path.-parseSoft :: String -> Maybe SoftPath-parseSoft = toSoft . getParsedPath <=< parsePath---- | Data type representing a private or public key with its respective network.-data XKey-    = XPrv { getXKeyPrv :: !XPrvKey-           , getXKeyNet :: !Network }-    | XPub { getXKeyPub :: !XPubKey-           , getXKeyNet :: !Network }-    deriving (Eq, Show, Generic, NFData)---- | Apply a parsed path to an extended key to derive the new key defined in the--- path. If the path starts with /m/, a private key will be returned and if the--- path starts with /M/, a public key will be returned. Private derivations on a--- public key, and public derivations with a hard segment, return an error--- value.-applyPath :: ParsedPath -> XKey -> Either String XKey-applyPath path key =-    case (path, key) of-        (ParsedPrv _, XPrv k n) -> return $ XPrv (derivPrvF k) n-        (ParsedPrv _, XPub {}) -> Left "applyPath: Invalid public key"-        (ParsedPub _, XPrv k n) -> return $ XPub (deriveXPubKey (derivPrvF k)) n-        (ParsedPub _, XPub k n) -> derivPubFE >>= \f -> return $ XPub (f k) n-    -- For empty parsed paths, we take a hint from the provided key-        (ParsedEmpty _, XPrv k n) -> return $ XPrv (derivPrvF k) n-        (ParsedEmpty _, XPub k n) -> derivPubFE >>= \f -> return $ XPub (f k) n-  where-    derivPrvF = goPrv id $ getParsedPath path-    derivPubFE = goPubE id $ getParsedPath path-    -- Build the full private derivation function starting from the end-    goPrv f p =-        case p of-            next :| i -> goPrv (f . flip hardSubKey i) next-            next :/ i -> goPrv (f . flip prvSubKey i) next-            Deriv     -> f-    -- Build the full public derivation function starting from the end-    goPubE f p =-        case p of-            next :/ i -> goPubE (f . flip pubSubKey i) next-            Deriv     -> Right f-            _         -> Left "applyPath: Invalid hard derivation"--{- Helpers for derivation paths and addresses -}---- | Derive an address from a given parent path.-derivePathAddr :: XPubKey -> SoftPath -> KeyIndex -> (Address, PubKey)-derivePathAddr key path = deriveAddr (derivePubPath path key)---- | Cyclic list of all addresses derived from a given parent path and starting--- from the given offset index.-derivePathAddrs ::-       XPubKey -> SoftPath -> KeyIndex -> [(Address, PubKey, KeyIndex)]-derivePathAddrs key path = deriveAddrs (derivePubPath path key)---- | Derive a multisig address from a given parent path. The number of required--- signatures (m in m of n) is also needed.-derivePathMSAddr ::-       [XPubKey]-    -> SoftPath-    -> Int-    -> KeyIndex-    -> (Address, RedeemScript)-derivePathMSAddr keys path =-    deriveMSAddr $ map (derivePubPath path) keys---- | Cyclic list of all multisig addresses derived from a given parent path and--- starting from the given offset index. The number of required signatures--- (m in m of n) is also needed.-derivePathMSAddrs ::-       [XPubKey]-    -> SoftPath-    -> Int-    -> KeyIndex-    -> [(Address, RedeemScript, KeyIndex)]-derivePathMSAddrs keys path =-    deriveMSAddrs $ map (derivePubPath path) keys--{- Utilities for extended keys -}---- | De-serialize HDW-specific private key.-getPadPrvKey :: Get SecKey-getPadPrvKey = do-    pad <- getWord8-    unless (pad == 0x00) $ fail "Private key must be padded with 0x00"-    secKeyGet---- | Serialize HDW-specific private key.-putPadPrvKey :: Putter SecKey-putPadPrvKey p = putWord8 0x00 >> secKeyPut p--bsPadPrvKey :: SecKey -> ByteString-bsPadPrvKey = runPut . putPadPrvKey
− src/Network/Haskoin/Keys/Mnemonic.hs
@@ -1,516 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Keys.Mnemonic-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Mnemonic keys (BIP-39). Only English dictionary.--}-module Network.Haskoin.Keys.Mnemonic-    ( -- * Mnemonic Sentences-      Entropy-    , Mnemonic-    , Passphrase-    , Seed-    , toMnemonic-    , fromMnemonic-    , mnemonicToSeed-    ) where--import           Control.Monad           (when)-import           Crypto.Hash             (SHA256 (..), hashWith)-import           Crypto.KDF.PBKDF2       (Parameters (..), fastPBKDF2_SHA512)-import           Data.Bits               (shiftL, shiftR)-import qualified Data.ByteArray          as BA-import           Data.ByteString         (ByteString)-import qualified Data.ByteString         as BS-import           Data.List-import qualified Data.Map.Strict         as M-import           Data.Maybe-import           Data.String.Conversions (cs)-import           Data.Text               (Text)-import qualified Data.Text               as T-import qualified Data.Text.Encoding      as E-import           Data.Vector             (Vector, (!))-import qualified Data.Vector             as V-import           Network.Haskoin.Util---- | Random data used to create a mnemonic sentence. Use a good entropy source.--- You will get your coins stolen if you don't. You have been warned.-type Entropy = ByteString---- | Human-readable mnemonic sentence.-type Mnemonic = Text---- | Optional passphrase for mnemnoic sentence.-type Passphrase = ByteString---- | Seed for a private key from a mnemonic sentence.-type Seed = ByteString---- | Mnemonic key checksum.-type Checksum = ByteString---- | Paremeters for PBKDF2 function.-pbkdfParams :: Parameters-pbkdfParams = Parameters {iterCounts = 2048, outputLength = 64}---- | Provide intial 'Entropy' as a 'ByteString' of length multiple of 4 bytes.--- Output a 'Mnemonic' sentence.-toMnemonic :: Entropy -> Either String Mnemonic-toMnemonic ent = do-    when (BS.null ent) $-        Left "toMnemonic: entropy can not be empty"-    when (remainder /= 0) $-        Left "toMnemonic: entropy must be multiples of 4 bytes"-    when (cs_len > 16) $-        Left "toMnemonic: maximum entropy is 64 bytes (512 bits)"-    return ms-  where-    (cs_len, remainder) = BS.length ent `quotRem` 4-    c = calcCS cs_len ent-    indices = bsToIndices $ ent `BS.append` c-    ms = T.unwords $ map (wl!) indices---- | Revert 'toMnemonic'. Do not use this to generate a 'Seed'. Instead use--- 'mnemonicToSeed'. This outputs the original 'Entropy' used to generate a--- 'Mnemonic' sentence.-fromMnemonic :: Mnemonic -> Either String Entropy-fromMnemonic ms = do-    when (T.null ms) $-        Left "fromMnemonic: empty mnemonic"-    when (word_count > 48) $-        Left $ "fromMnemonic: too many words: " ++ show word_count-    when (word_count `mod` 3 /= 0) $-        Left $ "fromMnemonic: wrong number of words:" ++ show word_count-    ms_bs <- indicesToBS =<< getIndices ms_words-    let (ms_ent, ms_cs) = BS.splitAt (ent_len * 4) ms_bs-        ms_cs_num = numCS cs_len ms_cs-        ent_cs_num = numCS cs_len $ calcCS cs_len ms_ent-    when (ent_cs_num /= ms_cs_num) $-        Left $ "fromMnemonic: checksum failed: " ++ sh ent_cs_num ms_cs_num-    return ms_ent-  where-    ms_words = T.words ms-    word_count = length ms_words-    (ent_len, cs_len) = (word_count * 11) `quotRem` 32-    sh cs_a cs_b = show cs_a ++ " /= " ++ show cs_b---- | Compute 'Checksum'.-calcCS :: Int -> Entropy -> Checksum-calcCS len = getBits len . BA.convert . hashWith SHA256--numCS :: Int -> Entropy -> Integer-numCS len =-    shiftCS . bsToInteger-  where-    shiftCS = case 8 - len `mod` 8 of-        8 -> id-        x -> flip shiftR x---- | Turn an arbitrary sequence of characters into a 512-bit 'Seed'. Use--- 'mnemonicToSeed' to get a seed from a 'Mnemonic' sentence. Warning: Does not--- perform NFKD normalization.-anyToSeed :: Passphrase -> Mnemonic -> Seed-anyToSeed pf ms =-    fastPBKDF2_SHA512 pbkdfParams (E.encodeUtf8 ms) ("mnemonic" `mappend` pf)---- | Get a 512-bit 'Seed' from a 'Mnemonic' sentence. Will validate checksum.--- 'Passphrase' can be used to protect the 'Mnemonic'. Use an empty string as--- 'Passphrase' if none is required.-mnemonicToSeed :: Passphrase -> Mnemonic -> Either String Seed-mnemonicToSeed pf ms = do-    ent <- fromMnemonic ms-    mnm <- toMnemonic ent-    return $ anyToSeed pf mnm---- | Get indices of words in word list.-getIndices :: [Text] -> Either String [Int]-getIndices ws-    | null n = return $ catMaybes i-    | otherwise = Left $ "getIndices: words not found: " ++ cs w-  where-    i = map (`M.lookup` wl') ws-    n = elemIndices Nothing i-    w = T.unwords $ map (ws !!) n---- | Turn a list of 11-bit numbers into a 'ByteString'-indicesToBS :: [Int] -> Either String ByteString-indicesToBS is = do-    when lrg $ Left "indicesToBS: index larger or equal than 2048"-    return . pad . integerToBS $ foldl' f 0 is `shiftL` shift_width-  where-    lrg = isJust $ find (>= 2048) is-    (q, r) = (length is * 11) `quotRem` 8-    shift_width =-        if r == 0-            then 0-            else 8 - r-    bl =-        if r == 0-            then q-            else q + 1 -- length of resulting ByteString-    pad bs = BS.append (BS.replicate (bl - BS.length bs) 0x00) bs-    f acc x = (acc `shiftL` 11) + fromIntegral x---- | Turn a 'ByteString' into a list of 11-bit numbers.-bsToIndices :: ByteString -> [Int]-bsToIndices bs =-    reverse . go q $ bsToInteger bs `shiftR` r-  where-    (q, r) = (BS.length bs * 8) `quotRem` 11-    go 0 _ = []-    go n i = fromIntegral (i `mod` 2048) : go (n - 1) (i `shiftR` 11)--wl' :: M.Map Text Int-wl' = V.ifoldr' (\i w m -> M.insert w i m) M.empty wl---- | Standard English dictionary from BIP-39 specification.-wl :: Vector Text-wl = V.fromListN 2048-    [ "abandon", "ability", "able", "about", "above", "absent"-    , "absorb", "abstract", "absurd", "abuse", "access", "accident"-    , "account", "accuse", "achieve", "acid", "acoustic", "acquire"-    , "across", "act", "action", "actor", "actress", "actual"-    , "adapt", "add", "addict", "address", "adjust", "admit"-    , "adult", "advance", "advice", "aerobic", "affair", "afford"-    , "afraid", "again", "age", "agent", "agree", "ahead"-    , "aim", "air", "airport", "aisle", "alarm", "album"-    , "alcohol", "alert", "alien", "all", "alley", "allow"-    , "almost", "alone", "alpha", "already", "also", "alter"-    , "always", "amateur", "amazing", "among", "amount", "amused"-    , "analyst", "anchor", "ancient", "anger", "angle", "angry"-    , "animal", "ankle", "announce", "annual", "another", "answer"-    , "antenna", "antique", "anxiety", "any", "apart", "apology"-    , "appear", "apple", "approve", "april", "arch", "arctic"-    , "area", "arena", "argue", "arm", "armed", "armor"-    , "army", "around", "arrange", "arrest", "arrive", "arrow"-    , "art", "artefact", "artist", "artwork", "ask", "aspect"-    , "assault", "asset", "assist", "assume", "asthma", "athlete"-    , "atom", "attack", "attend", "attitude", "attract", "auction"-    , "audit", "august", "aunt", "author", "auto", "autumn"-    , "average", "avocado", "avoid", "awake", "aware", "away"-    , "awesome", "awful", "awkward", "axis", "baby", "bachelor"-    , "bacon", "badge", "bag", "balance", "balcony", "ball"-    , "bamboo", "banana", "banner", "bar", "barely", "bargain"-    , "barrel", "base", "basic", "basket", "battle", "beach"-    , "bean", "beauty", "because", "become", "beef", "before"-    , "begin", "behave", "behind", "believe", "below", "belt"-    , "bench", "benefit", "best", "betray", "better", "between"-    , "beyond", "bicycle", "bid", "bike", "bind", "biology"-    , "bird", "birth", "bitter", "black", "blade", "blame"-    , "blanket", "blast", "bleak", "bless", "blind", "blood"-    , "blossom", "blouse", "blue", "blur", "blush", "board"-    , "boat", "body", "boil", "bomb", "bone", "bonus"-    , "book", "boost", "border", "boring", "borrow", "boss"-    , "bottom", "bounce", "box", "boy", "bracket", "brain"-    , "brand", "brass", "brave", "bread", "breeze", "brick"-    , "bridge", "brief", "bright", "bring", "brisk", "broccoli"-    , "broken", "bronze", "broom", "brother", "brown", "brush"-    , "bubble", "buddy", "budget", "buffalo", "build", "bulb"-    , "bulk", "bullet", "bundle", "bunker", "burden", "burger"-    , "burst", "bus", "business", "busy", "butter", "buyer"-    , "buzz", "cabbage", "cabin", "cable", "cactus", "cage"-    , "cake", "call", "calm", "camera", "camp", "can"-    , "canal", "cancel", "candy", "cannon", "canoe", "canvas"-    , "canyon", "capable", "capital", "captain", "car", "carbon"-    , "card", "cargo", "carpet", "carry", "cart", "case"-    , "cash", "casino", "castle", "casual", "cat", "catalog"-    , "catch", "category", "cattle", "caught", "cause", "caution"-    , "cave", "ceiling", "celery", "cement", "census", "century"-    , "cereal", "certain", "chair", "chalk", "champion", "change"-    , "chaos", "chapter", "charge", "chase", "chat", "cheap"-    , "check", "cheese", "chef", "cherry", "chest", "chicken"-    , "chief", "child", "chimney", "choice", "choose", "chronic"-    , "chuckle", "chunk", "churn", "cigar", "cinnamon", "circle"-    , "citizen", "city", "civil", "claim", "clap", "clarify"-    , "claw", "clay", "clean", "clerk", "clever", "click"-    , "client", "cliff", "climb", "clinic", "clip", "clock"-    , "clog", "close", "cloth", "cloud", "clown", "club"-    , "clump", "cluster", "clutch", "coach", "coast", "coconut"-    , "code", "coffee", "coil", "coin", "collect", "color"-    , "column", "combine", "come", "comfort", "comic", "common"-    , "company", "concert", "conduct", "confirm", "congress", "connect"-    , "consider", "control", "convince", "cook", "cool", "copper"-    , "copy", "coral", "core", "corn", "correct", "cost"-    , "cotton", "couch", "country", "couple", "course", "cousin"-    , "cover", "coyote", "crack", "cradle", "craft", "cram"-    , "crane", "crash", "crater", "crawl", "crazy", "cream"-    , "credit", "creek", "crew", "cricket", "crime", "crisp"-    , "critic", "crop", "cross", "crouch", "crowd", "crucial"-    , "cruel", "cruise", "crumble", "crunch", "crush", "cry"-    , "crystal", "cube", "culture", "cup", "cupboard", "curious"-    , "current", "curtain", "curve", "cushion", "custom", "cute"-    , "cycle", "dad", "damage", "damp", "dance", "danger"-    , "daring", "dash", "daughter", "dawn", "day", "deal"-    , "debate", "debris", "decade", "december", "decide", "decline"-    , "decorate", "decrease", "deer", "defense", "define", "defy"-    , "degree", "delay", "deliver", "demand", "demise", "denial"-    , "dentist", "deny", "depart", "depend", "deposit", "depth"-    , "deputy", "derive", "describe", "desert", "design", "desk"-    , "despair", "destroy", "detail", "detect", "develop", "device"-    , "devote", "diagram", "dial", "diamond", "diary", "dice"-    , "diesel", "diet", "differ", "digital", "dignity", "dilemma"-    , "dinner", "dinosaur", "direct", "dirt", "disagree", "discover"-    , "disease", "dish", "dismiss", "disorder", "display", "distance"-    , "divert", "divide", "divorce", "dizzy", "doctor", "document"-    , "dog", "doll", "dolphin", "domain", "donate", "donkey"-    , "donor", "door", "dose", "double", "dove", "draft"-    , "dragon", "drama", "drastic", "draw", "dream", "dress"-    , "drift", "drill", "drink", "drip", "drive", "drop"-    , "drum", "dry", "duck", "dumb", "dune", "during"-    , "dust", "dutch", "duty", "dwarf", "dynamic", "eager"-    , "eagle", "early", "earn", "earth", "easily", "east"-    , "easy", "echo", "ecology", "economy", "edge", "edit"-    , "educate", "effort", "egg", "eight", "either", "elbow"-    , "elder", "electric", "elegant", "element", "elephant", "elevator"-    , "elite", "else", "embark", "embody", "embrace", "emerge"-    , "emotion", "employ", "empower", "empty", "enable", "enact"-    , "end", "endless", "endorse", "enemy", "energy", "enforce"-    , "engage", "engine", "enhance", "enjoy", "enlist", "enough"-    , "enrich", "enroll", "ensure", "enter", "entire", "entry"-    , "envelope", "episode", "equal", "equip", "era", "erase"-    , "erode", "erosion", "error", "erupt", "escape", "essay"-    , "essence", "estate", "eternal", "ethics", "evidence", "evil"-    , "evoke", "evolve", "exact", "example", "excess", "exchange"-    , "excite", "exclude", "excuse", "execute", "exercise", "exhaust"-    , "exhibit", "exile", "exist", "exit", "exotic", "expand"-    , "expect", "expire", "explain", "expose", "express", "extend"-    , "extra", "eye", "eyebrow", "fabric", "face", "faculty"-    , "fade", "faint", "faith", "fall", "false", "fame"-    , "family", "famous", "fan", "fancy", "fantasy", "farm"-    , "fashion", "fat", "fatal", "father", "fatigue", "fault"-    , "favorite", "feature", "february", "federal", "fee", "feed"-    , "feel", "female", "fence", "festival", "fetch", "fever"-    , "few", "fiber", "fiction", "field", "figure", "file"-    , "film", "filter", "final", "find", "fine", "finger"-    , "finish", "fire", "firm", "first", "fiscal", "fish"-    , "fit", "fitness", "fix", "flag", "flame", "flash"-    , "flat", "flavor", "flee", "flight", "flip", "float"-    , "flock", "floor", "flower", "fluid", "flush", "fly"-    , "foam", "focus", "fog", "foil", "fold", "follow"-    , "food", "foot", "force", "forest", "forget", "fork"-    , "fortune", "forum", "forward", "fossil", "foster", "found"-    , "fox", "fragile", "frame", "frequent", "fresh", "friend"-    , "fringe", "frog", "front", "frost", "frown", "frozen"-    , "fruit", "fuel", "fun", "funny", "furnace", "fury"-    , "future", "gadget", "gain", "galaxy", "gallery", "game"-    , "gap", "garage", "garbage", "garden", "garlic", "garment"-    , "gas", "gasp", "gate", "gather", "gauge", "gaze"-    , "general", "genius", "genre", "gentle", "genuine", "gesture"-    , "ghost", "giant", "gift", "giggle", "ginger", "giraffe"-    , "girl", "give", "glad", "glance", "glare", "glass"-    , "glide", "glimpse", "globe", "gloom", "glory", "glove"-    , "glow", "glue", "goat", "goddess", "gold", "good"-    , "goose", "gorilla", "gospel", "gossip", "govern", "gown"-    , "grab", "grace", "grain", "grant", "grape", "grass"-    , "gravity", "great", "green", "grid", "grief", "grit"-    , "grocery", "group", "grow", "grunt", "guard", "guess"-    , "guide", "guilt", "guitar", "gun", "gym", "habit"-    , "hair", "half", "hammer", "hamster", "hand", "happy"-    , "harbor", "hard", "harsh", "harvest", "hat", "have"-    , "hawk", "hazard", "head", "health", "heart", "heavy"-    , "hedgehog", "height", "hello", "helmet", "help", "hen"-    , "hero", "hidden", "high", "hill", "hint", "hip"-    , "hire", "history", "hobby", "hockey", "hold", "hole"-    , "holiday", "hollow", "home", "honey", "hood", "hope"-    , "horn", "horror", "horse", "hospital", "host", "hotel"-    , "hour", "hover", "hub", "huge", "human", "humble"-    , "humor", "hundred", "hungry", "hunt", "hurdle", "hurry"-    , "hurt", "husband", "hybrid", "ice", "icon", "idea"-    , "identify", "idle", "ignore", "ill", "illegal", "illness"-    , "image", "imitate", "immense", "immune", "impact", "impose"-    , "improve", "impulse", "inch", "include", "income", "increase"-    , "index", "indicate", "indoor", "industry", "infant", "inflict"-    , "inform", "inhale", "inherit", "initial", "inject", "injury"-    , "inmate", "inner", "innocent", "input", "inquiry", "insane"-    , "insect", "inside", "inspire", "install", "intact", "interest"-    , "into", "invest", "invite", "involve", "iron", "island"-    , "isolate", "issue", "item", "ivory", "jacket", "jaguar"-    , "jar", "jazz", "jealous", "jeans", "jelly", "jewel"-    , "job", "join", "joke", "journey", "joy", "judge"-    , "juice", "jump", "jungle", "junior", "junk", "just"-    , "kangaroo", "keen", "keep", "ketchup", "key", "kick"-    , "kid", "kidney", "kind", "kingdom", "kiss", "kit"-    , "kitchen", "kite", "kitten", "kiwi", "knee", "knife"-    , "knock", "know", "lab", "label", "labor", "ladder"-    , "lady", "lake", "lamp", "language", "laptop", "large"-    , "later", "latin", "laugh", "laundry", "lava", "law"-    , "lawn", "lawsuit", "layer", "lazy", "leader", "leaf"-    , "learn", "leave", "lecture", "left", "leg", "legal"-    , "legend", "leisure", "lemon", "lend", "length", "lens"-    , "leopard", "lesson", "letter", "level", "liar", "liberty"-    , "library", "license", "life", "lift", "light", "like"-    , "limb", "limit", "link", "lion", "liquid", "list"-    , "little", "live", "lizard", "load", "loan", "lobster"-    , "local", "lock", "logic", "lonely", "long", "loop"-    , "lottery", "loud", "lounge", "love", "loyal", "lucky"-    , "luggage", "lumber", "lunar", "lunch", "luxury", "lyrics"-    , "machine", "mad", "magic", "magnet", "maid", "mail"-    , "main", "major", "make", "mammal", "man", "manage"-    , "mandate", "mango", "mansion", "manual", "maple", "marble"-    , "march", "margin", "marine", "market", "marriage", "mask"-    , "mass", "master", "match", "material", "math", "matrix"-    , "matter", "maximum", "maze", "meadow", "mean", "measure"-    , "meat", "mechanic", "medal", "media", "melody", "melt"-    , "member", "memory", "mention", "menu", "mercy", "merge"-    , "merit", "merry", "mesh", "message", "metal", "method"-    , "middle", "midnight", "milk", "million", "mimic", "mind"-    , "minimum", "minor", "minute", "miracle", "mirror", "misery"-    , "miss", "mistake", "mix", "mixed", "mixture", "mobile"-    , "model", "modify", "mom", "moment", "monitor", "monkey"-    , "monster", "month", "moon", "moral", "more", "morning"-    , "mosquito", "mother", "motion", "motor", "mountain", "mouse"-    , "move", "movie", "much", "muffin", "mule", "multiply"-    , "muscle", "museum", "mushroom", "music", "must", "mutual"-    , "myself", "mystery", "myth", "naive", "name", "napkin"-    , "narrow", "nasty", "nation", "nature", "near", "neck"-    , "need", "negative", "neglect", "neither", "nephew", "nerve"-    , "nest", "net", "network", "neutral", "never", "news"-    , "next", "nice", "night", "noble", "noise", "nominee"-    , "noodle", "normal", "north", "nose", "notable", "note"-    , "nothing", "notice", "novel", "now", "nuclear", "number"-    , "nurse", "nut", "oak", "obey", "object", "oblige"-    , "obscure", "observe", "obtain", "obvious", "occur", "ocean"-    , "october", "odor", "off", "offer", "office", "often"-    , "oil", "okay", "old", "olive", "olympic", "omit"-    , "once", "one", "onion", "online", "only", "open"-    , "opera", "opinion", "oppose", "option", "orange", "orbit"-    , "orchard", "order", "ordinary", "organ", "orient", "original"-    , "orphan", "ostrich", "other", "outdoor", "outer", "output"-    , "outside", "oval", "oven", "over", "own", "owner"-    , "oxygen", "oyster", "ozone", "pact", "paddle", "page"-    , "pair", "palace", "palm", "panda", "panel", "panic"-    , "panther", "paper", "parade", "parent", "park", "parrot"-    , "party", "pass", "patch", "path", "patient", "patrol"-    , "pattern", "pause", "pave", "payment", "peace", "peanut"-    , "pear", "peasant", "pelican", "pen", "penalty", "pencil"-    , "people", "pepper", "perfect", "permit", "person", "pet"-    , "phone", "photo", "phrase", "physical", "piano", "picnic"-    , "picture", "piece", "pig", "pigeon", "pill", "pilot"-    , "pink", "pioneer", "pipe", "pistol", "pitch", "pizza"-    , "place", "planet", "plastic", "plate", "play", "please"-    , "pledge", "pluck", "plug", "plunge", "poem", "poet"-    , "point", "polar", "pole", "police", "pond", "pony"-    , "pool", "popular", "portion", "position", "possible", "post"-    , "potato", "pottery", "poverty", "powder", "power", "practice"-    , "praise", "predict", "prefer", "prepare", "present", "pretty"-    , "prevent", "price", "pride", "primary", "print", "priority"-    , "prison", "private", "prize", "problem", "process", "produce"-    , "profit", "program", "project", "promote", "proof", "property"-    , "prosper", "protect", "proud", "provide", "public", "pudding"-    , "pull", "pulp", "pulse", "pumpkin", "punch", "pupil"-    , "puppy", "purchase", "purity", "purpose", "purse", "push"-    , "put", "puzzle", "pyramid", "quality", "quantum", "quarter"-    , "question", "quick", "quit", "quiz", "quote", "rabbit"-    , "raccoon", "race", "rack", "radar", "radio", "rail"-    , "rain", "raise", "rally", "ramp", "ranch", "random"-    , "range", "rapid", "rare", "rate", "rather", "raven"-    , "raw", "razor", "ready", "real", "reason", "rebel"-    , "rebuild", "recall", "receive", "recipe", "record", "recycle"-    , "reduce", "reflect", "reform", "refuse", "region", "regret"-    , "regular", "reject", "relax", "release", "relief", "rely"-    , "remain", "remember", "remind", "remove", "render", "renew"-    , "rent", "reopen", "repair", "repeat", "replace", "report"-    , "require", "rescue", "resemble", "resist", "resource", "response"-    , "result", "retire", "retreat", "return", "reunion", "reveal"-    , "review", "reward", "rhythm", "rib", "ribbon", "rice"-    , "rich", "ride", "ridge", "rifle", "right", "rigid"-    , "ring", "riot", "ripple", "risk", "ritual", "rival"-    , "river", "road", "roast", "robot", "robust", "rocket"-    , "romance", "roof", "rookie", "room", "rose", "rotate"-    , "rough", "round", "route", "royal", "rubber", "rude"-    , "rug", "rule", "run", "runway", "rural", "sad"-    , "saddle", "sadness", "safe", "sail", "salad", "salmon"-    , "salon", "salt", "salute", "same", "sample", "sand"-    , "satisfy", "satoshi", "sauce", "sausage", "save", "say"-    , "scale", "scan", "scare", "scatter", "scene", "scheme"-    , "school", "science", "scissors", "scorpion", "scout", "scrap"-    , "screen", "script", "scrub", "sea", "search", "season"-    , "seat", "second", "secret", "section", "security", "seed"-    , "seek", "segment", "select", "sell", "seminar", "senior"-    , "sense", "sentence", "series", "service", "session", "settle"-    , "setup", "seven", "shadow", "shaft", "shallow", "share"-    , "shed", "shell", "sheriff", "shield", "shift", "shine"-    , "ship", "shiver", "shock", "shoe", "shoot", "shop"-    , "short", "shoulder", "shove", "shrimp", "shrug", "shuffle"-    , "shy", "sibling", "sick", "side", "siege", "sight"-    , "sign", "silent", "silk", "silly", "silver", "similar"-    , "simple", "since", "sing", "siren", "sister", "situate"-    , "six", "size", "skate", "sketch", "ski", "skill"-    , "skin", "skirt", "skull", "slab", "slam", "sleep"-    , "slender", "slice", "slide", "slight", "slim", "slogan"-    , "slot", "slow", "slush", "small", "smart", "smile"-    , "smoke", "smooth", "snack", "snake", "snap", "sniff"-    , "snow", "soap", "soccer", "social", "sock", "soda"-    , "soft", "solar", "soldier", "solid", "solution", "solve"-    , "someone", "song", "soon", "sorry", "sort", "soul"-    , "sound", "soup", "source", "south", "space", "spare"-    , "spatial", "spawn", "speak", "special", "speed", "spell"-    , "spend", "sphere", "spice", "spider", "spike", "spin"-    , "spirit", "split", "spoil", "sponsor", "spoon", "sport"-    , "spot", "spray", "spread", "spring", "spy", "square"-    , "squeeze", "squirrel", "stable", "stadium", "staff", "stage"-    , "stairs", "stamp", "stand", "start", "state", "stay"-    , "steak", "steel", "stem", "step", "stereo", "stick"-    , "still", "sting", "stock", "stomach", "stone", "stool"-    , "story", "stove", "strategy", "street", "strike", "strong"-    , "struggle", "student", "stuff", "stumble", "style", "subject"-    , "submit", "subway", "success", "such", "sudden", "suffer"-    , "sugar", "suggest", "suit", "summer", "sun", "sunny"-    , "sunset", "super", "supply", "supreme", "sure", "surface"-    , "surge", "surprise", "surround", "survey", "suspect", "sustain"-    , "swallow", "swamp", "swap", "swarm", "swear", "sweet"-    , "swift", "swim", "swing", "switch", "sword", "symbol"-    , "symptom", "syrup", "system", "table", "tackle", "tag"-    , "tail", "talent", "talk", "tank", "tape", "target"-    , "task", "taste", "tattoo", "taxi", "teach", "team"-    , "tell", "ten", "tenant", "tennis", "tent", "term"-    , "test", "text", "thank", "that", "theme", "then"-    , "theory", "there", "they", "thing", "this", "thought"-    , "three", "thrive", "throw", "thumb", "thunder", "ticket"-    , "tide", "tiger", "tilt", "timber", "time", "tiny"-    , "tip", "tired", "tissue", "title", "toast", "tobacco"-    , "today", "toddler", "toe", "together", "toilet", "token"-    , "tomato", "tomorrow", "tone", "tongue", "tonight", "tool"-    , "tooth", "top", "topic", "topple", "torch", "tornado"-    , "tortoise", "toss", "total", "tourist", "toward", "tower"-    , "town", "toy", "track", "trade", "traffic", "tragic"-    , "train", "transfer", "trap", "trash", "travel", "tray"-    , "treat", "tree", "trend", "trial", "tribe", "trick"-    , "trigger", "trim", "trip", "trophy", "trouble", "truck"-    , "true", "truly", "trumpet", "trust", "truth", "try"-    , "tube", "tuition", "tumble", "tuna", "tunnel", "turkey"-    , "turn", "turtle", "twelve", "twenty", "twice", "twin"-    , "twist", "two", "type", "typical", "ugly", "umbrella"-    , "unable", "unaware", "uncle", "uncover", "under", "undo"-    , "unfair", "unfold", "unhappy", "uniform", "unique", "unit"-    , "universe", "unknown", "unlock", "until", "unusual", "unveil"-    , "update", "upgrade", "uphold", "upon", "upper", "upset"-    , "urban", "urge", "usage", "use", "used", "useful"-    , "useless", "usual", "utility", "vacant", "vacuum", "vague"-    , "valid", "valley", "valve", "van", "vanish", "vapor"-    , "various", "vast", "vault", "vehicle", "velvet", "vendor"-    , "venture", "venue", "verb", "verify", "version", "very"-    , "vessel", "veteran", "viable", "vibrant", "vicious", "victory"-    , "video", "view", "village", "vintage", "violin", "virtual"-    , "virus", "visa", "visit", "visual", "vital", "vivid"-    , "vocal", "voice", "void", "volcano", "volume", "vote"-    , "voyage", "wage", "wagon", "wait", "walk", "wall"-    , "walnut", "want", "warfare", "warm", "warrior", "wash"-    , "wasp", "waste", "water", "wave", "way", "wealth"-    , "weapon", "wear", "weasel", "weather", "web", "wedding"-    , "weekend", "weird", "welcome", "west", "wet", "whale"-    , "what", "wheat", "wheel", "when", "where", "whip"-    , "whisper", "wide", "width", "wife", "wild", "will"-    , "win", "window", "wine", "wing", "wink", "winner"-    , "winter", "wire", "wisdom", "wise", "wish", "witness"-    , "wolf", "woman", "wonder", "wood", "wool", "word"-    , "work", "world", "worry", "worth", "wrap", "wreck"-    , "wrestle", "wrist", "write", "wrong", "yard", "year"-    , "yellow", "you", "young", "youth", "zebra", "zero"-    , "zone", "zoo"-    ]
− src/Network/Haskoin/Network.hs
@@ -1,21 +0,0 @@-{-|-Module      : Network.Haskoin.Network-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--This module provides basic types used for the Bitcoin networking protocol-together with 'Data.Serialize' instances for efficiently serializing and-de-serializing them.--}-module Network.Haskoin.Network-    ( module Common-    , module Message-    , module Bloom-    ) where--import           Network.Haskoin.Network.Bloom   as Bloom-import           Network.Haskoin.Network.Common  as Common-import           Network.Haskoin.Network.Message as Message
− src/Network/Haskoin/Network/Bloom.hs
@@ -1,263 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric  #-}-{-|-Module      : Network.Haskoin.Network.Bloom-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Bloom filters are used to reduce data transfer when synchronizing thin cients.-When bloom filters are used a client will obtain filtered blocks that only-contain transactions that pass the bloom filter. Transactions announced via inv-messages also pass the filter.--}-module Network.Haskoin.Network.Bloom-    ( -- * Bloom Filters-      BloomFlags(..)-    , BloomFilter(..)-    , FilterLoad(..)-    , FilterAdd(..)-    , bloomCreate-    , bloomInsert-    , bloomContains-    , isBloomValid-    , isBloomEmpty-    , isBloomFull-    , acceptsFilters-    , bloomRelevantUpdate-    ) where--import           Control.DeepSeq-import           Control.Monad                      (forM_, replicateM)-import           Data.Bits-import           Data.ByteString                    (ByteString)-import qualified Data.ByteString                    as BS-import qualified Data.Foldable                      as F-import           Data.Hash.Murmur                   (murmur3)-import           Data.List                          (foldl')-import qualified Data.Sequence                      as S-import           Data.Serialize                     (Serialize, encode, get,-                                                     put)-import           Data.Serialize.Get                 (getByteString, getWord32le,-                                                     getWord8)-import           Data.Serialize.Put                 (putByteString, putWord32le,-                                                     putWord8)-import           Data.Word-import           GHC.Generics                       (Generic)-import           Network.Haskoin.Network.Common-import           Network.Haskoin.Script.Common-import           Network.Haskoin.Transaction.Common--- | 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001%-maxBloomSize :: Int-maxBloomSize = 36000--maxHashFuncs :: Word32-maxHashFuncs = 50--ln2Squared :: Double-ln2Squared = 0.4804530139182014246671025263266649717305529515945455--ln2 :: Double-ln2 = 0.6931471805599453094172321214581765680755001343602552--bitMask :: [Word8]-bitMask = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]---- | The bloom flags are used to tell the remote peer how to auto-update--- the provided bloom filter.-data BloomFlags-    = BloomUpdateNone -- ^ never update-    | BloomUpdateAll -- ^ auto-update on all outputs-    | BloomUpdateP2PubKeyOnly-    -- ^ auto-update on pay-to-pubkey or pay-to-multisig (default)-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize BloomFlags where-    get = go =<< getWord8-      where-        go 0 = return BloomUpdateNone-        go 1 = return BloomUpdateAll-        go 2 = return BloomUpdateP2PubKeyOnly-        go _ = fail "BloomFlags get: Invalid bloom flag"--    put f = putWord8 $ case f of-        BloomUpdateNone         -> 0-        BloomUpdateAll          -> 1-        BloomUpdateP2PubKeyOnly -> 2---- | A bloom filter is a probabilistic data structure that SPV clients send to--- other peers to filter the set of transactions received from them. Bloom--- filters can have false positives but not false negatives. Some transactions--- that pass the filter may not be relevant to the receiving peer. By--- controlling the false positive rate, SPV nodes can trade off bandwidth--- versus privacy.-data BloomFilter = BloomFilter-    { bloomData      :: !(S.Seq Word8)-    -- ^ bloom filter data-    , bloomHashFuncs :: !Word32-    -- ^ number of hash functions for this filter-    , bloomTweak     :: !Word32-    -- ^ hash function random nonce-    , bloomFlags     :: !BloomFlags-    -- ^ bloom filter auto-update flags-    }-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize BloomFilter where--    get = BloomFilter <$> (S.fromList <$> (readDat =<< get))-                      <*> getWord32le <*> getWord32le-                      <*> get-      where-        readDat (VarInt len) = replicateM (fromIntegral len) getWord8--    put (BloomFilter dat hashFuncs tweak flags) = do-        putVarInt $ S.length dat-        forM_ (F.toList dat) putWord8-        putWord32le hashFuncs-        putWord32le tweak-        put flags---- | Set a new bloom filter on the peer connection.-newtype FilterLoad = FilterLoad { filterLoadBloomFilter :: BloomFilter }-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize FilterLoad where-    get = FilterLoad <$> get-    put (FilterLoad f) = put f---- | Add the given data element to the connections current filter without--- requiring a completely new one to be set.-newtype FilterAdd = FilterAdd { getFilterData :: ByteString }-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize FilterAdd where-    get = do-        (VarInt len) <- get-        dat <- getByteString $ fromIntegral len-        return $ FilterAdd dat--    put (FilterAdd bs) = do-        putVarInt $ BS.length bs-        putByteString bs----- | Build a bloom filter that will provide the given false positive rate when--- the given number of elements have been inserted.-bloomCreate :: Int          -- ^ number of elements-            -> Double       -- ^ false positive rate-            -> Word32       -- ^ random nonce (tweak) for the hash function-            -> BloomFlags   -- ^ bloom filter flags-            -> BloomFilter  -- ^ bloom filter-bloomCreate numElem fpRate =-    BloomFilter (S.replicate bloomSize 0) numHashF-  where-    -- Bloom filter size in bytes-    bloomSize = truncate $ min a b / 8-    -- Suggested size in bits-    a         = -1 / ln2Squared * fromIntegral numElem * log fpRate-    -- Maximum size in bits-    b         = fromIntegral $ maxBloomSize * 8-    numHashF  = truncate $ min c (fromIntegral maxHashFuncs)-    -- Suggested number of hash functions-    c         = fromIntegral bloomSize * 8 / fromIntegral numElem * ln2--bloomHash :: BloomFilter -> Word32 -> ByteString -> Word32-bloomHash bfilter hashNum bs =-    murmur3 seed bs `mod` (fromIntegral (S.length (bloomData bfilter)) * 8)-  where-    seed = hashNum * 0xfba4c795 + bloomTweak bfilter---- | Insert arbitrary data into a bloom filter. Returns the new bloom filter--- containing the new data.-bloomInsert :: BloomFilter    -- ^ Original bloom filter-            -> ByteString     -- ^ New data to insert-            -> BloomFilter    -- ^ Bloom filter containing the new data-bloomInsert bfilter bs-    | isBloomFull bfilter = bfilter-    | otherwise = bfilter { bloomData = newData }-  where-    idxs    = map (\i -> bloomHash bfilter i bs) [0..bloomHashFuncs bfilter - 1]-    upd s i = S.adjust (.|. bitMask !! fromIntegral (7 .&. i))-                       (fromIntegral $ i `shiftR` 3) s-    newData = foldl upd (bloomData bfilter) idxs---- | Tests if some arbitrary data matches the filter. This can be either because--- the data was inserted into the filter or because it is a false positive.-bloomContains :: BloomFilter    -- ^ Bloom filter-              -> ByteString-              -- ^ Data that will be checked against the given bloom filter-              -> Bool-              -- ^ Returns True if the data matches the filter-bloomContains bfilter bs-    | isBloomFull bfilter  = True-    | isBloomEmpty bfilter = False-    | otherwise            = all isSet idxs-  where-    s       = bloomData bfilter-    idxs    = map (\i -> bloomHash bfilter i bs) [0..bloomHashFuncs bfilter - 1]-    isSet i = S.index s (fromIntegral $ i `shiftR` 3)-          .&. (bitMask !! fromIntegral (7 .&. i)) /= 0---- | Checks if any of the outputs of a tx is in the current bloom filter.--- If it is, add the txid and vout as an outpoint (i.e. so that--- a future tx that spends the output won't be missed).-bloomRelevantUpdate :: BloomFilter-                    -- ^ Bloom filter-                    -> Tx-                    -- ^ Tx that may (or may not) have relevant outputs-                    -> Maybe BloomFilter-                    -- ^ Returns an updated bloom filter adding relevant output-bloomRelevantUpdate bfilter tx-    | isBloomFull bfilter || isBloomEmpty bfilter = Nothing-    | bloomFlags bfilter == BloomUpdateNone = Nothing-    | length matchOuts > 0 = Just $ foldl' (addRelevant) bfilter matchOuts-    | otherwise = Nothing-    where-        -- TxHash if we end up inserting an outpoint-        h = txHash tx-        -- Decode the scriptOutpus and add vOuts in case we make them outpoints-        decodedOutputScripts = traverse (decodeOutputBS . scriptOutput) $ txOut tx-        err = error $ "Error Decoding output script"-        idxOutputScripts = either (const err) (zip [0..]) decodedOutputScripts-        -- Check if any txOuts were contained in the bloom filter-        matchFilter = filter (\(_,op) -> bloomContains bfilter $ encodeScriptOut op)-        matchOuts = matchFilter idxOutputScripts--        addRelevant :: BloomFilter -> (Word32,ScriptOutput) -> BloomFilter-        addRelevant bfilter (id,scriptOut) = case (bloomFlags bfilter,scriptType) of-            -- We filtered out BloomUpdateNone so we insert any PayPk or PayMulSig-            (_, True) -> bloomInsert bfilter outpoint-            (BloomUpdateAll, _ ) -> bloomInsert bfilter outpoint-            _ ->  error "Error Updating Bloom Filter with relevant outpoint"-            where-                outpoint = encode $ OutPoint{ outPointHash = h, outPointIndex = id}-                scriptType = (\s -> isPayPK s ||isPayMulSig s) scriptOut--        -- Encodes a scriptOutput so it can be checked agains the Bloom Filter-        encodeScriptOut :: ScriptOutput -> ByteString-        encodeScriptOut (PayMulSig outputMuSig _) = encode outputMuSig-        encodeScriptOut (PayWitnessScriptHash scriptHash) = encode scriptHash-        encodeScriptOut (DataCarrier getOutputData) = encode getOutputData-        encodeScriptOut outputHash = (encode . getOutputHash) outputHash---- | Returns True if the filter is empty (all bytes set to 0x00)-isBloomEmpty :: BloomFilter -> Bool-isBloomEmpty bfilter = all (== 0x00) $ F.toList $ bloomData bfilter---- | Returns True if the filter is full (all bytes set to 0xff)-isBloomFull :: BloomFilter -> Bool-isBloomFull bfilter = all (== 0xff) $ F.toList $ bloomData bfilter---- | Tests if a given bloom filter is valid.-isBloomValid :: BloomFilter -- ^ Bloom filter to test-             -> Bool        -- ^ True if the given filter is valid-isBloomValid bfilter =-    S.length (bloomData bfilter) <= maxBloomSize &&-    bloomHashFuncs bfilter <= maxHashFuncs---- | Does the peer with these version services accept bloom filters?-acceptsFilters :: Word64 -> Bool-acceptsFilters srv = srv .&. (1 `shiftL` 2) /= 0
− src/Network/Haskoin/Network/Common.hs
@@ -1,613 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE LambdaCase        #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Network.Common-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Common functions and data types related to peer-to-peer network.--}-module Network.Haskoin.Network.Common-    ( -- * Network Data Types-      Addr(..)-    , NetworkAddressTime-    , Alert(..)-    , GetData(..)-    , Inv(..)-    , InvVector(..)-    , InvType(..)-    , HostAddress-    , hostToSockAddr-    , sockToHostAddress-    , NetworkAddress(..)-    , NotFound(..)-    , Ping(..)-    , Pong(..)-    , Reject(..)-    , RejectCode(..)-    , VarInt(..)-    , VarString(..)-    , Version(..)-    , MessageCommand(..)-      -- ** Useful Functions-    , reject-    , nodeNone-    , nodeNetwork-    , nodeGetUTXO-    , nodeBloom-    , nodeWitness-    , nodeXThin-    , commandToString-    , stringToCommand-    , putVarInt-    ) where--import           Control.DeepSeq-import           Control.Monad               (forM_, liftM2, replicateM, unless)-import           Data.Bits                   (shiftL)-import           Data.ByteString             (ByteString)-import qualified Data.ByteString             as B-import           Data.ByteString.Char8       as C (replicate)-import           Data.Serialize              as S-import           Data.String-import           Data.String.Conversions     (cs)-import           Data.Word                   (Word32, Word64)-import           GHC.Generics                (Generic)-import           Network.Haskoin.Crypto.Hash-import           Network.Socket              (SockAddr (..))-import           Text.Read                   as R---- | Network address with a timestamp.-type NetworkAddressTime = (Word32, NetworkAddress)---- | Provides information about known nodes in the bitcoin network. An 'Addr'--- type is sent inside a 'Message' as a response to a 'GetAddr' message.-newtype Addr =-    Addr { -- List of addresses of other nodes on the network with timestamps.-           addrList :: [NetworkAddressTime]-         }-    deriving (Eq, Show, Generic, NFData)--instance Serialize Addr where--    get = Addr <$> (repList =<< S.get)-      where-        repList (VarInt c) = replicateM (fromIntegral c) action-        action             = liftM2 (,) getWord32le S.get--    put (Addr xs) = do-        putVarInt $ length xs-        forM_ xs $ \(a,b) -> putWord32le a >> put b---- | Data type describing signed messages that can be sent between bitcoin--- nodes to display important notifications to end users about the health of--- the network.-data Alert =-    Alert {-          -- | Alert payload.-            alertPayload   :: !VarString-          -- | ECDSA signature of the payload-          , alertSignature :: !VarString-          } deriving (Eq, Show, Read, Generic, NFData)--instance Serialize Alert where-    get = Alert <$> S.get <*> S.get-    put (Alert p s) = put p >> put s---- | The 'GetData' type is used to retrieve information on a specific object--- ('Block' or 'Tx') identified by the objects hash. The payload of a 'GetData'--- request is a list of 'InvVector' which represent all the hashes of objects--- that a node wants. The response to a 'GetBlock' message will be either a--- 'Block' or a 'Tx' message depending on the type of the object referenced by--- the hash. Usually, 'GetData' messages are sent after a node receives an 'Inv'--- message that contains unknown object hashes.-newtype GetData =-    GetData { -- | list of object hashes-              getDataList :: [InvVector]-            } deriving (Eq, Show, Generic, NFData)--instance Serialize GetData where--    get = GetData <$> (repList =<< S.get)-      where-        repList (VarInt c) = replicateM (fromIntegral c) S.get--    put (GetData xs) = do-        putVarInt $ length xs-        forM_ xs put---- | 'Inv' messages are used by nodes to advertise their knowledge of new--- objects by publishing a list of hashes to a peer. 'Inv' messages can be sent--- unsolicited or in response to a 'GetBlocks' message.-newtype Inv =-    Inv {-        -- | inventory-          invList :: [InvVector]-        } deriving (Eq, Show, Generic, NFData)--instance Serialize Inv where--    get = Inv <$> (repList =<< S.get)-      where-        repList (VarInt c) = replicateM (fromIntegral c) S.get--    put (Inv xs) = do-        putVarInt $ length xs-        forM_ xs put---- | Data type identifying the type of an inventory vector. SegWit types are--- only used in 'GetData' messages, not 'Inv'.-data InvType-    = InvError -- ^ error-    | InvTx -- ^ transaction-    | InvBlock -- ^ block-    | InvMerkleBlock -- ^ filtered block-    | InvWitnessTx -- ^ segwit transaction-    | InvWitnessBlock -- ^ segwit block-    | InvWitnessMerkleBlock -- ^ segwit filtere block-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize InvType where-    get = go =<< getWord32le-      where-        go x =-            case x of-                0 -> return InvError-                1 -> return InvTx-                2 -> return InvBlock-                3 -> return InvMerkleBlock-                _-                    | x == 1 `shiftL` 30 + 1 -> return InvWitnessTx-                    | x == 1 `shiftL` 30 + 2 -> return InvWitnessBlock-                    | x == 1 `shiftL` 30 + 3 -> return InvWitnessMerkleBlock-                    | otherwise -> fail "bitcoinGet InvType: Invalid Type"-    put x =-        putWord32le $-        case x of-            InvError              -> 0-            InvTx                 -> 1-            InvBlock              -> 2-            InvMerkleBlock        -> 3-            InvWitnessTx          -> 1 `shiftL` 30 + 1-            InvWitnessBlock       -> 1 `shiftL` 30 + 2-            InvWitnessMerkleBlock -> 1 `shiftL` 30 + 3---- | Invectory vectors represent hashes identifying objects such as a 'Block' or--- a 'Tx'. They notify other peers about new data or data they have otherwise--- requested.-data InvVector =-    InvVector {-                -- | type of object-                invType :: !InvType-                -- | 256-bit hash of object-              , invHash :: !Hash256-              } deriving (Eq, Show, Generic, NFData)--instance Serialize InvVector where-    get = InvVector <$> S.get <*> S.get-    put (InvVector t h) = put t >> put h--newtype HostAddress =-    HostAddress ByteString-    deriving (Eq, Show, Ord, Generic, NFData)--instance Serialize HostAddress where-    put (HostAddress bs) = putByteString bs-    get = HostAddress <$> getByteString 18---- | Data type describing a bitcoin network address. Addresses are stored in--- IPv6 format. IPv4 addresses are mapped to IPv6 using IPv4 mapped IPv6--- addresses: <http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses>.-data NetworkAddress =-    NetworkAddress { -- | bitmask of services available for this address-                     naServices :: !Word64-                     -- | address and port information-                   , naAddress  :: !HostAddress-                   } deriving (Eq, Show, Generic, NFData)--hostToSockAddr :: HostAddress -> SockAddr-hostToSockAddr (HostAddress bs) =-    case runGet getSockAddr bs of-        Left e  -> error e-        Right x -> x--sockToHostAddress :: SockAddr -> HostAddress-sockToHostAddress = HostAddress . runPut . putSockAddr--putSockAddr :: SockAddr -> Put-putSockAddr (SockAddrInet6 p _ (a, b, c, d) _) = do-    putWord32be a-    putWord32be b-    putWord32be c-    putWord32be d-    putWord16be (fromIntegral p)--putSockAddr (SockAddrInet p a) = do-    putWord32be 0x00000000-    putWord32be 0x00000000-    putWord32be 0x0000ffff-    putWord32host a-    putWord16be (fromIntegral p)--putSockAddr _ = error "Invalid address type"--getSockAddr :: Get SockAddr-getSockAddr = do-    a <- getWord32be-    b <- getWord32be-    c <- getWord32be-    if a == 0x00000000 && b == 0x00000000 && c == 0x0000ffff-        then do-            d <- getWord32host-            p <- getWord16be-            return $ SockAddrInet (fromIntegral p) d-        else do-            d <- getWord32be-            p <- getWord16be-            return $ SockAddrInet6 (fromIntegral p) 0 (a, b, c, d) 0--instance Serialize NetworkAddress where-    get = NetworkAddress <$> getWord64le <*> S.get-    put (NetworkAddress s a) = putWord64le s >> put a---- | A 'NotFound' message is returned as a response to a 'GetData' message--- whe one of the requested objects could not be retrieved. This could happen,--- for example, if a tranasaction was requested and was not available in the--- memory pool of the receiving node.-newtype NotFound =-    NotFound { -- | Inventory vectors related to this request-               notFoundList :: [InvVector]-             } deriving (Eq, Show, Generic, NFData)--instance Serialize NotFound where--    get = NotFound <$> (repList =<< S.get)-      where-        repList (VarInt c) = replicateM (fromIntegral c) S.get--    put (NotFound xs) = do-        putVarInt $ length xs-        forM_ xs put---- | A 'Ping' message is sent to bitcoin peers to check if a connection is still--- open.-newtype Ping =-    Ping { -- | A random nonce used to identify the recipient of the ping-           -- request once a Pong response is received.-           pingNonce :: Word64-         } deriving (Eq, Show, Read, Generic, NFData)---- | A Pong message is sent as a response to a ping message.-newtype Pong =-    Pong {-           -- | nonce from corresponding 'Ping'-           pongNonce :: Word64-         } deriving (Eq, Show, Read, Generic, NFData)--instance Serialize Ping where-    get = Ping <$> getWord64le-    put (Ping n) = putWord64le n--instance Serialize Pong where-    get = Pong <$> getWord64le-    put (Pong n) = putWord64le n---- | The 'Reject' message is sent when messages are rejected by a peer.-data Reject =-    Reject {-             -- | type of message rejected-             rejectMessage :: !MessageCommand-             -- | rejection code-           , rejectCode    :: !RejectCode-             -- | text reason for rejection-           , rejectReason  :: !VarString-             -- | extra data such as block or tx hash-           , rejectData    :: !ByteString-           } deriving (Eq, Show, Read, Generic, NFData)---- | Rejection code associated to the 'Reject' message.-data RejectCode-    = RejectMalformed-    | RejectInvalid-    | RejectObsolete-    | RejectDuplicate-    | RejectNonStandard-    | RejectDust-    | RejectInsufficientFee-    | RejectCheckpoint-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize RejectCode where--    get = getWord8 >>= \code -> case code of-        0x01 -> return RejectMalformed-        0x10 -> return RejectInvalid-        0x11 -> return RejectObsolete-        0x12 -> return RejectDuplicate-        0x40 -> return RejectNonStandard-        0x41 -> return RejectDust-        0x42 -> return RejectInsufficientFee-        0x43 -> return RejectCheckpoint-        _    -> fail $ unwords-            [ "Reject get: Invalid code"-            , show code-            ]--    put code = putWord8 $ case code of-        RejectMalformed       -> 0x01-        RejectInvalid         -> 0x10-        RejectObsolete        -> 0x11-        RejectDuplicate       -> 0x12-        RejectNonStandard     -> 0x40-        RejectDust            -> 0x41-        RejectInsufficientFee -> 0x42-        RejectCheckpoint      -> 0x43---- | Convenience function to build a 'Reject' message.-reject :: MessageCommand -> RejectCode -> ByteString -> Reject-reject cmd code reason =-    Reject cmd code (VarString reason) B.empty--instance Serialize Reject where-    get =-        S.get >>= \(VarString bs) ->-            Reject (stringToCommand bs) <$> S.get <*> S.get <*> maybeData-      where-        maybeData =-            isEmpty >>= \done ->-                if done-                    then return B.empty-                    else getByteString 32-    put (Reject cmd code reason dat) = do-        put $ VarString $ commandToString cmd-        put code-        put reason-        unless (B.null dat) $ putByteString dat---- | Data type representing a variable-length integer. The 'VarInt' type--- usually precedes an array or a string that can vary in length.-newtype VarInt = VarInt { getVarInt :: Word64 }-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize VarInt where--    get = VarInt <$> ( getWord8 >>= go )-      where-        go 0xff = getWord64le-        go 0xfe = fromIntegral <$> getWord32le-        go 0xfd = fromIntegral <$> getWord16le-        go x    = fromIntegral <$> return x--    put (VarInt x)-        | x < 0xfd =-            putWord8 $ fromIntegral x-        | x <= 0xffff = do-            putWord8 0xfd-            putWord16le $ fromIntegral x-        | x <= 0xffffffff = do-            putWord8 0xfe-            putWord32le $ fromIntegral x-        | otherwise = do-            putWord8 0xff-            putWord64le x--putVarInt :: Integral a => a -> Put-putVarInt = put . VarInt . fromIntegral---- | Data type for serialization of variable-length strings.-newtype VarString = VarString { getVarString :: ByteString }-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize VarString where--    get = VarString <$> (readBS =<< S.get)-      where-        readBS (VarInt len) = getByteString (fromIntegral len)--    put (VarString bs) = do-        putVarInt $ B.length bs-        putByteString bs---- | When a bitcoin node creates an outgoing connection to another node,--- the first message it will send is a 'Version' message. The other node--- will similarly respond with it's own 'Version' message.-data Version =-    Version {-              -- | protocol version-              version     :: !Word32-              -- | features supported by this connection-            , services    :: !Word64-              -- | unix timestamp-            , timestamp   :: !Word64-              -- | network address of remote node-            , addrRecv    :: !NetworkAddress-              -- | network address of sending node-            , addrSend    :: !NetworkAddress-              -- | random nonce to detect connection to self-            , verNonce    :: !Word64-              -- | user agent string-            , userAgent   :: !VarString-              -- | height of the last block in sending node-            , startHeight :: !Word32-              -- | relay transactions flag (BIP-37)-            , relay       :: !Bool-            } deriving (Eq, Show, Generic, NFData)--instance Serialize Version where--    get = Version <$> getWord32le-                  <*> getWord64le-                  <*> getWord64le-                  <*> S.get-                  <*> S.get-                  <*> getWord64le-                  <*> S.get-                  <*> getWord32le-                  <*> (go =<< isEmpty)-      where-        go True  = return True-        go False = getBool--    put (Version v s t ar as n ua sh r) = do-        putWord32le v-        putWord64le s-        putWord64le t-        put         ar-        put         as-        putWord64le n-        put         ua-        putWord32le sh-        putBool     r---- | 0x00 is 'False', anything else is 'True'.-getBool :: Get Bool-getBool = go =<< getWord8-  where-    go 0 = return False-    go _ = return True--putBool :: Bool -> Put-putBool True  = putWord8 1-putBool False = putWord8 0---- | A 'MessageCommand' is included in a 'MessageHeader' in order to identify--- the type of message present in the payload. This allows the message--- de-serialization code to know how to decode a particular message payload.--- Every valid 'Message' constructor has a corresponding 'MessageCommand'--- constructor.-data MessageCommand-    = MCVersion-    | MCVerAck-    | MCAddr-    | MCInv-    | MCGetData-    | MCNotFound-    | MCGetBlocks-    | MCGetHeaders-    | MCTx-    | MCBlock-    | MCMerkleBlock-    | MCHeaders-    | MCGetAddr-    | MCFilterLoad-    | MCFilterAdd-    | MCFilterClear-    | MCPing-    | MCPong-    | MCAlert-    | MCMempool-    | MCReject-    | MCSendHeaders-    | MCOther ByteString-    deriving (Eq, Generic, NFData)--instance Show MessageCommand where-    showsPrec _ = shows . commandToString--instance Read MessageCommand where-    readPrec = do-        String str <- lexP-        return (stringToCommand (cs str))--instance Serialize MessageCommand where-    get = go <$> getByteString 12-      where-        go bs =-            let str = unpackCommand bs-             in stringToCommand str-    put mc = putByteString $ packCommand $ commandToString mc--instance IsString MessageCommand where-    fromString str = stringToCommand (cs str)---- | Read a 'MessageCommand' from its string representation.-stringToCommand :: ByteString -> MessageCommand-stringToCommand str = case str of-    "version"     -> MCVersion-    "verack"      -> MCVerAck-    "addr"        -> MCAddr-    "inv"         -> MCInv-    "getdata"     -> MCGetData-    "notfound"    -> MCNotFound-    "getblocks"   -> MCGetBlocks-    "getheaders"  -> MCGetHeaders-    "tx"          -> MCTx-    "block"       -> MCBlock-    "merkleblock" -> MCMerkleBlock-    "headers"     -> MCHeaders-    "getaddr"     -> MCGetAddr-    "filterload"  -> MCFilterLoad-    "filteradd"   -> MCFilterAdd-    "filterclear" -> MCFilterClear-    "ping"        -> MCPing-    "pong"        -> MCPong-    "alert"       -> MCAlert-    "mempool"     -> MCMempool-    "reject"      -> MCReject-    "sendheaders" -> MCSendHeaders-    _             -> MCOther str---- | Convert a 'MessageCommand' to its string representation.-commandToString :: MessageCommand -> ByteString-commandToString mc = case mc of-    MCVersion     -> "version"-    MCVerAck      -> "verack"-    MCAddr        -> "addr"-    MCInv         -> "inv"-    MCGetData     -> "getdata"-    MCNotFound    -> "notfound"-    MCGetBlocks   -> "getblocks"-    MCGetHeaders  -> "getheaders"-    MCTx          -> "tx"-    MCBlock       -> "block"-    MCMerkleBlock -> "merkleblock"-    MCHeaders     -> "headers"-    MCGetAddr     -> "getaddr"-    MCFilterLoad  -> "filterload"-    MCFilterAdd   -> "filteradd"-    MCFilterClear -> "filterclear"-    MCPing        -> "ping"-    MCPong        -> "pong"-    MCAlert       -> "alert"-    MCMempool     -> "mempool"-    MCReject      -> "reject"-    MCSendHeaders -> "sendheaders"-    MCOther c     -> c---- | Pack a string 'MessageCommand' so that it is exactly 12-bytes long.-packCommand :: ByteString -> ByteString-packCommand s = B.take 12 $-    s `mappend` C.replicate 12 '\NUL'---- | Undo packing done by 'packCommand'.-unpackCommand :: ByteString -> ByteString-unpackCommand = B.takeWhile (/= 0)---- | Node offers no services.-nodeNone :: Word64-nodeNone = 0---- | Services indicate node is a full node that can serve full blocks.-nodeNetwork :: Word64-nodeNetwork = 1---- | Services indicate node allows to request 'UTXO' set.-nodeGetUTXO :: Word64-nodeGetUTXO = 1 `shiftL` 1---- | Services indicate node accepts bloom filters.-nodeBloom :: Word64-nodeBloom = 1 `shiftL` 2---- | Services indicate SegWit-capable node.-nodeWitness :: Word64-nodeWitness = 1 `shiftL` 3---- | Services indicate Xtreme Thinblocks compatibility.-nodeXThin :: Word64-nodeXThin = 1 `shiftL` 4
− src/Network/Haskoin/Network/Message.hs
@@ -1,203 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric  #-}-{-|-Module      : Network.Haskoin.Network.Message-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Peer-to-peer network message serialization.--}-module Network.Haskoin.Network.Message-    ( -- * Network Message-      Message(..)-    , MessageHeader(..)-    , msgType-    , putMessage-    , getMessage-    ) where--import           Control.Monad                      (unless)-import           Control.DeepSeq-import           Data.ByteString                    (ByteString)-import qualified Data.ByteString                    as BS-import           Data.Serialize                     (Serialize, encode, get,-                                                     put)-import           Data.Serialize.Get                 (Get, getByteString,-                                                     getWord32be, getWord32le,-                                                     isolate, lookAhead)-import           Data.Serialize.Put                 (Putter, putByteString,-                                                     putWord32be, putWord32le)-import           Data.Word                          (Word32)-import           GHC.Generics                       (Generic)-import           Network.Haskoin.Block.Common-import           Network.Haskoin.Block.Merkle-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Network.Bloom-import           Network.Haskoin.Network.Common-import           Network.Haskoin.Transaction.Common---- | Data type representing the header of a 'Message'. All messages sent between--- nodes contain a message header.-data MessageHeader = MessageHeader-    { -- | magic bytes identify network-      headMagic       :: !Word32-      -- | message type-    , headCmd         :: !MessageCommand-      -- | length of payload-    , headPayloadSize :: !Word32-      -- | checksum of payload-    , headChecksum    :: !CheckSum32-    } deriving (Eq, Show, Generic, NFData)--instance Serialize MessageHeader where--    get = MessageHeader <$> getWord32be-                        <*> get-                        <*> getWord32le-                        <*> get--    put (MessageHeader m c l chk) = do-        putWord32be m-        put         c-        putWord32le l-        put         chk---- | The 'Message' type is used to identify all the valid messages that can be--- sent between bitcoin peers. Only values of type 'Message' will be accepted--- by other bitcoin peers as bitcoin protocol messages need to be correctly--- serialized with message headers. Serializing a 'Message' value will--- include the 'MessageHeader' with the correct checksum value automatically.--- No need to add the 'MessageHeader' separately.-data Message-    = MVersion !Version-    | MVerAck-    | MAddr !Addr-    | MInv !Inv-    | MGetData !GetData-    | MNotFound !NotFound-    | MGetBlocks !GetBlocks-    | MGetHeaders !GetHeaders-    | MTx !Tx-    | MBlock !Block-    | MMerkleBlock !MerkleBlock-    | MHeaders !Headers-    | MGetAddr-    | MFilterLoad !FilterLoad-    | MFilterAdd !FilterAdd-    | MFilterClear-    | MPing !Ping-    | MPong !Pong-    | MAlert !Alert-    | MMempool-    | MReject !Reject-    | MSendHeaders-    | MOther !ByteString !ByteString-    deriving (Eq, Show, Generic, NFData)---- | Get 'MessageCommand' assocated with a message.-msgType :: Message -> MessageCommand-msgType (MVersion _)     = MCVersion-msgType MVerAck          = MCVerAck-msgType (MAddr _)        = MCAddr-msgType (MInv _)         = MCInv-msgType (MGetData _)     = MCGetData-msgType (MNotFound _)    = MCNotFound-msgType (MGetBlocks _)   = MCGetBlocks-msgType (MGetHeaders _)  = MCGetHeaders-msgType (MTx _)          = MCTx-msgType (MBlock _)       = MCBlock-msgType (MMerkleBlock _) = MCMerkleBlock-msgType (MHeaders _)     = MCHeaders--msgType (MFilterLoad _)  = MCFilterLoad-msgType (MFilterAdd _)   = MCFilterAdd-msgType MFilterClear     = MCFilterClear-msgType (MPing _)        = MCPing-msgType (MPong _)        = MCPong-msgType (MAlert _)       = MCAlert-msgType MMempool         = MCMempool-msgType (MReject _)      = MCReject-msgType MSendHeaders     = MCSendHeaders-msgType MGetAddr         = MCGetAddr-msgType (MOther c _)     = MCOther c---- | Deserializer for network messages.-getMessage :: Network -> Get Message-getMessage net = do-    (MessageHeader mgc cmd len chk) <- get-    bs <- lookAhead $ getByteString $ fromIntegral len-    unless-        (mgc == getNetworkMagic net)-        (fail $ "get: Invalid network magic bytes: " ++ show mgc)-    unless-        (checkSum32 bs == chk)-        (fail $ "get: Invalid message checksum: " ++ show chk)-    if len > 0-        then isolate (fromIntegral len) $-             case cmd of-                 MCVersion     -> MVersion <$> get-                 MCAddr        -> MAddr <$> get-                 MCInv         -> MInv <$> get-                 MCGetData     -> MGetData <$> get-                 MCNotFound    -> MNotFound <$> get-                 MCGetBlocks   -> MGetBlocks <$> get-                 MCGetHeaders  -> MGetHeaders <$> get-                 MCTx          -> MTx <$> get-                 MCBlock       -> MBlock <$> get-                 MCMerkleBlock -> MMerkleBlock <$> get-                 MCHeaders     -> MHeaders <$> get-                 MCFilterLoad  -> MFilterLoad <$> get-                 MCFilterAdd   -> MFilterAdd <$> get-                 MCPing        -> MPing <$> get-                 MCPong        -> MPong <$> get-                 MCAlert       -> MAlert <$> get-                 MCReject      -> MReject <$> get-                 MCOther c     -> MOther c <$> getByteString (fromIntegral len)-                 _             -> fail $ "get: command " ++ show cmd ++-                                         " should not carry a payload"-        else case cmd of-                 MCGetAddr     -> return MGetAddr-                 MCVerAck      -> return MVerAck-                 MCFilterClear -> return MFilterClear-                 MCMempool     -> return MMempool-                 MCSendHeaders -> return MSendHeaders-                 _             -> fail $ "get: command " ++ show cmd ++-                                         " is expected to carry a payload"---- | Serializer for network messages.-putMessage :: Network -> Putter Message-putMessage net msg = do-    let (cmd, payload) =-            case msg of-                MVersion m     -> (MCVersion, encode m)-                MVerAck        -> (MCVerAck, BS.empty)-                MAddr m        -> (MCAddr, encode m)-                MInv m         -> (MCInv, encode m)-                MGetData m     -> (MCGetData, encode m)-                MNotFound m    -> (MCNotFound, encode m)-                MGetBlocks m   -> (MCGetBlocks, encode m)-                MGetHeaders m  -> (MCGetHeaders, encode m)-                MTx m          -> (MCTx, encode m)-                MBlock m       -> (MCBlock, encode m)-                MMerkleBlock m -> (MCMerkleBlock, encode m)-                MHeaders m     -> (MCHeaders, encode m)-                MGetAddr       -> (MCGetAddr, BS.empty)-                MFilterLoad m  -> (MCFilterLoad, encode m)-                MFilterAdd m   -> (MCFilterAdd, encode m)-                MFilterClear   -> (MCFilterClear, BS.empty)-                MPing m        -> (MCPing, encode m)-                MPong m        -> (MCPong, encode m)-                MAlert m       -> (MCAlert, encode m)-                MMempool       -> (MCMempool, BS.empty)-                MReject m      -> (MCReject, encode m)-                MSendHeaders   -> (MCSendHeaders, BS.empty)-                MOther c p     -> (MCOther c, p)-        chk = checkSum32 payload-        len = fromIntegral $ BS.length payload-        header = MessageHeader (getNetworkMagic net) cmd len chk-    put header-    putByteString payload
− src/Network/Haskoin/Script.hs
@@ -1,19 +0,0 @@-{-|-Module      : Network.Haskoin.Script-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--This module provides functions for parsing and evaluating bitcoin-transaction scripts. Data types are provided for building and-deconstructing all of the standard input and output script types.--}-module Network.Haskoin.Script-    ( module X-    ) where--import           Network.Haskoin.Script.Common   as X-import           Network.Haskoin.Script.SigHash  as X-import           Network.Haskoin.Script.Standard as X
− src/Network/Haskoin/Script/Common.hs
@@ -1,733 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Script.Common-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Common script-related functions and data types.--}-module Network.Haskoin.Script.Common-    ( ScriptOp(..)-    , Script(..)-    , PushDataType(..)-    , ScriptOutput(..)-    , isPayPK-    , isPayPKHash-    , isPayMulSig-    , isPayScriptHash-    , isPayWitnessPKHash-    , isPayWitnessScriptHash-    , isDataCarrier-    , encodeOutput-    , encodeOutputBS-    , decodeOutput-    , decodeOutputBS-    , toP2SH-    , toP2WSH-    , isPushOp-    , opPushData-    , intToScriptOp-    , scriptOpToInt-    ) where--import           Control.DeepSeq-import           Control.Monad-import           Data.Aeson                  as A-import           Data.ByteString             (ByteString)-import qualified Data.ByteString             as B-import           Data.Hashable-import           Data.Serialize              as S-import           Data.Serialize.Get          (getByteString, getWord16le,-                                              getWord32le, getWord8, isEmpty)-import           Data.Serialize.Put          (putByteString, putWord16le,-                                              putWord32le, putWord8)-import           Data.Word                   (Word8)-import           GHC.Generics                (Generic)-import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Keys.Common-import           Network.Haskoin.Util---- | Data type representing a transaction script. Scripts are defined as lists--- of script operators 'ScriptOp'. Scripts are used to:------ * Define the spending conditions in the output of a transaction.--- * Provide signatures in the input of a transaction (except SegWit).------ SigWit only: the segregated witness data structure, and not the input script,--- contains signatures and redeem script for pay-to-witness-script and--- pay-to-witness-public-key-hash transactions.-newtype Script =-    Script {-             -- | script operators defining this script-             scriptOps :: [ScriptOp]-           }-    deriving (Eq, Show, Read, Generic, Hashable, NFData)--instance Serialize Script where-    get =-        Script <$> getScriptOps-      where-        getScriptOps = do-            empty <- isEmpty-            if empty-                then return []-                else (:) <$> get <*> getScriptOps--    put (Script ops) = forM_ ops put---- | Data type representing the type of an OP_PUSHDATA opcode.-data PushDataType-    =-      -- | next opcode bytes is data to be pushed-      OPCODE-      -- | next byte contains number of bytes of data to be pushed-    | OPDATA1-      -- | next two bytes contains number of bytes to be pushed-    | OPDATA2-      -- | next four bytes contains the number of bytes to be pushed-    | OPDATA4-    deriving (Show, Read, Eq, Generic, Hashable, NFData)---- | Data type representing an operator allowed inside a 'Script'.-data ScriptOp-      -- Pushing Data-    = OP_PUSHDATA !ByteString-                  !PushDataType-    | OP_0-    | OP_1NEGATE-    | OP_RESERVED-    | OP_1-    | OP_2-    | OP_3-    | OP_4-    | OP_5-    | OP_6-    | OP_7-    | OP_8-    | OP_9-    | OP_10-    | OP_11-    | OP_12-    | OP_13-    | OP_14-    | OP_15-    | OP_16-      -- Flow control-    | OP_NOP-    | OP_VER -- reserved-    | OP_IF-    | OP_NOTIF-    | OP_VERIF -- resreved-    | OP_VERNOTIF -- reserved-    | OP_ELSE-    | OP_ENDIF-    | OP_VERIFY-    | OP_RETURN-      -- Stack operations-    | OP_TOALTSTACK-    | OP_FROMALTSTACK-    | OP_IFDUP-    | OP_DEPTH-    | OP_DROP-    | OP_DUP-    | OP_NIP-    | OP_OVER-    | OP_PICK-    | OP_ROLL-    | OP_ROT-    | OP_SWAP-    | OP_TUCK-    | OP_2DROP-    | OP_2DUP-    | OP_3DUP-    | OP_2OVER-    | OP_2ROT-    | OP_2SWAP-      -- Splice-    | OP_CAT-    | OP_SUBSTR-    | OP_LEFT-    | OP_RIGHT-    | OP_SIZE-      -- Bitwise logic-    | OP_INVERT-    | OP_AND-    | OP_OR-    | OP_XOR-    | OP_EQUAL-    | OP_EQUALVERIFY-    | OP_RESERVED1-    | OP_RESERVED2-      -- Arithmetic-    | OP_1ADD-    | OP_1SUB-    | OP_2MUL-    | OP_2DIV-    | OP_NEGATE-    | OP_ABS-    | OP_NOT-    | OP_0NOTEQUAL-    | OP_ADD-    | OP_SUB-    | OP_MUL-    | OP_DIV-    | OP_MOD-    | OP_LSHIFT-    | OP_RSHIFT-    | OP_BOOLAND-    | OP_BOOLOR-    | OP_NUMEQUAL-    | OP_NUMEQUALVERIFY-    | OP_NUMNOTEQUAL-    | OP_LESSTHAN-    | OP_GREATERTHAN-    | OP_LESSTHANOREQUAL-    | OP_GREATERTHANOREQUAL-    | OP_MIN-    | OP_MAX-    | OP_WITHIN-      -- Crypto-    | OP_RIPEMD160-    | OP_SHA1-    | OP_SHA256-    | OP_HASH160-    | OP_HASH256-    | OP_CODESEPARATOR-    | OP_CHECKSIG-    | OP_CHECKSIGVERIFY-    | OP_CHECKMULTISIG-    | OP_CHECKMULTISIGVERIFY-      -- Expansion-    | OP_NOP1-    | OP_NOP2-    | OP_NOP3-    | OP_NOP4-    | OP_NOP5-    | OP_NOP6-    | OP_NOP7-    | OP_NOP8-    | OP_NOP9-    | OP_NOP10-      -- Bitcoin Cash Nov 2018 hard fork-    | OP_CHECKDATASIG-    | OP_CHECKDATASIGVERIFY-      -- Other-    | OP_PUBKEYHASH-    | OP_PUBKEY-    | OP_INVALIDOPCODE !Word8-    deriving (Show, Read, Eq, Generic, Hashable, NFData)--instance Serialize ScriptOp where-    get = go =<< (fromIntegral <$> getWord8)-      where-        go op-            | op == 0x00 = return OP_0-            | op <= 0x4b = do-                payload <- getByteString (fromIntegral op)-                return $ OP_PUSHDATA payload OPCODE-            | op == 0x4c = do-                len  <- getWord8-                payload <- getByteString (fromIntegral len)-                return $ OP_PUSHDATA payload OPDATA1-            | op == 0x4d = do-                len  <- getWord16le-                payload <- getByteString (fromIntegral len)-                return $ OP_PUSHDATA payload OPDATA2-            | op == 0x4e = do-                len  <- getWord32le-                payload <- getByteString (fromIntegral len)-                return $ OP_PUSHDATA payload OPDATA4--            | op == 0x4f = return OP_1NEGATE-            | op == 0x50 = return OP_RESERVED-            | op == 0x51 = return OP_1-            | op == 0x52 = return OP_2-            | op == 0x53 = return OP_3-            | op == 0x54 = return OP_4-            | op == 0x55 = return OP_5-            | op == 0x56 = return OP_6-            | op == 0x57 = return OP_7-            | op == 0x58 = return OP_8-            | op == 0x59 = return OP_9-            | op == 0x5a = return OP_10-            | op == 0x5b = return OP_11-            | op == 0x5c = return OP_12-            | op == 0x5d = return OP_13-            | op == 0x5e = return OP_14-            | op == 0x5f = return OP_15-            | op == 0x60 = return OP_16--            -- Flow control-            | op == 0x61 = return OP_NOP-            | op == 0x62 = return OP_VER        -- reserved-            | op == 0x63 = return OP_IF-            | op == 0x64 = return OP_NOTIF-            | op == 0x65 = return OP_VERIF      -- reserved-            | op == 0x66 = return OP_VERNOTIF   -- reserved-            | op == 0x67 = return OP_ELSE-            | op == 0x68 = return OP_ENDIF-            | op == 0x69 = return OP_VERIFY-            | op == 0x6a = return OP_RETURN--            -- Stack-            | op == 0x6b = return OP_TOALTSTACK-            | op == 0x6c = return OP_FROMALTSTACK-            | op == 0x6d = return OP_2DROP-            | op == 0x6e = return OP_2DUP-            | op == 0x6f = return OP_3DUP-            | op == 0x70 = return OP_2OVER-            | op == 0x71 = return OP_2ROT-            | op == 0x72 = return OP_2SWAP-            | op == 0x73 = return OP_IFDUP-            | op == 0x74 = return OP_DEPTH-            | op == 0x75 = return OP_DROP-            | op == 0x76 = return OP_DUP-            | op == 0x77 = return OP_NIP-            | op == 0x78 = return OP_OVER-            | op == 0x79 = return OP_PICK-            | op == 0x7a = return OP_ROLL-            | op == 0x7b = return OP_ROT-            | op == 0x7c = return OP_SWAP-            | op == 0x7d = return OP_TUCK--            -- Splice-            | op == 0x7e = return OP_CAT-            | op == 0x7f = return OP_SUBSTR-            | op == 0x80 = return OP_LEFT-            | op == 0x81 = return OP_RIGHT-            | op == 0x82 = return OP_SIZE--            -- Bitwise logic-            | op == 0x83 = return OP_INVERT-            | op == 0x84 = return OP_AND-            | op == 0x85 = return OP_OR-            | op == 0x86 = return OP_XOR-            | op == 0x87 = return OP_EQUAL-            | op == 0x88 = return OP_EQUALVERIFY-            | op == 0x89 = return OP_RESERVED1-            | op == 0x8a = return OP_RESERVED2--            -- Arithmetic-            | op == 0x8b = return OP_1ADD-            | op == 0x8c = return OP_1SUB-            | op == 0x8d = return OP_2MUL-            | op == 0x8e = return OP_2DIV-            | op == 0x8f = return OP_NEGATE-            | op == 0x90 = return OP_ABS-            | op == 0x91 = return OP_NOT-            | op == 0x92 = return OP_0NOTEQUAL-            | op == 0x93 = return OP_ADD-            | op == 0x94 = return OP_SUB-            | op == 0x95 = return OP_MUL-            | op == 0x96 = return OP_DIV-            | op == 0x97 = return OP_MOD-            | op == 0x98 = return OP_LSHIFT-            | op == 0x99 = return OP_RSHIFT-            | op == 0x9a = return OP_BOOLAND-            | op == 0x9b = return OP_BOOLOR-            | op == 0x9c = return OP_NUMEQUAL-            | op == 0x9d = return OP_NUMEQUALVERIFY-            | op == 0x9e = return OP_NUMNOTEQUAL-            | op == 0x9f = return OP_LESSTHAN-            | op == 0xa0 = return OP_GREATERTHAN-            | op == 0xa1 = return OP_LESSTHANOREQUAL-            | op == 0xa2 = return OP_GREATERTHANOREQUAL-            | op == 0xa3 = return OP_MIN-            | op == 0xa4 = return OP_MAX-            | op == 0xa5 = return OP_WITHIN--            -- Crypto-            | op == 0xa6 = return OP_RIPEMD160-            | op == 0xa7 = return OP_SHA1-            | op == 0xa8 = return OP_SHA256-            | op == 0xa9 = return OP_HASH160-            | op == 0xaa = return OP_HASH256-            | op == 0xab = return OP_CODESEPARATOR-            | op == 0xac = return OP_CHECKSIG-            | op == 0xad = return OP_CHECKSIGVERIFY-            | op == 0xae = return OP_CHECKMULTISIG-            | op == 0xaf = return OP_CHECKMULTISIGVERIFY--            -- More NOPs-            | op == 0xb0 = return OP_NOP1-            | op == 0xb1 = return OP_NOP2-            | op == 0xb2 = return OP_NOP3-            | op == 0xb3 = return OP_NOP4-            | op == 0xb4 = return OP_NOP5-            | op == 0xb5 = return OP_NOP6-            | op == 0xb6 = return OP_NOP7-            | op == 0xb7 = return OP_NOP8-            | op == 0xb8 = return OP_NOP9-            | op == 0xb9 = return OP_NOP10--            -- Bitcoin Cash Nov 2018 hard fork-            | op == 0xba = return OP_CHECKDATASIG-            | op == 0xbb = return OP_CHECKDATASIGVERIFY--            -- Constants-            | op == 0xfd = return OP_PUBKEYHASH-            | op == 0xfe = return OP_PUBKEY--            | otherwise = return $ OP_INVALIDOPCODE op--    put op = case op of--        (OP_PUSHDATA payload optype)-> do-            let len = B.length payload-            case optype of-                OPCODE -> do-                    unless (len <= 0x4b) $-                        error "OP_PUSHDATA OPCODE: Payload size too big"-                    putWord8 $ fromIntegral len-                OPDATA1 -> do-                    unless (len <= 0xff) $-                        error "OP_PUSHDATA OPDATA1: Payload size too big"-                    putWord8 0x4c-                    putWord8 $ fromIntegral len-                OPDATA2 -> do-                    unless (len <= 0xffff) $-                        error "OP_PUSHDATA OPDATA2: Payload size too big"-                    putWord8 0x4d-                    putWord16le $ fromIntegral len-                OPDATA4 -> do-                    unless (len <= 0x7fffffff) $-                        error "OP_PUSHDATA OPDATA4: Payload size too big"-                    putWord8 0x4e-                    putWord32le $ fromIntegral len-            putByteString payload--        -- Constants-        OP_0                 -> putWord8 0x00-        OP_1NEGATE           -> putWord8 0x4f-        OP_RESERVED          -> putWord8 0x50-        OP_1                 -> putWord8 0x51-        OP_2                 -> putWord8 0x52-        OP_3                 -> putWord8 0x53-        OP_4                 -> putWord8 0x54-        OP_5                 -> putWord8 0x55-        OP_6                 -> putWord8 0x56-        OP_7                 -> putWord8 0x57-        OP_8                 -> putWord8 0x58-        OP_9                 -> putWord8 0x59-        OP_10                -> putWord8 0x5a-        OP_11                -> putWord8 0x5b-        OP_12                -> putWord8 0x5c-        OP_13                -> putWord8 0x5d-        OP_14                -> putWord8 0x5e-        OP_15                -> putWord8 0x5f-        OP_16                -> putWord8 0x60--        -- Crypto Constants-        OP_PUBKEY            -> putWord8 0xfe-        OP_PUBKEYHASH        -> putWord8 0xfd--        -- Invalid Opcodes-        (OP_INVALIDOPCODE x) -> putWord8 x--        -- Flow Control-        OP_NOP               -> putWord8 0x61-        OP_VER               -> putWord8 0x62-        OP_IF                -> putWord8 0x63-        OP_NOTIF             -> putWord8 0x64-        OP_VERIF             -> putWord8 0x65-        OP_VERNOTIF          -> putWord8 0x66-        OP_ELSE              -> putWord8 0x67-        OP_ENDIF             -> putWord8 0x68-        OP_VERIFY            -> putWord8 0x69-        OP_RETURN            -> putWord8 0x6a--        -- Stack Operations-        OP_TOALTSTACK        -> putWord8 0x6b-        OP_FROMALTSTACK      -> putWord8 0x6c-        OP_2DROP             -> putWord8 0x6d-        OP_2DUP              -> putWord8 0x6e-        OP_3DUP              -> putWord8 0x6f-        OP_2OVER             -> putWord8 0x70-        OP_2ROT              -> putWord8 0x71-        OP_2SWAP             -> putWord8 0x72-        OP_IFDUP             -> putWord8 0x73-        OP_DEPTH             -> putWord8 0x74-        OP_DROP              -> putWord8 0x75-        OP_DUP               -> putWord8 0x76-        OP_NIP               -> putWord8 0x77-        OP_OVER              -> putWord8 0x78-        OP_PICK              -> putWord8 0x79-        OP_ROLL              -> putWord8 0x7a-        OP_ROT               -> putWord8 0x7b-        OP_SWAP              -> putWord8 0x7c-        OP_TUCK              -> putWord8 0x7d--        -- Splice-        OP_CAT               -> putWord8 0x7e-        OP_SUBSTR            -> putWord8 0x7f-        OP_LEFT              -> putWord8 0x80-        OP_RIGHT             -> putWord8 0x81-        OP_SIZE              -> putWord8 0x82--        -- Bitwise Logic-        OP_INVERT            -> putWord8 0x83-        OP_AND               -> putWord8 0x84-        OP_OR                -> putWord8 0x85-        OP_XOR               -> putWord8 0x86-        OP_EQUAL             -> putWord8 0x87-        OP_EQUALVERIFY       -> putWord8 0x88-        OP_RESERVED1         -> putWord8 0x89-        OP_RESERVED2         -> putWord8 0x8a--        -- Arithmetic-        OP_1ADD              -> putWord8 0x8b-        OP_1SUB              -> putWord8 0x8c-        OP_2MUL              -> putWord8 0x8d-        OP_2DIV              -> putWord8 0x8e-        OP_NEGATE            -> putWord8 0x8f-        OP_ABS               -> putWord8 0x90-        OP_NOT               -> putWord8 0x91-        OP_0NOTEQUAL         -> putWord8 0x92-        OP_ADD               -> putWord8 0x93-        OP_SUB               -> putWord8 0x94-        OP_MUL               -> putWord8 0x95-        OP_DIV               -> putWord8 0x96-        OP_MOD               -> putWord8 0x97-        OP_LSHIFT            -> putWord8 0x98-        OP_RSHIFT            -> putWord8 0x99-        OP_BOOLAND           -> putWord8 0x9a-        OP_BOOLOR            -> putWord8 0x9b-        OP_NUMEQUAL          -> putWord8 0x9c-        OP_NUMEQUALVERIFY    -> putWord8 0x9d-        OP_NUMNOTEQUAL       -> putWord8 0x9e-        OP_LESSTHAN          -> putWord8 0x9f-        OP_GREATERTHAN       -> putWord8 0xa0-        OP_LESSTHANOREQUAL   -> putWord8 0xa1-        OP_GREATERTHANOREQUAL-> putWord8 0xa2-        OP_MIN               -> putWord8 0xa3-        OP_MAX               -> putWord8 0xa4-        OP_WITHIN            -> putWord8 0xa5--        -- Crypto-        OP_RIPEMD160         -> putWord8 0xa6-        OP_SHA1              -> putWord8 0xa7-        OP_SHA256            -> putWord8 0xa8-        OP_HASH160           -> putWord8 0xa9-        OP_HASH256           -> putWord8 0xaa-        OP_CODESEPARATOR     -> putWord8 0xab-        OP_CHECKSIG          -> putWord8 0xac-        OP_CHECKSIGVERIFY    -> putWord8 0xad-        OP_CHECKMULTISIG     -> putWord8 0xae-        OP_CHECKMULTISIGVERIFY -> putWord8 0xaf--        -- More NOPs-        OP_NOP1              -> putWord8 0xb0-        OP_NOP2              -> putWord8 0xb1-        OP_NOP3              -> putWord8 0xb2-        OP_NOP4              -> putWord8 0xb3-        OP_NOP5              -> putWord8 0xb4-        OP_NOP6              -> putWord8 0xb5-        OP_NOP7              -> putWord8 0xb6-        OP_NOP8              -> putWord8 0xb7-        OP_NOP9              -> putWord8 0xb8-        OP_NOP10             -> putWord8 0xb9--        -- Bitcoin Cash Nov 2018 hard fork-        OP_CHECKDATASIG      -> putWord8 0xba-        OP_CHECKDATASIGVERIFY -> putWord8 0xbb----- | Check whether opcode is only data.-isPushOp :: ScriptOp -> Bool-isPushOp op = case op of-    OP_PUSHDATA _ _ -> True-    OP_0            -> True-    OP_1NEGATE      -> True-    OP_1            -> True-    OP_2            -> True-    OP_3            -> True-    OP_4            -> True-    OP_5            -> True-    OP_6            -> True-    OP_7            -> True-    OP_8            -> True-    OP_9            -> True-    OP_10           -> True-    OP_11           -> True-    OP_12           -> True-    OP_13           -> True-    OP_14           -> True-    OP_15           -> True-    OP_16           -> True-    _               -> False---- | Optimally encode data using one of the 4 types of data pushing opcodes.-opPushData :: ByteString -> ScriptOp-opPushData bs-    | len <= 0x4b       = OP_PUSHDATA bs OPCODE-    | len <= 0xff       = OP_PUSHDATA bs OPDATA1-    | len <= 0xffff     = OP_PUSHDATA bs OPDATA2-    | len <= 0xffffffff = OP_PUSHDATA bs OPDATA4-    | otherwise         = error "opPushData: payload size too big"-  where-    len = B.length bs---- | Transforms integers @[1 .. 16]@ to 'ScriptOp' @[OP_1 .. OP_16]@.-intToScriptOp :: Int -> ScriptOp-intToScriptOp i-    | i `elem` [1 .. 16] = op-    | otherwise = err-  where-    op = either (const err) id . S.decode . B.singleton . fromIntegral $ i + 0x50-    err = error $ "intToScriptOp: Invalid integer " ++ show i---- | Decode 'ScriptOp' @[OP_1 .. OP_16]@ to integers @[1 .. 16]@. This functions--- fails for other values of 'ScriptOp'-scriptOpToInt :: ScriptOp -> Either String Int-scriptOpToInt s-    | res `elem` [1..16] = return res-    | otherwise          = Left $ "scriptOpToInt: invalid opcode " ++ show s-  where-    res = fromIntegral (B.head $ S.encode s) - 0x50---- | Data type describing standard transaction output scripts. Output scripts--- provide the conditions that must be fulfilled for someone to spend the funds--- in a transaction output.-data ScriptOutput-      -- | pay to public key-    = PayPK { getOutputPubKey :: !PubKeyI }-      -- | pay to public key hash-    | PayPKHash { getOutputHash :: !Hash160 }-      -- | multisig-    | PayMulSig { getOutputMulSigKeys     :: ![PubKeyI]-                , getOutputMulSigRequired :: !Int }-      -- | pay to a script hash-    | PayScriptHash { getOutputHash :: !Hash160 }-      -- | pay to witness public key hash-    | PayWitnessPKHash { getOutputHash :: !Hash160 }-      -- | pay to witness script hash-    | PayWitnessScriptHash { getScriptHash :: !Hash256 }-      -- | provably unspendable data carrier-    | DataCarrier { getOutputData :: !ByteString }-    deriving (Eq, Show, Read, Generic, Hashable, NFData)--instance FromJSON ScriptOutput where-    parseJSON = withText "scriptoutput" $ \t -> either fail return $-        maybeToEither "scriptoutput not hex" (decodeHex t) >>=-        decodeOutputBS--instance ToJSON ScriptOutput where-    toJSON = String . encodeHex . encodeOutputBS---- | Is script a pay-to-public-key output?-isPayPK :: ScriptOutput -> Bool-isPayPK (PayPK _) = True-isPayPK _         = False---- | Is script a pay-to-pub-key-hash output?-isPayPKHash :: ScriptOutput -> Bool-isPayPKHash (PayPKHash _) = True-isPayPKHash _             = False---- | Is script a pay-to-multi-sig output?-isPayMulSig :: ScriptOutput -> Bool-isPayMulSig (PayMulSig _ _) = True-isPayMulSig _               = False---- | Is script a pay-to-script-hash output?-isPayScriptHash :: ScriptOutput -> Bool-isPayScriptHash (PayScriptHash _) = True-isPayScriptHash _                 = False---- | Is script a pay-to-witness-pub-key-hash output?-isPayWitnessPKHash :: ScriptOutput -> Bool-isPayWitnessPKHash (PayWitnessPKHash _) = True-isPayWitnessPKHash _                    = False---- | Is script a pay-to-witness-script-hash output?-isPayWitnessScriptHash :: ScriptOutput -> Bool-isPayWitnessScriptHash (PayWitnessScriptHash _) = True-isPayWitnessScriptHash _                        = False---- | Is script a data carrier output?-isDataCarrier :: ScriptOutput -> Bool-isDataCarrier (DataCarrier _) = True-isDataCarrier _               = False---- | Tries to decode a 'ScriptOutput' from a 'Script'. This can fail if the--- script is not recognized as any of the standard output types.-decodeOutput :: Script -> Either String ScriptOutput-decodeOutput s = case scriptOps s of-    -- Pay to PubKey-    [OP_PUSHDATA bs _, OP_CHECKSIG] -> PayPK <$> S.decode bs-    -- Pay to PubKey Hash-    [OP_DUP, OP_HASH160, OP_PUSHDATA bs _, OP_EQUALVERIFY, OP_CHECKSIG] ->-        PayPKHash <$> S.decode bs-    -- Pay to Script Hash-    [OP_HASH160, OP_PUSHDATA bs _, OP_EQUAL] ->-        PayScriptHash <$> S.decode  bs-    -- Pay to Witness-    [OP_0, OP_PUSHDATA bs OPCODE]-      | B.length bs == 20 -> PayWitnessPKHash     <$> S.decode bs-      | B.length bs == 32 -> PayWitnessScriptHash <$> S.decode bs-    -- Provably unspendable data carrier output-    [OP_RETURN, OP_PUSHDATA bs _] -> Right $ DataCarrier bs-    -- Pay to MultiSig Keys-    _ -> matchPayMulSig s---- | Similar to 'decodeOutput' but decodes from a 'ByteString'.-decodeOutputBS :: ByteString -> Either String ScriptOutput-decodeOutputBS = decodeOutput <=< S.decode---- | Computes a 'Script' from a standard 'ScriptOutput'.-encodeOutput :: ScriptOutput -> Script-encodeOutput s = Script $ case s of-    -- Pay to PubKey-    (PayPK k) -> [opPushData $ S.encode k, OP_CHECKSIG]-    -- Pay to PubKey Hash Address-    (PayPKHash h) ->-        [ OP_DUP, OP_HASH160, opPushData $ S.encode h, OP_EQUALVERIFY, OP_CHECKSIG]-    -- Pay to MultiSig Keys-    (PayMulSig ps r)-      | r <= length ps ->-        let opM = intToScriptOp r-            opN = intToScriptOp $ length ps-            keys = map (opPushData . S.encode) ps-            in opM : keys ++ [opN, OP_CHECKMULTISIG]-      | otherwise -> error "encodeOutput: PayMulSig r must be <= than pkeys"-    -- Pay to Script Hash Address-    (PayScriptHash h) ->-        [ OP_HASH160, opPushData $ S.encode h, OP_EQUAL]-    -- Pay to Witness PubKey Hash Address-    (PayWitnessPKHash h) ->-        [ OP_0, opPushData $ S.encode h ]-    (PayWitnessScriptHash h) ->-        [ OP_0, opPushData $ S.encode h ]-    -- Provably unspendable output-    (DataCarrier d) -> [OP_RETURN, opPushData d]---- | Similar to 'encodeOutput' but encodes to a ByteString-encodeOutputBS :: ScriptOutput -> ByteString-encodeOutputBS = S.encode . encodeOutput---- | Encode script as pay-to-script-hash script-toP2SH :: Script -> ScriptOutput-toP2SH = PayScriptHash . addressHash . S.encode---- | Encode script as a pay-to-witness-script-hash script-toP2WSH :: Script -> ScriptOutput-toP2WSH = PayWitnessScriptHash . sha256 . S.encode---- | Match @[OP_N, PubKey1, ..., PubKeyM, OP_M, OP_CHECKMULTISIG]@-matchPayMulSig :: Script -> Either String ScriptOutput-matchPayMulSig (Script ops) = case splitAt (length ops - 2) ops of-    (m:xs,[n,OP_CHECKMULTISIG]) -> do-        (intM,intN) <- liftM2 (,) (scriptOpToInt m) (scriptOpToInt n)-        if intM <= intN && length xs == intN-            then liftM2 PayMulSig (go xs) (return intM)-            else Left "matchPayMulSig: Invalid M or N parameters"-    _ -> Left "matchPayMulSig: script did not match output template"-  where-    go (OP_PUSHDATA bs _:xs) = liftM2 (:) (S.decode bs) (go xs)-    go []                    = return []-    go  _                    = Left "matchPayMulSig: invalid multisig opcode"
− src/Network/Haskoin/Script/SigHash.hs
@@ -1,319 +0,0 @@-{-# LANGUAGE DeriveGeneric              #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE OverloadedStrings          #-}-{-|-Module      : Network.Haskoin.Script.SigHash-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Transaction signatures and related functions.--}-module Network.Haskoin.Script.SigHash-( SigHash-, SigHashFlag(..)-, sigHashAll-, sigHashNone-, sigHashSingle-, hasAnyoneCanPayFlag-, hasForkIdFlag-, setAnyoneCanPayFlag-, setForkIdFlag-, isSigHashAll-, isSigHashNone-, isSigHashSingle-, isSigHashUnknown-, sigHashAddForkId-, sigHashGetForkId-, sigHashAddNetworkId-, txSigHash-, txSigHashForkId-, TxSignature(..)-, encodeTxSig-, decodeTxSig-) where--import           Control.DeepSeq-import           Control.Monad-import           Crypto.Secp256k1-import qualified Data.Aeson                         as J-import           Data.Bits-import           Data.ByteString                    (ByteString)-import qualified Data.ByteString                    as BS-import           Data.Hashable-import           Data.Maybe-import           Data.Scientific-import           Data.Serialize-import           Data.Serialize.Put                 (runPut)-import           Data.Word-import           GHC.Generics                       (Generic)-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Crypto.Signature-import           Network.Haskoin.Network-import           Network.Haskoin.Script.Common-import           Network.Haskoin.Transaction.Common-import           Network.Haskoin.Util---- | Constant representing a SIGHASH flag that controls what is being signed.-data SigHashFlag-    = SIGHASH_ALL-      -- ^ sign all outputs-    | SIGHASH_NONE-      -- ^ sign no outputs-    | SIGHASH_SINGLE-      -- ^ sign the output index corresponding to the input-    | SIGHASH_FORKID-      -- ^ replay protection for Bitcoin Cash transactions-    | SIGHASH_ANYONECANPAY-      -- ^ new inputs can be added-    deriving (Eq, Ord, Show, Read, Generic)--instance NFData SigHashFlag--instance Hashable SigHashFlag--instance Enum SigHashFlag where-    fromEnum SIGHASH_ALL          = 0x01-    fromEnum SIGHASH_NONE         = 0x02-    fromEnum SIGHASH_SINGLE       = 0x03-    fromEnum SIGHASH_FORKID       = 0x40-    fromEnum SIGHASH_ANYONECANPAY = 0x80-    toEnum 0x01 = SIGHASH_ALL-    toEnum 0x02 = SIGHASH_NONE-    toEnum 0x03 = SIGHASH_SINGLE-    toEnum 0x40 = SIGHASH_FORKID-    toEnum 0x80 = SIGHASH_ANYONECANPAY-    toEnum _    = error "Not a valid sighash flag"---- | Data type representing the different ways a transaction can be signed.--- When producing a signature, a hash of the transaction is used as the message--- to be signed. The 'SigHash' parameter controls which parts of the--- transaction are used or ignored to produce the transaction hash. The idea is--- that if some part of a transaction is not used to produce the transaction--- hash, then you can change that part of the transaction after producing a--- signature without invalidating that signature.------ If the 'SIGHASH_ANYONECANPAY' flag is set (true), then only the current input--- is signed. Otherwise, all of the inputs of a transaction are signed. The--- default value for 'SIGHASH_ANYONECANPAY' is unset (false).-newtype SigHash =-    SigHash Word32-    deriving ( Eq-             , Ord-             , Bits-             , Enum-             , Integral-             , Num-             , Real-             , Show-             , Read-             , Generic-             , Hashable-             , NFData-             )--instance J.FromJSON SigHash where-    parseJSON =-        J.withScientific "sighash" $-        maybe mzero (return . SigHash) . toBoundedInteger--instance J.ToJSON SigHash where-    toJSON = J.Number . fromIntegral---- | SIGHASH_NONE as a byte.-sigHashNone :: SigHash-sigHashNone = fromIntegral $ fromEnum SIGHASH_NONE---- | SIGHASH_ALL as a byte.-sigHashAll :: SigHash-sigHashAll = fromIntegral $ fromEnum SIGHASH_ALL---- | SIGHASH_SINGLE as a byte.-sigHashSingle :: SigHash-sigHashSingle = fromIntegral $ fromEnum SIGHASH_SINGLE---- | SIGHASH_FORKID as a byte.-sigHashForkId :: SigHash-sigHashForkId = fromIntegral $ fromEnum SIGHASH_FORKID---- | SIGHASH_ANYONECANPAY as a byte.-sigHashAnyoneCanPay :: SigHash-sigHashAnyoneCanPay = fromIntegral $ fromEnum SIGHASH_ANYONECANPAY---- | Set SIGHASH_FORKID flag.-setForkIdFlag :: SigHash -> SigHash-setForkIdFlag = (.|. sigHashForkId)---- | Set SIGHASH_ANYONECANPAY flag.-setAnyoneCanPayFlag :: SigHash -> SigHash-setAnyoneCanPayFlag = (.|. sigHashAnyoneCanPay)---- | Is the SIGHASH_FORKID flag set?-hasForkIdFlag :: SigHash -> Bool-hasForkIdFlag = (/= 0) . (.&. sigHashForkId)---- | Is the SIGHASH_ANYONECANPAY flag set?-hasAnyoneCanPayFlag :: SigHash -> Bool-hasAnyoneCanPayFlag = (/= 0) . (.&. sigHashAnyoneCanPay)---- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_ALL'.-isSigHashAll :: SigHash -> Bool-isSigHashAll = (== sigHashAll) . (.&. 0x1f)---- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_NONE'.-isSigHashNone :: SigHash -> Bool-isSigHashNone = (== sigHashNone) . (.&. 0x1f)---- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_SINGLE'.-isSigHashSingle :: SigHash -> Bool-isSigHashSingle = (== sigHashSingle) . (.&. 0x1f)---- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_UNKNOWN'.-isSigHashUnknown :: SigHash -> Bool-isSigHashUnknown =-    (`notElem` [sigHashAll, sigHashNone, sigHashSingle]) . (.&. 0x1f)---- | Add a fork id to a 'SigHash'.-sigHashAddForkId :: SigHash -> Word32 -> SigHash-sigHashAddForkId sh w = (fromIntegral w `shiftL` 8) .|. (sh .&. 0x000000ff)---- | Add fork id of a particular network to a 'SigHash'.-sigHashAddNetworkId :: Network -> SigHash -> SigHash-sigHashAddNetworkId net =-    (`sigHashAddForkId` fromMaybe 0 (getSigHashForkId net))---- | Get fork id from 'SigHash'.-sigHashGetForkId :: SigHash -> Word32-sigHashGetForkId (SigHash n) = fromIntegral $ n `shiftR` 8---- | Computes the hash that will be used for signing a transaction.-txSigHash :: Network-          -> Tx      -- ^ transaction to sign-          -> Script  -- ^ script from output being spent-          -> Word64  -- ^ value of output being spent-          -> Int     -- ^ index of input being signed-          -> SigHash -- ^ what to sign-          -> Hash256 -- ^ hash to be signed-txSigHash net tx out v i sh-    | hasForkIdFlag sh && isJust (getSigHashForkId net) =-        txSigHashForkId net tx out v i sh-    | otherwise = do-        let newIn = buildInputs (txIn tx) fout i sh-        -- When SigSingle and input index > outputs, then sign integer 1-        fromMaybe one $ do-            newOut <- buildOutputs (txOut tx) i sh-            let newTx = Tx (txVersion tx) newIn newOut [] (txLockTime tx)-            return $-                doubleSHA256 $-                runPut $ do-                    put newTx-                    putWord32le $ fromIntegral sh-  where-    fout = Script $ filter (/= OP_CODESEPARATOR) $ scriptOps out-    one = "0100000000000000000000000000000000000000000000000000000000000000"---- | Build transaction inputs for computing sighashes.-buildInputs :: [TxIn] -> Script -> Int -> SigHash -> [TxIn]-buildInputs txins out i sh-    | hasAnyoneCanPayFlag sh =-        [ (txins !! i) { scriptInput = encode out } ]-    | isSigHashAll sh || isSigHashUnknown sh = single-    | otherwise = zipWith noSeq single [0 ..]-  where-    emptyIn = map (\ti -> ti { scriptInput = BS.empty }) txins-    single =-        updateIndex i emptyIn $ \ti -> ti { scriptInput = encode out }-    noSeq ti j =-        if i == j-        then ti-        else ti { txInSequence = 0 }---- | Build transaction outputs for computing sighashes.-buildOutputs :: [TxOut] -> Int -> SigHash -> Maybe [TxOut]-buildOutputs txos i sh-    | isSigHashAll sh || isSigHashUnknown sh = return txos-    | isSigHashNone sh = return []-    | i >= length txos = Nothing-    | otherwise = return $ buffer ++ [txos !! i]-  where-    buffer = replicate i $ TxOut maxBound BS.empty---- | Compute the hash that will be used for signing a transaction. This--- function is used when the 'SIGHASH_FORKID' flag is set.-txSigHashForkId-    :: Network-    -> Tx      -- ^ transaction to sign-    -> Script  -- ^ script from output being spent-    -> Word64  -- ^ value of output being spent-    -> Int     -- ^ index of input being signed-    -> SigHash -- ^ what to sign-    -> Hash256 -- ^ hash to be signed-txSigHashForkId net tx out v i sh =-    doubleSHA256 . runPut $ do-        putWord32le $ txVersion tx-        put hashPrevouts-        put hashSequence-        put $ prevOutput $ txIn tx !! i-        putScript out-        putWord64le v-        putWord32le $ txInSequence $ txIn tx !! i-        put hashOutputs-        putWord32le $ txLockTime tx-        putWord32le $ fromIntegral $ sigHashAddNetworkId net sh-  where-    hashPrevouts-        | not $ hasAnyoneCanPayFlag sh =-            doubleSHA256 $ runPut $ mapM_ (put . prevOutput) $ txIn tx-        | otherwise = zeros-    hashSequence-        | not (hasAnyoneCanPayFlag sh) &&-              not (isSigHashSingle sh) && not (isSigHashNone sh) =-            doubleSHA256 $ runPut $ mapM_ (putWord32le . txInSequence) $ txIn tx-        | otherwise = zeros-    hashOutputs-        | not (isSigHashSingle sh) && not (isSigHashNone sh) =-            doubleSHA256 $ runPut $ mapM_ put $ txOut tx-        | isSigHashSingle sh && i < length (txOut tx) =-            doubleSHA256 $ encode $ txOut tx !! i-        | otherwise = zeros-    putScript s = do-        let encodedScript = encode s-        putVarInt $ BS.length encodedScript-        putByteString encodedScript-    zeros :: Hash256-    zeros = "0000000000000000000000000000000000000000000000000000000000000000"---- | Data type representing a signature together with a 'SigHash'. The 'SigHash'--- is serialized as one byte at the end of an ECDSA 'Sig'. All signatures in--- transaction inputs are of type 'TxSignature'.-data TxSignature-    = TxSignature { txSignature        :: !Sig-                  , txSignatureSigHash :: !SigHash-                  }-    | TxSignatureEmpty-    deriving (Eq, Show, Generic)--instance NFData TxSignature---- | Serialize a 'TxSignature'.-encodeTxSig :: TxSignature -> ByteString-encodeTxSig TxSignatureEmpty = error "Can not encode an empty signature"-encodeTxSig (TxSignature sig (SigHash n)) =-    runPut $ putSig sig >> putWord8 (fromIntegral n)---- | Deserialize a 'TxSignature'.-decodeTxSig :: Network -> ByteString -> Either String TxSignature-decodeTxSig _   bs | BS.null bs = Left "Empty signature candidate"-decodeTxSig net bs =-    case decodeStrictSig $ BS.init bs of-        Just sig -> do-            let sh = fromIntegral $ BS.last bs-            when (isSigHashUnknown sh) $-                Left "Non-canonical signature: unknown hashtype byte"-            when (isNothing (getSigHashForkId net) && hasForkIdFlag sh) $-                Left "Non-canonical signature: invalid network for forkId"-            return $ TxSignature sig sh-        Nothing -> Left "Non-canonical signature: could not parse signature"
− src/Network/Haskoin/Script/Standard.hs
@@ -1,178 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Script.Standard-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Standard scripts like pay-to-public-key, pay-to-public-key-hash,-pay-to-script-hash, pay-to-multisig and corresponding SegWit variants.--}-module Network.Haskoin.Script.Standard-    ( ScriptInput(..)-    , SimpleInput(..)-    , RedeemScript-    , encodeInput-    , encodeInputBS-    , decodeInput-    , decodeInputBS-    , sortMulSig-    , scriptOpToInt-    , isSpendPK-    , isSpendPKHash-    , isSpendMulSig-    , isScriptHashInput-    ) where--import           Control.Applicative            ((<|>))-import           Control.DeepSeq-import           Control.Monad                  (guard, (<=<))-import           Data.ByteString                (ByteString)-import           Data.Function                  (on)-import           Data.List                      (sortBy)-import           Data.Serialize                 (decode, encode)-import           GHC.Generics                   (Generic)-import           Network.Haskoin.Constants-import           Network.Haskoin.Keys.Common-import           Network.Haskoin.Script.Common-import           Network.Haskoin.Script.SigHash-import           Network.Haskoin.Util----- | Data type describing standard transaction input scripts. Input scripts--- provide the signing data required to unlock the coins of the output they are--- trying to spend, except in pay-to-witness-public-key-hash and--- pay-to-script-hash transactions.-data SimpleInput-      -- | spend pay-to-public-key output-    = SpendPK { getInputSig :: !TxSignature-                  -- ^ transaction signature-               }-      -- | spend pay-to-public-key-hash output-    | SpendPKHash { getInputSig :: !TxSignature-                      -- ^ embedded signature-                  , getInputKey :: !PubKeyI-                      -- ^ public key-                   }-      -- | spend multisig output-    | SpendMulSig { getInputMulSigKeys :: ![TxSignature]-                      -- ^ list of signatures-                   }-    deriving (Eq, Show, Generic, NFData)---- | Returns true if the input script is spending from a pay-to-public-key--- output.-isSpendPK :: ScriptInput -> Bool-isSpendPK (RegularInput (SpendPK _)) = True-isSpendPK _                          = False---- | Returns true if the input script is spending from a pay-to-public-key-hash--- output.-isSpendPKHash :: ScriptInput -> Bool-isSpendPKHash (RegularInput (SpendPKHash _ _)) = True-isSpendPKHash _                                = False---- | Returns true if the input script is spending a multisig output.-isSpendMulSig :: ScriptInput -> Bool-isSpendMulSig (RegularInput (SpendMulSig _)) = True-isSpendMulSig _                              = False---- | Returns true if the input script is spending a pay-to-script-hash output.-isScriptHashInput :: ScriptInput -> Bool-isScriptHashInput (ScriptHashInput _ _) = True-isScriptHashInput _                     = False---- | A redeem script is the output script serialized into the spending input--- script. It must be included in inputs that spend pay-to-script-hash outputs.-type RedeemScript = ScriptOutput---- | Standard input script high-level representation.-data ScriptInput-    = RegularInput-          { getRegularInput :: !SimpleInput-            -- ^ get wrapped simple input-          }-    | ScriptHashInput-          { getScriptHashInput  :: !SimpleInput-            -- ^ get simple input associated with redeem script-          , getScriptHashRedeem :: !RedeemScript-            -- ^ redeem script-          }-    deriving (Eq, Show, Generic, NFData)---- | Heuristic to decode an input script into one of the standard types.-decodeSimpleInput :: Network -> Script -> Either String SimpleInput-decodeSimpleInput net (Script ops) =-    maybeToEither errMsg $ matchPK ops <|> matchPKHash ops <|> matchMulSig ops-  where-    matchPK [op] = SpendPK <$> f op-    matchPK _    = Nothing-    matchPKHash [op, OP_PUSHDATA pub _] =-        SpendPKHash <$> f op <*> eitherToMaybe (decode pub)-    matchPKHash _ = Nothing-    matchMulSig (x:xs) = do-        guard $ x == OP_0-        SpendMulSig <$> mapM f xs-    matchMulSig _ = Nothing-    f OP_0                    = return TxSignatureEmpty-    f (OP_PUSHDATA "" OPCODE) = f OP_0-    f (OP_PUSHDATA bs _)      = eitherToMaybe $ decodeTxSig net bs-    f _                       = Nothing-    errMsg = "decodeInput: Could not decode script input"---- | Heuristic to decode a 'ScriptInput' from a 'Script'. This function fails if--- the script can not be parsed as a standard script input.-decodeInput :: Network -> Script -> Either String ScriptInput-decodeInput net s@(Script ops) =-    maybeToEither errMsg $ matchSimpleInput <|> matchPayScriptHash-  where-    matchSimpleInput =-        RegularInput <$> eitherToMaybe (decodeSimpleInput net s)-    matchPayScriptHash =-        case splitAt (length (scriptOps s) - 1) ops of-            (is, [OP_PUSHDATA bs _]) -> do-                rdm <- eitherToMaybe $ decodeOutputBS bs-                inp <- eitherToMaybe $ decodeSimpleInput net $ Script is-                return $ ScriptHashInput inp rdm-            _ -> Nothing-    errMsg = "decodeInput: Could not decode script input"---- | Like 'decodeInput' but decodes directly from a serialized script--- 'ByteString'.-decodeInputBS :: Network -> ByteString -> Either String ScriptInput-decodeInputBS net = decodeInput net <=< decode---- | Encode a standard input into a script.-encodeInput :: ScriptInput -> Script-encodeInput s = case s of-    RegularInput ri -> encodeSimpleInput ri-    ScriptHashInput i o -> Script $-        scriptOps (encodeSimpleInput i) ++ [opPushData $ encodeOutputBS o]---- | Similar to 'encodeInput' but encodes directly to a serialized script--- 'ByteString'.-encodeInputBS :: ScriptInput -> ByteString-encodeInputBS = encode . encodeInput---- | Encode a standard 'SimpleInput' into opcodes as an input 'Script'.-encodeSimpleInput :: SimpleInput -> Script-encodeSimpleInput s =-    Script $-    case s of-        SpendPK ts       -> [f ts]-        SpendPKHash ts p -> [f ts, opPushData $ encode p]-        SpendMulSig xs   -> OP_0 : map f xs-  where-    f TxSignatureEmpty = OP_0-    f ts               = opPushData $ encodeTxSig ts---- | Sort the public keys of a multisig output in ascending order by comparing--- their compressed serialized representations. Refer to BIP-67.-sortMulSig :: ScriptOutput -> ScriptOutput-sortMulSig out = case out of-    PayMulSig keys r -> PayMulSig (sortBy (compare `on` encode) keys) r-    _ -> error "Can only call orderMulSig on PayMulSig scripts"
− src/Network/Haskoin/Test.hs
@@ -1,23 +0,0 @@-{-|-Module      : Network.Haskoin.Test-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Arbitrary instances for testing.--}-module Network.Haskoin.Test-    ( module X-    ) where--import           Network.Haskoin.Test.Address     as X-import           Network.Haskoin.Test.Block       as X-import           Network.Haskoin.Test.Crypto      as X-import           Network.Haskoin.Test.Keys        as X-import           Network.Haskoin.Test.Message     as X-import           Network.Haskoin.Test.Network     as X-import           Network.Haskoin.Test.Script      as X-import           Network.Haskoin.Test.Transaction as X-import           Network.Haskoin.Test.Util        as X
− src/Network/Haskoin/Test/Address.hs
@@ -1,25 +0,0 @@-{-|-Module      : Network.Haskoin.Test.Address-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}-module Network.Haskoin.Test.Address where--import           Network.Haskoin.Address-import           Network.Haskoin.Test.Crypto-import           Test.QuickCheck---- | Arbitrary pay-to-public-key-hash or pay-to-script-hash address.-arbitraryAddress :: Gen Address-arbitraryAddress = oneof [arbitraryPubKeyAddress, arbitraryScriptAddress]---- | Arbitrary pay-to-public-key-hash address.-arbitraryPubKeyAddress :: Gen Address-arbitraryPubKeyAddress = p2pkhAddr <$> arbitraryHash160---- | Arbitrary pay-to-script-hash address.-arbitraryScriptAddress :: Gen Address-arbitraryScriptAddress = p2shAddr <$> arbitraryHash160
− src/Network/Haskoin/Test/Block.hs
@@ -1,69 +0,0 @@-{-|-Module      : Network.Haskoin.Test.Block-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}-module Network.Haskoin.Test.Block where--import           Network.Haskoin.Block.Merkle-import           Network.Haskoin.Block.Common-import           Network.Haskoin.Constants-import           Network.Haskoin.Test.Crypto-import           Network.Haskoin.Test.Network-import           Network.Haskoin.Test.Transaction-import           Test.QuickCheck---- | Block full or arbitrary transactions.-arbitraryBlock :: Network -> Gen Block-arbitraryBlock net = do-    h   <- arbitraryBlockHeader-    c   <- choose (0,10)-    txs <- vectorOf c (arbitraryTx net)-    return $ Block h txs---- | Block header with random hash.-arbitraryBlockHeader :: Gen BlockHeader-arbitraryBlockHeader =-    BlockHeader <$> arbitrary-                      <*> arbitraryBlockHash-                      <*> arbitraryHash256-                      <*> arbitrary-                      <*> arbitrary-                      <*> arbitrary---- | Arbitrary block hash.-arbitraryBlockHash :: Gen BlockHash-arbitraryBlockHash = BlockHash <$> arbitraryHash256---- | Arbitrary 'GetBlocks' object with at least one block hash.-arbitraryGetBlocks :: Gen GetBlocks-arbitraryGetBlocks =-    GetBlocks <$> arbitrary-              <*> listOf1 arbitraryBlockHash-              <*> arbitraryBlockHash---- | Arbitrary 'GetHeaders' object with at least one block header.-arbitraryGetHeaders :: Gen GetHeaders-arbitraryGetHeaders =-    GetHeaders <$> arbitrary-               <*> listOf1 arbitraryBlockHash-               <*> arbitraryBlockHash---- | Arbitrary 'Headers' object with at least one block header.-arbitraryHeaders :: Gen Headers-arbitraryHeaders =-    Headers <$> listOf1 ((,) <$> arbitraryBlockHeader <*> arbitraryVarInt)---- | Arbitrary 'MerkleBlock' with at least one hash.-arbitraryMerkleBlock :: Gen MerkleBlock-arbitraryMerkleBlock = do-    bh     <- arbitraryBlockHeader-    ntx    <- arbitrary-    hashes <- listOf1 arbitraryHash256-    c      <- choose (1,10)-    flags  <- vectorOf (c*8) arbitrary-    return $ MerkleBlock bh ntx hashes flags-
− src/Network/Haskoin/Test/Crypto.hs
@@ -1,33 +0,0 @@-{-|-Module      : Network.Haskoin.Test.Crypto-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}-module Network.Haskoin.Test.Crypto where--import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Test.Util-import           Test.QuickCheck---- | Arbitrary 160-bit hash.-arbitraryHash160 :: Gen Hash160-arbitraryHash160 =-    ripemd160 <$> arbitraryBSn 20---- | Arbitrary 256-bit hash.-arbitraryHash256 :: Gen Hash256-arbitraryHash256 =-    sha256 <$> arbitraryBSn 32---- | Arbitrary 512-bit hash.-arbitraryHash512 :: Gen Hash512-arbitraryHash512 =-    sha512 <$> arbitraryBSn 64---- | Arbitrary 32-bit checksum.-arbitraryCheckSum32 :: Gen CheckSum32-arbitraryCheckSum32 =-    checkSum32 <$> arbitraryBSn 4
− src/Network/Haskoin/Test/Keys.hs
@@ -1,85 +0,0 @@-{-|-Module      : Network.Haskoin.Test.Keys-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}-module Network.Haskoin.Test.Keys where--import           Data.Bits                     (clearBit)-import           Data.List                     (foldl')-import           Data.Word                     (Word32)-import           Network.Haskoin.Crypto-import           Network.Haskoin.Keys.Common-import           Network.Haskoin.Keys.Extended-import           Network.Haskoin.Test.Crypto-import           Test.QuickCheck---- | Arbitrary private key with arbitrary compressed flag.-arbitrarySecKeyI :: Gen SecKeyI-arbitrarySecKeyI = wrapSecKey <$> arbitrary <*> arbitrary---- | Arbitrary keypair, both either compressed or not.-arbitraryKeyPair :: Gen (SecKeyI, PubKeyI)-arbitraryKeyPair = do-    k <- arbitrarySecKeyI-    return (k, derivePubKeyI k)---- | Arbitrary extended private key.-arbitraryXPrvKey :: Gen XPrvKey-arbitraryXPrvKey =-    XPrvKey <$> arbitrary-            <*> arbitrary-            <*> arbitrary-            <*> arbitraryHash256-            <*> arbitrary---- | Arbitrary extended public key with its corresponding private key.-arbitraryXPubKey :: Gen (XPrvKey, XPubKey)-arbitraryXPubKey = (\k -> (k, deriveXPubKey k)) <$> arbitraryXPrvKey--{- Custom derivations -}---- | Arbitrary derivation index with last bit unset.-genIndex :: Gen Word32-genIndex = (`clearBit` 31) <$> arbitrary---- | Arbitrary BIP-32 path index. Can be hardened or not.-arbitraryBip32PathIndex :: Gen Bip32PathIndex-arbitraryBip32PathIndex =-    oneof [ Bip32SoftIndex <$> genIndex-          , Bip32HardIndex <$> genIndex-          ]---- | Arbitrary BIP-32 derivation path composed of only hardened derivations.-arbitraryHardPath :: Gen HardPath-arbitraryHardPath = foldl' (:|) Deriv <$> listOf genIndex---- | Arbitrary BIP-32 derivation path composed of only non-hardened derivations.-arbitrarySoftPath :: Gen SoftPath-arbitrarySoftPath = foldl' (:/) Deriv <$> listOf genIndex---- | Arbitrary derivation path composed of hardened and non-hardened derivations.-arbitraryDerivPath :: Gen DerivPath-arbitraryDerivPath = concatBip32Segments <$> listOf arbitraryBip32PathIndex---- | Arbitrary parsed derivation path. Can contain 'ParsedPrv', 'ParsedPub' or--- 'ParsedEmpty' elements.-arbitraryParsedPath :: Gen ParsedPath-arbitraryParsedPath =-    oneof [ ParsedPrv <$> arbitraryDerivPath-          , ParsedPub <$> arbitraryDerivPath-          , ParsedEmpty <$> arbitraryDerivPath-          ]---- | Arbitrary message hash, private key, nonce and corresponding signature. The--- signature is generated with a random message, random private key and a random--- nonce.-arbitrarySignature :: Gen (Hash256, SecKey, Sig)-arbitrarySignature = do-    m <- arbitraryHash256-    key <- arbitrary-    let sig = signHash key m-    return (m, key, sig)
− src/Network/Haskoin/Test/Message.hs
@@ -1,52 +0,0 @@-{-|-Module      : Network.Haskoin.Test.Message-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}-module Network.Haskoin.Test.Message where--import           Network.Haskoin.Constants-import           Network.Haskoin.Network.Message-import           Network.Haskoin.Test.Block-import           Network.Haskoin.Test.Crypto-import           Network.Haskoin.Test.Network-import           Network.Haskoin.Test.Transaction-import           Test.QuickCheck---- | Arbitrary 'MessageHeader'.-arbitraryMessageHeader :: Gen MessageHeader-arbitraryMessageHeader =-    MessageHeader <$> arbitrary-                  <*> arbitraryMessageCommand-                  <*> arbitrary-                  <*> arbitraryCheckSum32---- | Arbitrary 'Message'.-arbitraryMessage :: Network -> Gen Message-arbitraryMessage net =-    oneof-        [ MVersion <$> arbitraryVersion-        , return MVerAck-        , MAddr <$> arbitraryAddr1-        , MInv <$> arbitraryInv1-        , MGetData <$> arbitraryGetData-        , MNotFound <$> arbitraryNotFound-        , MGetBlocks <$> arbitraryGetBlocks-        , MGetHeaders <$> arbitraryGetHeaders-        , MTx <$> arbitraryTx net-        , MBlock <$> arbitraryBlock net-        , MMerkleBlock <$> arbitraryMerkleBlock-        , MHeaders <$> arbitraryHeaders-        , return MGetAddr-        , MFilterLoad <$> arbitraryFilterLoad-        , MFilterAdd <$> arbitraryFilterAdd-        , return MFilterClear-        , MPing <$> arbitraryPing-        , MPong <$> arbitraryPong-        , MAlert <$> arbitraryAlert-        , MReject <$> arbitraryReject-        , return MSendHeaders-        ]
− src/Network/Haskoin/Test/Network.hs
@@ -1,178 +0,0 @@-{-|-Module      : Network.Haskoin.Test.Network-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}-module Network.Haskoin.Test.Network where--import qualified Data.ByteString             as BS (empty, pack)-import qualified Data.ByteString.Char8       as C8-import           Data.Word                   (Word16, Word32)-import           Network.Haskoin.Network-import           Network.Haskoin.Test.Crypto-import           Network.Haskoin.Test.Util-import           Network.Socket              (SockAddr (..))-import           Test.QuickCheck---- | Arbitrary 'VarInt'.-arbitraryVarInt :: Gen VarInt-arbitraryVarInt = VarInt <$> arbitrary---- | Arbitrary 'VarString'.-arbitraryVarString :: Gen VarString-arbitraryVarString = VarString <$> arbitraryBS---- | Arbitrary 'NetworkAddress'.-arbitraryNetworkAddress :: Gen NetworkAddress-arbitraryNetworkAddress = do-    s <- arbitrary-    a <- arbitrary-    p <- arbitrary-    d <- oneof-        [ do-            b <- arbitrary-            c <- arbitrary-            d <- arbitrary-            return $ SockAddrInet6 (fromIntegral p) 0 (a,b,c,d) 0-        , return $ SockAddrInet (fromIntegral (p :: Word16)) a-        ]-    let n = sockToHostAddress d-    return $ NetworkAddress s n---- | Arbitrary 'NetworkAddressTime'.-arbitraryNetworkAddressTime :: Gen (Word32, NetworkAddress)-arbitraryNetworkAddressTime = (,) <$> arbitrary <*> arbitraryNetworkAddress---- | Arbitrary 'InvType'.-arbitraryInvType :: Gen InvType-arbitraryInvType = elements [InvError, InvTx, InvBlock, InvMerkleBlock]---- | Arbitrary 'InvVector'.-arbitraryInvVector :: Gen InvVector-arbitraryInvVector = InvVector <$> arbitraryInvType <*> arbitraryHash256---- | Arbitrary non-empty 'Inv'.-arbitraryInv1 :: Gen Inv-arbitraryInv1 = Inv <$> listOf1 arbitraryInvVector---- | Arbitrary 'Version'.-arbitraryVersion :: Gen Version-arbitraryVersion =-    Version <$> arbitrary-            <*> arbitrary-            <*> arbitrary-            <*> arbitraryNetworkAddress-            <*> arbitraryNetworkAddress-            <*> arbitrary-            <*> arbitraryVarString-            <*> arbitrary-            <*> arbitrary---- | Arbitrary non-empty 'Addr'.-arbitraryAddr1 :: Gen Addr-arbitraryAddr1 = Addr <$> listOf1 arbitraryNetworkAddressTime---- | Arbitrary 'Alert' with random payload and signature. Signature is not--- valid.-arbitraryAlert :: Gen Alert-arbitraryAlert = Alert <$> arbitraryVarString <*> arbitraryVarString---- | Arbitrary 'Reject'.-arbitraryReject :: Gen Reject-arbitraryReject = do-    m <- arbitraryMessageCommand-    c <- arbitraryRejectCode-    s <- arbitraryVarString-    d <- oneof [ return BS.empty-               , BS.pack <$> vectorOf 32 arbitrary-               ]-    return $ Reject m c s d---- | Arbitrary 'RejectCode'.-arbitraryRejectCode :: Gen RejectCode-arbitraryRejectCode =-    elements-        [ RejectMalformed-        , RejectInvalid-        , RejectInvalid-        , RejectDuplicate-        , RejectNonStandard-        , RejectDust-        , RejectInsufficientFee-        , RejectCheckpoint-        ]---- | Arbitrary non-empty 'GetData'.-arbitraryGetData :: Gen GetData-arbitraryGetData = GetData <$> listOf1 arbitraryInvVector---- | Arbitrary 'NotFound'.-arbitraryNotFound :: Gen NotFound-arbitraryNotFound = NotFound <$> listOf1 arbitraryInvVector---- | Arbitrary 'Ping'.-arbitraryPing :: Gen Ping-arbitraryPing = Ping <$> arbitrary---- | Arbitrary 'Pong'.-arbitraryPong :: Gen Pong-arbitraryPong = Pong <$> arbitrary---- | Arbitrary bloom filter flags.-arbitraryBloomFlags :: Gen BloomFlags-arbitraryBloomFlags =-    elements-        [ BloomUpdateNone-        , BloomUpdateAll-        , BloomUpdateP2PubKeyOnly-        ]---- | Arbitrary bloom filter with its corresponding number of elements--- and false positive rate.-arbitraryBloomFilter :: Gen (Int, Double, BloomFilter)-arbitraryBloomFilter = do-    n     <- choose (0,100000)-    fp    <- choose (1e-8,1)-    tweak <- arbitrary-    fl    <- arbitraryBloomFlags-    return (n, fp, bloomCreate n fp tweak fl)---- | Arbitrary 'FilterLoad'.-arbitraryFilterLoad :: Gen FilterLoad-arbitraryFilterLoad = do-    (_, _, bf) <- arbitraryBloomFilter-    return $ FilterLoad bf---- | Arbitrary 'FilterAdd'.-arbitraryFilterAdd :: Gen FilterAdd-arbitraryFilterAdd = FilterAdd <$> arbitraryBS---- | Arbitrary 'MessageCommand'.-arbitraryMessageCommand :: Gen MessageCommand-arbitraryMessageCommand = do-    ASCIIString str <- arbitrary-    elements-        [ MCVersion-        , MCVerAck-        , MCAddr-        , MCInv-        , MCGetData-        , MCNotFound-        , MCGetBlocks-        , MCGetHeaders-        , MCTx-        , MCBlock-        , MCMerkleBlock-        , MCHeaders-        , MCGetAddr-        , MCFilterLoad-        , MCFilterAdd-        , MCFilterClear-        , MCPing-        , MCPong-        , MCAlert-        , MCOther (C8.take 12 (C8.pack (filter (/= '\NUL') str)))-        ]
− src/Network/Haskoin/Test/Script.hs
@@ -1,374 +0,0 @@-{-# LANGUAGE LambdaCase #-}-{-|-Module      : Network.Haskoin.Test.Script-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}-module Network.Haskoin.Test.Script where--import           Crypto.Secp256k1-import           Data.Maybe-import           Data.Word-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Keys.Common-import           Network.Haskoin.Script-import           Network.Haskoin.Test.Address-import           Network.Haskoin.Test.Crypto-import           Network.Haskoin.Test.Keys-import           Network.Haskoin.Test.Util-import           Network.Haskoin.Transaction.Common-import           Network.Haskoin.Util-import           Test.QuickCheck---- | Arbitrary 'Script' with random script ops.-arbitraryScript :: Gen Script-arbitraryScript = Script <$> listOf arbitraryScriptOp---- | Arbitrary 'ScriptOp' (push operations have random data).-arbitraryScriptOp :: Gen ScriptOp-arbitraryScriptOp =-    oneof-          -- Pushing Data-        [ opPushData <$> arbitraryBS1-        , return OP_0-        , return OP_1NEGATE-        , return OP_RESERVED-        , return OP_1-        , return OP_2-        , return OP_3-        , return OP_4-        , return OP_5-        , return OP_6-        , return OP_7-        , return OP_8-        , return OP_9-        , return OP_10-        , return OP_11-        , return OP_12-        , return OP_13-        , return OP_14-        , return OP_15-        , return OP_16-        -- Flow control-        , return OP_NOP-        , return OP_VER-        , return OP_IF-        , return OP_NOTIF-        , return OP_VERIF-        , return OP_VERNOTIF-        , return OP_ELSE-        , return OP_ENDIF-        , return OP_VERIFY-        , return OP_RETURN-        -- Stack operations-        , return OP_TOALTSTACK-        , return OP_FROMALTSTACK-        , return OP_IFDUP-        , return OP_DEPTH-        , return OP_DROP-        , return OP_DUP-        , return OP_NIP-        , return OP_OVER-        , return OP_PICK-        , return OP_ROLL-        , return OP_ROT-        , return OP_SWAP-        , return OP_TUCK-        , return OP_2DROP-        , return OP_2DUP-        , return OP_3DUP-        , return OP_2OVER-        , return OP_2ROT-        , return OP_2SWAP-        -- Splice-        , return OP_CAT-        , return OP_SUBSTR-        , return OP_LEFT-        , return OP_RIGHT-        , return OP_SIZE-        -- Bitwise logic-        , return OP_INVERT-        , return OP_AND-        , return OP_OR-        , return OP_XOR-        , return OP_EQUAL-        , return OP_EQUALVERIFY-        , return OP_RESERVED1-        , return OP_RESERVED2-        -- Arithmetic-        , return OP_1ADD-        , return OP_1SUB-        , return OP_2MUL-        , return OP_2DIV-        , return OP_NEGATE-        , return OP_ABS-        , return OP_NOT-        , return OP_0NOTEQUAL-        , return OP_ADD-        , return OP_SUB-        , return OP_MUL-        , return OP_DIV-        , return OP_MOD-        , return OP_LSHIFT-        , return OP_RSHIFT-        , return OP_BOOLAND-        , return OP_BOOLOR-        , return OP_NUMEQUAL-        , return OP_NUMEQUALVERIFY-        , return OP_NUMNOTEQUAL-        , return OP_LESSTHAN-        , return OP_GREATERTHAN-        , return OP_LESSTHANOREQUAL-        , return OP_GREATERTHANOREQUAL-        , return OP_MIN-        , return OP_MAX-        , return OP_WITHIN-        -- Crypto-        , return OP_RIPEMD160-        , return OP_SHA1-        , return OP_SHA256-        , return OP_HASH160-        , return OP_HASH256-        , return OP_CODESEPARATOR-        , return OP_CHECKSIG-        , return OP_CHECKSIGVERIFY-        , return OP_CHECKMULTISIG-        , return OP_CHECKMULTISIGVERIFY-        -- Expansion-        , return OP_NOP1-        , return OP_NOP2-        , return OP_NOP3-        , return OP_NOP4-        , return OP_NOP5-        , return OP_NOP6-        , return OP_NOP7-        , return OP_NOP8-        , return OP_NOP9-        , return OP_NOP10-        -- Other-        , return OP_PUBKEYHASH-        , return OP_PUBKEY-        , return $ OP_INVALIDOPCODE 0xff-        ]---- | Arbtirary 'ScriptOp' with a value in @[OP_1 .. OP_16]@.-arbitraryIntScriptOp :: Gen ScriptOp-arbitraryIntScriptOp =-    elements-        [ OP_1,  OP_2,  OP_3,  OP_4-        , OP_5,  OP_6,  OP_7,  OP_8-        , OP_9,  OP_10, OP_11, OP_12-        , OP_13, OP_14, OP_15, OP_16-        ]---- | Arbitrary 'PushDataType'.-arbitraryPushDataType :: Gen PushDataType-arbitraryPushDataType = elements [OPCODE, OPDATA1, OPDATA2, OPDATA4]---- | Arbitrary 'SigHash' (including invalid/unknown sighash codes).-arbitrarySigHash :: Gen SigHash-arbitrarySigHash = fromIntegral <$> (arbitrary :: Gen Word32)---- | Arbitrary valid 'SigHash'.-arbitraryValidSigHash :: Network -> Gen SigHash-arbitraryValidSigHash net = do-    sh <- elements [sigHashAll, sigHashNone, sigHashSingle]-    f1 <--        elements $-        if isJust (getSigHashForkId net)-            then [id, setForkIdFlag]-            else [id]-    f2 <- elements [id, setAnyoneCanPayFlag]-    return $ f1 $ f2 sh---- | Arbitrary message hash, private key and corresponding 'TxSignature'. The--- signature is generated deterministically using a random message and a random--- private key.-arbitraryTxSignature :: Network -> Gen (TxHash, SecKey, TxSignature)-arbitraryTxSignature net = do-    (m, key, sig) <- arbitrarySignature-    sh <- (fromIntegral <$> (arbitrary :: Gen Word8)) `suchThat` filterBad-    let txsig = TxSignature sig sh-    return (TxHash m, key, txsig)-  where-    filterBad sh = not $-        isSigHashUnknown sh ||-        isNothing (getSigHashForkId net) && hasForkIdFlag sh---- | Arbitrary transaction signature that could also be empty.-arbitraryTxSignatureEmpty :: Network -> Gen TxSignature-arbitraryTxSignatureEmpty net =-    frequency [ (1, return TxSignatureEmpty)-              , (10, lst3 <$> arbitraryTxSignature net)-              ]---- | Arbitrary m of n parameters.-arbitraryMSParam :: Gen (Int, Int)-arbitraryMSParam = do-    m <- choose (1,16)-    n <- choose (m,16)-    return (m, n)---- | Arbitrary 'ScriptOutput' (Can by any valid type).-arbitraryScriptOutput :: Network -> Gen ScriptOutput-arbitraryScriptOutput net =-    oneof $-    [ arbitraryPKOutput-    , arbitraryPKHashOutput-    , arbitraryMSOutput-    , arbitrarySHOutput-    , arbitraryDCOutput-    ] ++-    if getSegWit net-        then [arbitraryWPKHashOutput, arbitraryWSHOutput]-        else []---- | Arbitrary 'ScriptOutput' of type 'PayPK', 'PayPKHash' or 'PayMS'--- (Not 'PayScriptHash', 'DataCarrier', or SegWit)-arbitrarySimpleOutput :: Gen ScriptOutput-arbitrarySimpleOutput =-    oneof-        [ arbitraryPKOutput-        , arbitraryPKHashOutput-        , arbitraryMSOutput-        ]---- | Arbitrary 'ScriptOutput' of type 'PayPK'-arbitraryPKOutput :: Gen ScriptOutput-arbitraryPKOutput =  PayPK . snd <$> arbitraryKeyPair---- | Arbitrary 'ScriptOutput' of type 'PayPKHash'-arbitraryPKHashOutput :: Gen ScriptOutput-arbitraryPKHashOutput = PayPKHash <$> arbitraryHash160---- | Arbitrary 'PayWitnessPKHash' output.-arbitraryWPKHashOutput :: Gen ScriptOutput-arbitraryWPKHashOutput = PayWitnessPKHash <$> arbitraryHash160---- | Arbitrary 'PayWitnessScriptHash' output.-arbitraryWSHOutput :: Gen ScriptOutput-arbitraryWSHOutput = PayWitnessScriptHash <$> arbitraryHash256---- | Arbitrary 'ScriptOutput' of type 'PayMS'.-arbitraryMSOutput :: Gen ScriptOutput-arbitraryMSOutput = do-    (m, n) <- arbitraryMSParam-    keys <- map snd <$> vectorOf n arbitraryKeyPair-    return $ PayMulSig keys m---- | Arbitrary 'ScriptOutput' of type 'PayMS', only using compressed keys.-arbitraryMSOutputC :: Gen ScriptOutput-arbitraryMSOutputC = do-    (m, n) <- arbitraryMSParam-    keys <--        map snd <$>-        vectorOf n (arbitraryKeyPair `suchThat` (pubKeyCompressed . snd))-    return $ PayMulSig keys m---- | Arbitrary 'ScriptOutput' of type 'PayScriptHash'.-arbitrarySHOutput :: Gen ScriptOutput-arbitrarySHOutput = PayScriptHash . getAddrHash160 <$> arbitraryScriptAddress---- | Arbitrary 'ScriptOutput' of type 'DataCarrier'.-arbitraryDCOutput :: Gen ScriptOutput-arbitraryDCOutput = DataCarrier <$> arbitraryBS1---- | Arbitrary 'ScriptInput'.-arbitraryScriptInput :: Network -> Gen ScriptInput-arbitraryScriptInput net =-    oneof-        [ arbitraryPKInput net-        , arbitraryPKHashInput net-        , arbitraryMSInput net-        , arbitrarySHInput net-        ]---- | Arbitrary 'ScriptInput' of type 'SpendPK', 'SpendPKHash' or 'SpendMulSig'--- (not 'ScriptHashInput')-arbitrarySimpleInput :: Network -> Gen ScriptInput-arbitrarySimpleInput net =-    oneof-        [ arbitraryPKInput net-        , arbitraryPKHashInput net-        , arbitraryMSInput net-        ]---- | Arbitrary 'ScriptInput' of type 'SpendPK'.-arbitraryPKInput :: Network -> Gen ScriptInput-arbitraryPKInput net = RegularInput . SpendPK <$> arbitraryTxSignatureEmpty net---- | Arbitrary 'ScriptInput' of type 'SpendPK'.-arbitraryPKHashInput :: Network -> Gen ScriptInput-arbitraryPKHashInput net = do-    sig <- arbitraryTxSignatureEmpty net-    key <- snd <$> arbitraryKeyPair-    return $ RegularInput $ SpendPKHash sig key---- | Like 'arbitraryPKHashInput' without empty signatures.-arbitraryPKHashInputFull :: Network -> Gen ScriptInput-arbitraryPKHashInputFull net = do-    sig <- lst3 <$> arbitraryTxSignature net-    key <- snd <$> arbitraryKeyPair-    return $ RegularInput $ SpendPKHash sig key---- | Like above but only compressed.-arbitraryPKHashInputFullC :: Network -> Gen ScriptInput-arbitraryPKHashInputFullC net = do-    sig <- lst3 <$> arbitraryTxSignature net-    key <- fmap snd $ arbitraryKeyPair `suchThat` (pubKeyCompressed . snd)-    return $ RegularInput $ SpendPKHash sig key---- | Arbitrary 'ScriptInput' of type 'SpendMulSig'.-arbitraryMSInput :: Network -> Gen ScriptInput-arbitraryMSInput net = do-    m    <- fst <$> arbitraryMSParam-    sigs <- vectorOf m (arbitraryTxSignatureEmpty net)-    return $ RegularInput $ SpendMulSig sigs---- | Arbitrary 'ScriptInput' of type 'ScriptHashInput'.-arbitrarySHInput :: Network -> Gen ScriptInput-arbitrarySHInput net = do-    i <- arbitrarySimpleInput net-    ScriptHashInput (getRegularInput i) <$> arbitrarySimpleOutput---- | Arbitrary 'ScriptInput' of type 'ScriptHashInput' containing a--- 'RedeemScript' of type 'PayMulSig' and an input of type 'SpendMulSig'.-arbitraryMulSigSHInput :: Network -> Gen ScriptInput-arbitraryMulSigSHInput net =-    arbitraryMSOutput >>= \case-        rdm@(PayMulSig _ m) -> do-            sigs <- vectorOf m (arbitraryTxSignatureEmpty net)-            return $ ScriptHashInput (SpendMulSig sigs) rdm-        _ -> undefined---- | Arbitrary 'ScriptInput' of type 'ScriptHashInput' containing a--- 'RedeemScript' of type 'PayMulSig' and an input of type 'SpendMulSig'.-arbitraryMulSigSHInputC :: Network -> Gen ScriptInput-arbitraryMulSigSHInputC net =-    arbitraryMSOutputC >>= \case-        rdm@(PayMulSig _ m) -> do-          sigs <- vectorOf m (arbitraryTxSignatureEmpty net)-          return $ ScriptHashInput (SpendMulSig sigs) rdm-        _ -> undefined---- | Like 'arbitraryMulSigSHCInput' with no empty signatures.-arbitraryMulSigSHInputFull :: Network -> Gen ScriptInput-arbitraryMulSigSHInputFull net =-    arbitraryMSOutput >>= \case-        rdm@(PayMulSig _ m) -> do-            sigs <- map lst3 <$> vectorOf m (arbitraryTxSignature net)-            return $ ScriptHashInput (SpendMulSig sigs) rdm-        _ -> undefined---- | Like 'arbitraryMulSigSHCInput' with no empty signatures.-arbitraryMulSigSHInputFullC :: Network -> Gen ScriptInput-arbitraryMulSigSHInputFullC net =-    arbitraryMSOutputC >>= \case-        rdm@(PayMulSig _ m) -> do-            sigs <- map lst3 <$> vectorOf m (arbitraryTxSignature net)-            return $ ScriptHashInput (SpendMulSig sigs) rdm-        _ -> undefined
− src/Network/Haskoin/Test/Transaction.hs
@@ -1,275 +0,0 @@-{-|-Module      : Network.Haskoin.Test.Transaction-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}-module Network.Haskoin.Test.Transaction where--import           Control.Monad-import qualified Data.ByteString             as BS-import           Data.Either                 (fromRight)-import           Data.List                   (nub, nubBy, permutations)-import           Data.Word                   (Word64)-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Keys.Common-import           Network.Haskoin.Script-import           Network.Haskoin.Test.Crypto-import           Network.Haskoin.Test.Keys-import           Network.Haskoin.Test.Script-import           Network.Haskoin.Test.Util-import           Network.Haskoin.Transaction-import           Test.QuickCheck---- | Wrapped coin value for testing.-newtype TestCoin = TestCoin { getTestCoin :: Word64 }-    deriving (Eq, Show)--instance Coin TestCoin where-    coinValue = getTestCoin---- | Arbitrary transaction hash (for non-existent transaction).-arbitraryTxHash :: Gen TxHash-arbitraryTxHash = TxHash <$> arbitraryHash256---- | Arbitrary amount of Satoshi as 'Word64' (Between 1 and 21e14)-arbitrarySatoshi :: Network -> Gen TestCoin-arbitrarySatoshi net = TestCoin <$> choose (1, getMaxSatoshi net)---- | Arbitrary 'OutPoint'.-arbitraryOutPoint :: Gen OutPoint-arbitraryOutPoint = OutPoint <$> arbitraryTxHash <*> arbitrary---- | Arbitrary 'TxOut'.-arbitraryTxOut :: Network -> Gen TxOut-arbitraryTxOut net =-    TxOut <$> (getTestCoin <$> arbitrarySatoshi net)-          <*> (encodeOutputBS <$> arbitraryScriptOutput net)---- | Arbitrary 'TxIn'.-arbitraryTxIn :: Network -> Gen TxIn-arbitraryTxIn net =-    TxIn <$> arbitraryOutPoint-         <*> (encodeInputBS <$> arbitraryScriptInput net)-         <*> arbitrary---- | Arbitrary transaction. Can be regular or with witnesses.-arbitraryTx :: Network -> Gen Tx-arbitraryTx net = oneof [arbitraryLegacyTx net, arbitraryWitnessTx net]---- | Arbitrary regular transaction.-arbitraryLegacyTx :: Network -> Gen Tx-arbitraryLegacyTx net = arbitraryWLTx net False---- | Arbitrary witness transaction (witness data is fake).-arbitraryWitnessTx :: Network -> Gen Tx-arbitraryWitnessTx net = arbitraryWLTx net True---- | Arbitrary witness or legacy transaction.-arbitraryWLTx :: Network -> Bool -> Gen Tx-arbitraryWLTx net wit = do-    ni <- choose (0, 5)-    no <--        if wit-            then choose (0, 5)-            else choose-                     ( if ni == 0-                           then 2-                           else 0-                     , 5 -- avoid witness case-                     )-    inps <- vectorOf ni (arbitraryTxIn net)-    outs <- vectorOf no (arbitraryTxOut net)-    let uniqueInps = nubBy (\a b -> prevOutput a == prevOutput b) inps-    w <- if wit then vectorOf (length uniqueInps) (listOf arbitraryBS) else return []-    Tx <$> arbitrary <*> pure uniqueInps <*> pure outs <*> pure w <*> arbitrary---- | Arbitrary transaction containing only inputs of type 'SpendPKHash',--- 'SpendScriptHash' (multisig) and outputs of type 'PayPKHash' and 'PaySH'.--- Only compressed public keys are used.-arbitraryAddrOnlyTx :: Network -> Gen Tx-arbitraryAddrOnlyTx net = do-    ni <- choose (0, 5)-    no <- choose (0, 5)-    inps <- vectorOf ni (arbitraryAddrOnlyTxIn net)-    outs <- vectorOf no (arbitraryAddrOnlyTxOut net)-    Tx <$> arbitrary <*> pure inps <*> pure outs <*> pure [] <*> arbitrary---- | Like 'arbitraryAddrOnlyTx' without empty signatures in the inputs.-arbitraryAddrOnlyTxFull :: Network -> Gen Tx-arbitraryAddrOnlyTxFull net = do-    ni <- choose (0, 5)-    no <- choose (0, 5)-    inps <- vectorOf ni (arbitraryAddrOnlyTxInFull net)-    outs <- vectorOf no (arbitraryAddrOnlyTxOut net)-    Tx <$> arbitrary <*> pure inps <*> pure outs <*> pure [] <*> arbitrary---- | Arbitrary TxIn that can only be of type 'SpendPKHash' or 'SpendScriptHash'--- (multisig). Only compressed public keys are used.-arbitraryAddrOnlyTxIn :: Network -> Gen TxIn-arbitraryAddrOnlyTxIn net = do-    inp <- oneof [arbitraryPKHashInput net, arbitraryMulSigSHInput net]-    TxIn <$> arbitraryOutPoint <*> pure (encodeInputBS inp) <*> arbitrary---- | like 'arbitraryAddrOnlyTxIn' with no empty signatures.-arbitraryAddrOnlyTxInFull :: Network -> Gen TxIn-arbitraryAddrOnlyTxInFull net = do-    inp <--        oneof [arbitraryPKHashInputFullC net, arbitraryMulSigSHInputFullC net]-    TxIn <$> arbitraryOutPoint <*> pure (encodeInputBS inp) <*> arbitrary---- | Arbitrary 'TxOut' that can only be of type 'PayPKHash' or 'PaySH'.-arbitraryAddrOnlyTxOut :: Network -> Gen TxOut-arbitraryAddrOnlyTxOut net = do-    v <- getTestCoin <$> arbitrarySatoshi net-    out <- oneof [arbitraryPKHashOutput, arbitrarySHOutput]-    return $ TxOut v $ encodeOutputBS out---- | Arbitrary 'SigInput' with the corresponding private keys used--- to generate the 'ScriptOutput' or 'RedeemScript'.-arbitrarySigInput :: Network -> Gen (SigInput, [SecKeyI])-arbitrarySigInput net =-    oneof-        [ wrapKey <$> arbitraryPKSigInput net-        , wrapKey <$> arbitraryPKHashSigInput net-        , arbitraryMSSigInput net-        , arbitrarySHSigInput net-        , wrapKey <$> arbitraryWPKHSigInput net-        , arbitraryWSHSigInput net-        ]---- | Arbitrary 'SigInput' with a 'ScriptOutput' of type 'PayPK'.-arbitraryPKSigInput :: Network -> Gen (SigInput, SecKeyI)-arbitraryPKSigInput net = arbitraryAnyInput net False---- | Arbitrary 'SigInput' with a 'ScriptOutput' of type 'PayPKHash'.-arbitraryPKHashSigInput :: Network -> Gen (SigInput, SecKeyI)-arbitraryPKHashSigInput net = arbitraryAnyInput net True---- | Arbitrary 'SigInput'.-arbitraryAnyInput :: Network -> Bool -> Gen (SigInput, SecKeyI)-arbitraryAnyInput net pkh = do-    (k, p) <- arbitraryKeyPair-    let out | pkh = PayPKHash $ getAddrHash160 $ pubKeyAddr p-            | otherwise = PayPK p-    (val, op, sh) <- arbitraryInputStuff net-    return (SigInput out val op sh Nothing, k)---- | Arbitrary value, out point and sighash for an input.-arbitraryInputStuff :: Network -> Gen (Word64, OutPoint, SigHash)-arbitraryInputStuff net = do-    val <- getTestCoin <$> arbitrarySatoshi net-    op <- arbitraryOutPoint-    sh <- arbitraryValidSigHash net-    return (val, op, sh)---- | Arbitrary 'SigInput' with a 'ScriptOutput' of type 'PayMulSig'.-arbitraryMSSigInput :: Network -> Gen (SigInput, [SecKeyI])-arbitraryMSSigInput net = do-    (m, n) <- arbitraryMSParam-    ks <- vectorOf n arbitraryKeyPair-    let out = PayMulSig (map snd ks) m-    (val, op, sh) <- arbitraryInputStuff net-    perm <- choose (0, n - 1)-    let ksPerm = map fst $ take m $ permutations ks !! perm-    return (SigInput out val op sh Nothing, ksPerm)---- | Arbitrary 'SigInput' with 'ScriptOutput' of type 'PaySH' and a--- 'RedeemScript'.-arbitrarySHSigInput :: Network -> Gen (SigInput, [SecKeyI])-arbitrarySHSigInput net = do-    (SigInput rdm val op sh _, ks) <- oneof-        [ wrapKey <$> arbitraryPKSigInput net-        , wrapKey <$> arbitraryPKHashSigInput net-        , arbitraryMSSigInput net-        ]-    let out = PayScriptHash $ getAddrHash160 $ payToScriptAddress rdm-    return (SigInput out val op sh $ Just rdm, ks)--arbitraryWPKHSigInput :: Network -> Gen (SigInput, SecKeyI)-arbitraryWPKHSigInput net = do-    (k, p) <- arbitraryKeyPair-    (val, op, sh) <- arbitraryInputStuff net-    let out = PayWitnessPKHash . getAddrHash160 $ pubKeyAddr p-    return (SigInput out val op sh Nothing, k)--arbitraryWSHSigInput :: Network -> Gen (SigInput, [SecKeyI])-arbitraryWSHSigInput net = do-    (SigInput rdm val op sh _, ks) <- oneof-        [ wrapKey <$> arbitraryPKSigInput net-        , wrapKey <$> arbitraryPKHashSigInput net-        , arbitraryMSSigInput net-        ]-    let out = PayWitnessScriptHash . getAddrHash256 $ payToWitnessScriptAddress rdm-    return (SigInput out val op sh $ Just rdm, ks)---- | Arbitrary 'Tx' (empty 'TxIn'), 'SigInputs' and private keys that can be--- passed to 'signTx' or 'detSignTx' to fully sign the 'Tx'.-arbitrarySigningData :: Network -> Gen (Tx, [SigInput], [SecKeyI])-arbitrarySigningData net = do-    v <- arbitrary-    ni <- choose (1, 5)-    no <- choose (1, 5)-    sigis <- vectorOf ni (arbitrarySigInput net)-    let uSigis = nubBy (\(a, _) (b, _) -> sigInputOP a == sigInputOP b) sigis-    inps <- forM uSigis $ \(s, _) -> TxIn (sigInputOP s) BS.empty <$> arbitrary-    outs <- vectorOf no (arbitraryTxOut net)-    l <- arbitrary-    perm <- choose (0, length inps - 1)-    let tx = Tx v (permutations inps !! perm) outs [] l-        keys = concatMap snd uSigis-    return (tx, map fst uSigis, keys)---- | Arbitrary transaction with empty inputs.-arbitraryEmptyTx :: Network -> Gen Tx-arbitraryEmptyTx net = do-    v    <- arbitrary-    no   <- choose (1,5)-    ni   <- choose (1,5)-    outs <- vectorOf no (arbitraryTxOut net)-    ops  <- vectorOf ni arbitraryOutPoint-    t    <- arbitrary-    s    <- arbitrary-    return $ Tx v (map (\op -> TxIn op BS.empty s) (nub ops)) outs [] t---- | Arbitrary partially-signed transactions.-arbitraryPartialTxs ::-       Network -> Gen ([Tx], [(ScriptOutput, Word64, OutPoint, Int, Int)])-arbitraryPartialTxs net = do-    tx <- arbitraryEmptyTx net-    res <--        forM (map prevOutput $ txIn tx) $ \op -> do-            (so, val, rdmM, prvs, m, n) <- arbitraryData-            txs <- mapM (singleSig so val rdmM tx op . secKeyData) prvs-            return (txs, (so, val, op, m, n))-    return (concatMap fst res, map snd res)-  where-    singleSig so val rdmM tx op prv = do-        sh <- arbitraryValidSigHash net-        let sigi = SigInput so val op sh rdmM-        return . fromRight (error "Could not decode transaction") $-            signTx net tx [sigi] [prv]-    arbitraryData = do-        (m, n) <- arbitraryMSParam-        val <- getTestCoin <$> arbitrarySatoshi net-        nPrv <- choose (m, n)-        keys <- vectorOf n arbitraryKeyPair-        perm <- choose (0, length keys - 1)-        let pubKeys = map snd keys-            prvKeys = take nPrv $ permutations (map fst keys) !! perm-        let so = PayMulSig pubKeys m-        elements-            [ (so, val, Nothing, prvKeys, m, n)-            , ( PayScriptHash $ getAddrHash160 $ payToScriptAddress so-              , val-              , Just so-              , prvKeys-              , m-              , n)-            ]--wrapKey :: (SigInput, SecKeyI) -> (SigInput, [SecKeyI])-wrapKey (s, k) = (s, [k])
− src/Network/Haskoin/Test/Util.hs
@@ -1,40 +0,0 @@-{-|-Module      : Network.Haskoin.Test.Util-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}-module Network.Haskoin.Test.Util where--import           Data.ByteString       (ByteString, pack)-import           Data.Time.Clock       (UTCTime (..))-import           Data.Time.Clock.POSIX (posixSecondsToUTCTime)-import           Data.Word             (Word32)-import           Test.QuickCheck---- | Arbitrary strict 'ByteString'.-arbitraryBS :: Gen ByteString-arbitraryBS = pack <$> arbitrary---- | Arbitrary non-empty strict ByteString-arbitraryBS1 :: Gen ByteString-arbitraryBS1 = pack <$> listOf1 arbitrary---- | Arbitrary strict ByteString of a given length-arbitraryBSn :: Int -> Gen ByteString-arbitraryBSn n = pack <$> vectorOf n arbitrary---- | Arbitrary UTCTime that generates dates after 01 Jan 1970 01:00:00 CET-arbitraryUTCTime :: Gen UTCTime-arbitraryUTCTime = do-    w <- arbitrary :: Gen Word32-    return $ posixSecondsToUTCTime $ realToFrac w---- | Generate a Maybe from a Gen a-arbitraryMaybe :: Gen a -> Gen (Maybe a)-arbitraryMaybe g = frequency [ (1, return Nothing)-                             , (5, Just <$> g)-                             ]-
− src/Network/Haskoin/Transaction.hs
@@ -1,17 +0,0 @@-{-|-Module      : Network.Haskoin.Transaction-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Transactions and related code.--}-module Network.Haskoin.Transaction-    ( module Common-    , module Builder-    ) where--import           Network.Haskoin.Transaction.Builder as Builder-import           Network.Haskoin.Transaction.Common  as Common
− src/Network/Haskoin/Transaction/Builder.hs
@@ -1,474 +0,0 @@-{-# LANGUAGE LambdaCase        #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Transaction.Builder-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Code to simplify transaction creation, signing, fee calculation and coin-selection.--}-module Network.Haskoin.Transaction.Builder-    ( -- * Transaction Creation & Signing-      buildAddrTx-    , buildTx-    , buildInput-    , SigInput(..)-    , signTx-    , signNestedWitnessTx-    , makeSignature-    , signInput-    , signNestedInput-    , verifyStdTx-    , mergeTxs-    , sigKeys-    , mergeTxInput-    , findSigInput-    , verifyStdInput-      -- * Coin Selection-    , Coin(..)-    , chooseCoins-    , chooseCoinsSink-    , chooseMSCoins-    , chooseMSCoinsSink-    , countMulSig-    , greedyAddSink-    , guessTxFee-    , guessMSTxFee-    , guessTxSize-    , guessMSSize-    ) where--import           Control.Applicative                      ((<|>))-import           Control.Arrow                            (first)-import           Control.Monad                            (foldM, unless)-import           Control.Monad.Identity                   (runIdentity)-import           Crypto.Secp256k1-import qualified Data.ByteString                          as B-import           Data.Conduit                             (ConduitT, Void,-                                                           await, runConduit,-                                                           (.|))-import           Data.Conduit.List                        (sourceList)-import           Data.Either                              (fromRight)-import           Data.List                                (nub)-import           Data.Maybe                               (catMaybes, fromJust,-                                                           isJust)-import           Data.Serialize                           (decode, encode)-import           Data.String.Conversions                  (cs)-import           Data.Text                                (Text)-import           Data.Word                                (Word64)-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto.Hash              (Hash256, addressHash)-import           Network.Haskoin.Crypto.Signature-import           Network.Haskoin.Keys.Common-import           Network.Haskoin.Network.Common-import           Network.Haskoin.Script-import           Network.Haskoin.Transaction.Builder.Sign (SigInput (..),-                                                           buildInput,-                                                           makeSigHash,-                                                           makeSignature,-                                                           sigKeys)-import qualified Network.Haskoin.Transaction.Builder.Sign as S-import           Network.Haskoin.Transaction.Common-import           Network.Haskoin.Transaction.Segwit       (decodeWitnessInput,-                                                           isSegwit,-                                                           viewWitnessProgram)-import           Network.Haskoin.Util---- | Any type can be used as a Coin if it can provide a value in Satoshi.--- The value is used in coin selection algorithms.-class Coin c where-    coinValue :: c -> Word64---- | Coin selection algorithm for normal (non-multisig) transactions. This--- function returns the selected coins together with the amount of change to--- send back to yourself, taking the fee into account.-chooseCoins :: Coin c-            => Word64 -- ^ value to send-            -> Word64 -- ^ fee per byte-            -> Int    -- ^ number of outputs (including change)-            -> Bool   -- ^ try to find better solutions-            -> [c]    -- ^ list of ordered coins to choose from-            -> Either String ([c], Word64)-            -- ^ coin selection and change-chooseCoins target fee nOut continue coins =-    runIdentity . runConduit $-    sourceList coins .| chooseCoinsSink target fee nOut continue---- | Coin selection algorithm for normal (non-multisig) transactions. This--- function returns the selected coins together with the amount of change to--- send back to yourself, taking the fee into account. This version uses a Sink--- for conduit-based coin selection.-chooseCoinsSink :: (Monad m, Coin c)-                => Word64 -- ^ value to send-                -> Word64 -- ^ fee per byte-                -> Int    -- ^ number of outputs (including change)-                -> Bool   -- ^ try to find better solution-                -> ConduitT c Void m (Either String ([c], Word64))-                -- ^ coin selection and change-chooseCoinsSink target fee nOut continue-    | target > 0 =-        maybeToEither err <$>-            greedyAddSink target (guessTxFee fee nOut) continue-    | otherwise = return $ Left "chooseCoins: Target must be > 0"-  where-    err = "chooseCoins: No solution found"---- | Coin selection algorithm for multisig transactions. This function returns--- the selected coins together with the amount of change to send back to--- yourself, taking the fee into account. This function assumes all the coins--- are script hash outputs that send funds to a multisignature address.-chooseMSCoins :: Coin c-              => Word64     -- ^ value to send-              -> Word64     -- ^ fee per byte-              -> (Int, Int) -- ^ m of n multisig-              -> Int        -- ^ number of outputs (including change)-              -> Bool       -- ^ try to find better solution-              -> [c]-              -> Either String ([c], Word64)-              -- ^ coin selection change amount-chooseMSCoins target fee ms nOut continue coins =-    runIdentity . runConduit $-        sourceList coins .| chooseMSCoinsSink target fee ms nOut continue---- | Coin selection algorithm for multisig transactions. This function returns--- the selected coins together with the amount of change to send back to--- yourself, taking the fee into account. This function assumes all the coins--- are script hash outputs that send funds to a multisignature address. This--- version uses a Sink if you need conduit-based coin selection.-chooseMSCoinsSink :: (Monad m, Coin c)-                  => Word64     -- ^ value to send-                  -> Word64     -- ^ fee per byte-                  -> (Int, Int) -- ^ m of n multisig-                  -> Int        -- ^ number of outputs (including change)-                  -> Bool       -- ^ try to find better solution-                  -> ConduitT c Void m (Either String ([c], Word64))-                  -- ^ coin selection and change-chooseMSCoinsSink target fee ms nOut continue-    | target > 0 =-        maybeToEither err <$>-            greedyAddSink target (guessMSTxFee fee ms nOut) continue-    | otherwise = return $ Left "chooseMSCoins: Target must be > 0"-  where-    err = "chooseMSCoins: No solution found"---- | Select coins greedily by starting from an empty solution. If the 'continue'--- flag is set, the algorithm will try to find a better solution in the stream--- after a solution is found. If the next solution found is not strictly better--- than the previously found solution, the algorithm stops and returns the--- previous solution. If the continue flag is not set, the algorithm will return--- the first solution it finds in the stream.-greedyAddSink :: (Monad m, Coin c)-              => Word64          -- ^ value to send-              -> (Int -> Word64) -- ^ coin count to fee function-              -> Bool            -- ^ try to find better solutions-              -> ConduitT c Void m (Maybe ([c], Word64))-              -- ^ coin selection and change-greedyAddSink target guessFee continue =-    go [] 0 [] 0-  where-    -- The goal is the value we must reach (including the fee) for a certain-    -- amount of selected coins.-    goal c = target + guessFee c-    go acc aTot ps pTot = await >>= \case-        -- A coin is available in the stream-        Just coin -> do-            let val = coinValue coin-            -- We have reached the goal using this coin-            if val + aTot >= goal (length acc + 1)-                -- If we want to continue searching for better solutions-                then if continue-                    -- This solution is the first one or-                    -- This solution is better than the previous one-                    then if pTot == 0 || val + aTot < pTot-                        -- Continue searching for better solutions in the stream-                        then go [] 0 (coin:acc) (val + aTot)-                        -- Otherwise, we stop here and return the previous-                        -- solution-                        else return $ Just (ps, pTot - goal (length ps))-                    -- Otherwise, return this solution-                    else return $-                        Just (coin : acc, val + aTot - goal (length acc + 1))-                -- We have not yet reached the goal. Add the coin to the-                -- accumulator-                else go (coin:acc) (val + aTot) ps pTot-        -- We reached the end of the stream-        Nothing ->-            return $ if null ps-                -- If no solution was found, return Nothing-                then Nothing-                -- If we have a solution, return it-                else Just (ps, pTot - goal (length ps))---- | Estimate tranasction fee to pay based on transaction size estimation.-guessTxFee :: Word64 -> Int -> Int -> Word64-guessTxFee byteFee nOut nIn =-    byteFee * fromIntegral (guessTxSize nIn [] nOut 0)---- | Same as 'guessTxFee' but for multisig transactions.-guessMSTxFee :: Word64 -> (Int, Int) -> Int -> Int -> Word64-guessMSTxFee byteFee ms nOut nIn =-    byteFee * fromIntegral (guessTxSize 0 (replicate nIn ms) nOut 0)---- | Computes an upper bound on the size of a transaction based on some known--- properties of the transaction.-guessTxSize :: Int         -- ^ number of regular transaction inputs-            -> [(Int,Int)] -- ^ multisig m of n for each input-            -> Int         -- ^ number of P2PKH outputs-            -> Int         -- ^ number of P2SH outputs-            -> Int         -- ^ upper bound on transaction size-guessTxSize pki msi pkout msout =-    8 + inpLen + inp + outLen + out-  where-    inpLen = B.length $ encode $ VarInt $ fromIntegral $ length msi + pki-    outLen = B.length $ encode $ VarInt $ fromIntegral $ pkout + msout-    inp = pki * 148 + sum (map guessMSSize msi)-             -- (20: hash160) + (5: opcodes) +-             -- (1: script len) + (8: Word64)-    out =-        pkout * 34 +-             -- (20: hash160) + (3: opcodes) +-             -- (1: script len) + (8: Word64)-        msout * 32---- | Size of a multisig P2SH input.-guessMSSize :: (Int,Int) -> Int-guessMSSize (m, n)-    -- OutPoint (36) + Sequence (4) + Script- = 40 + fromIntegral (B.length $ encode $ VarInt $ fromIntegral scp) + scp-    -- OP_M + n*PubKey + OP_N + OP_CHECKMULTISIG-  where-    rdm =-        fromIntegral $-        B.length $ encode $ opPushData $ B.replicate (n * 34 + 3) 0-    -- Redeem + m*sig + OP_0-    scp = rdm + m * 73 + 1--{- Build a new Tx -}---- | Build a transaction by providing a list of outpoints as inputs--- and a list of recipient addresses and amounts as outputs.-buildAddrTx :: Network -> [OutPoint] -> [(Text, Word64)] -> Either String Tx-buildAddrTx net xs ys = buildTx xs =<< mapM f ys-  where-    f (s, v) =-        maybe (Left ("buildAddrTx: Invalid address " ++ cs s)) Right $ do-            a <- stringToAddr net s-            let o = addressToOutput a-            return (o, v)---- | Build a transaction by providing a list of outpoints as inputs--- and a list of 'ScriptOutput' and amounts as outputs.-buildTx :: [OutPoint] -> [(ScriptOutput, Word64)] -> Either String Tx-buildTx xs ys =-    mapM fo ys >>= \os -> return $ Tx 1 (map fi xs) os [] 0-  where-    fi outPoint = TxIn outPoint B.empty maxBound-    fo (o, v)-        | v <= 2100000000000000 = return $ TxOut v $ encodeOutputBS o-        | otherwise =-            Left $ "buildTx: Invalid amount " ++ show v---- | Sign a transaction by providing the 'SigInput' signing parameters and a--- list of private keys. The signature is computed deterministically as defined--- in RFC-6979.------ Example: P2SH-P2WKH------ > sigIn = SigInput (PayWitnessPKHash h) 100000 op sigHashAll Nothing--- > signedTx = signTx btc unsignedTx [sigIn] [key]------ Example: P2SH-P2WSH multisig------ > sigIn = SigInput (PayWitnessScriptHash h) 100000 op sigHashAll (Just $ PayMulSig [p1,p2,p3] 2)--- > signedTx = signTx btc unsignedTx [sigIn] [k1,k3]-signTx :: Network-       -> Tx               -- ^ transaction to sign-       -> [SigInput]       -- ^ signing parameters-       -> [SecKey]         -- ^ private keys to sign with-       -> Either String Tx -- ^ signed transaction-signTx net tx si = S.signTx net tx $ notNested <$> si-  where notNested s = (s, False)---- | This function differs from 'signTx' by assuming all segwit inputs are--- P2SH-nested.  Use the same signing parameters for segwit inputs as in 'signTx'.-signNestedWitnessTx :: Network-                    -> Tx               -- ^ transaction to sign-                    -> [SigInput]       -- ^ signing parameters-                    -> [SecKey]         -- ^ private keys to sign with-                    -> Either String Tx -- ^ signed transaction-signNestedWitnessTx net tx si = S.signTx net tx $ nested <$> si-  where-    -- NOTE: the nesting flag is ignored for non-segwit inputs-    nested s = (s, True)----- | Sign a single input in a transaction deterministically (RFC-6979).-signInput :: Network -> Tx -> Int -> SigInput -> SecKeyI -> Either String Tx-signInput net tx i si = S.signInput net tx i (si, False)---- | Like 'signInput' but treat segwit inputs as nested-signNestedInput :: Network -> Tx -> Int -> SigInput -> SecKeyI -> Either String Tx-signNestedInput net tx i si = S.signInput net tx i (si, True)---- | Order the 'SigInput' with respect to the transaction inputs. This allows--- the user to provide the 'SigInput' in any order. Users can also provide only--- a partial set of 'SigInput' entries.-findSigInput :: [SigInput] -> [TxIn] -> [(SigInput, Int)]-findSigInput = S.findInputIndex sigInputOP--{- Merge multisig transactions -}---- | Merge partially-signed multisig transactions.  This function does not--- support segwit and P2SH-segwit inputs.  Use PSBTs to merge transactions with--- segwit inputs.-mergeTxs :: Network -> [Tx] -> [(ScriptOutput, Word64, OutPoint)] -> Either String Tx-mergeTxs net txs os-    | null txs = Left "Transaction list is empty"-    | length (nub emptyTxs) /= 1 = Left "Transactions do not match"-    | length txs == 1 = return $ head txs-    | otherwise = foldM (mergeTxInput net txs) (head emptyTxs) outs-  where-    zipOp = zip (matchTemplate os (txIn $ head txs) f) [0..]-    outs = map (first $ (\(o,v,_) -> (o,v)) . fromJust) $ filter (isJust . fst) zipOp-    f (_, _, o) txin = o == prevOutput txin-    emptyTxs = map (\tx -> foldl clearInput tx outs) txs-    ins is i = updateIndex i is (\ti -> ti{ scriptInput = B.empty })-    clearInput tx (_, i) =-        Tx (txVersion tx) (ins (txIn tx) i) (txOut tx) [] (txLockTime tx)---- | Merge input from partially-signed multisig transactions.  This function--- does not support segwit and P2SH-segwit inputs.-mergeTxInput ::-       Network-    -> [Tx]-    -> Tx-    -> ((ScriptOutput, Word64), Int)-    -> Either String Tx-mergeTxInput net txs tx ((so, val), i)-    -- Ignore transactions with empty inputs- = do-    let ins = map (scriptInput . (!! i) . txIn) txs-    sigRes <- mapM extractSigs $ filter (not . B.null) ins-    let rdm = snd $ head sigRes-    unless (all (== rdm) $ map snd sigRes) $ Left "Redeem scripts do not match"-    si <- encodeInputBS <$> go (nub $ concatMap fst sigRes) so rdm-    let ins' = updateIndex i (txIn tx) (\ti -> ti {scriptInput = si})-    return $ Tx (txVersion tx) ins' (txOut tx) [] (txLockTime tx)-  where-    go allSigs out rdmM =-        case out of-            PayMulSig msPubs r ->-                let sigs =-                        take r $-                        catMaybes $ matchTemplate allSigs msPubs $ f out-                 in return $ RegularInput $ SpendMulSig sigs-            PayScriptHash _ ->-                case rdmM of-                    Just rdm -> do-                        si <- go allSigs rdm Nothing-                        return $ ScriptHashInput (getRegularInput si) rdm-                    _ -> Left "Invalid output script type"-            _ -> Left "Invalid output script type"-    extractSigs si =-        case decodeInputBS net si of-            Right (RegularInput (SpendMulSig sigs)) -> Right (sigs, Nothing)-            Right (ScriptHashInput (SpendMulSig sigs) rdm) ->-                Right (sigs, Just rdm)-            _ -> Left "Invalid script input type"-    f out (TxSignature x sh) p =-        verifyHashSig-            (txSigHash net tx (encodeOutput out) val i sh)-            x-            (pubKeyPoint p)-    f _ TxSignatureEmpty _ = False--{- Tx verification -}---- | Verify if a transaction is valid and all of its inputs are standard.-verifyStdTx :: Network -> Tx -> [(ScriptOutput, Word64, OutPoint)] -> Bool-verifyStdTx net tx xs =-    not (null (txIn tx)) && all go (zip (matchTemplate xs (txIn tx) f) [0 ..])-  where-    f (_, _, o) txin = o == prevOutput txin-    go (Just (so, val, _), i) = verifyStdInput net tx i so val-    go _                      = False---- | Verify if a transaction input is valid and standard.-verifyStdInput :: Network -> Tx -> Int -> ScriptOutput -> Word64 -> Bool-verifyStdInput net tx i so0 val-    | isSegwit so0 = fromRight False $ (inp == mempty &&) . verifySegwitInput so0 <$> wp so0-    | otherwise    = fromRight False-                   $ verifyLegacyInput so0 <$> decodeInputBS net inp-                 <|> (nestedScriptOutput >>= \so -> verifyNestedInput so0 so <$> wp so)-  where-    verifyLegacyInput so si = case (so, si) of-        (PayPK pub, RegularInput (SpendPK (TxSignature sig sh))) ->-            verifyHashSig (theTxSigHash so sh Nothing) sig (pubKeyPoint pub)-        (PayPKHash h, RegularInput (SpendPKHash (TxSignature sig sh) pub)) ->-            pubKeyAddr pub == p2pkhAddr h &&-            verifyHashSig (theTxSigHash so sh Nothing) sig (pubKeyPoint pub)-        (PayMulSig pubs r, RegularInput (SpendMulSig sigs)) ->-            countMulSig net tx out val i (pubKeyPoint <$> pubs) sigs == r-        (PayScriptHash h, ScriptHashInput si' rdm) ->-            payToScriptAddress rdm == p2shAddr h && verifyLegacyInput rdm (RegularInput si')-        _ -> False-      where out = encodeOutput so--    verifySegwitInput so (rdm, si) = case (so, rdm, si) of-        (PayWitnessPKHash h, Nothing, SpendPKHash (TxSignature sig sh) pub) ->-            pubKeyWitnessAddr pub == p2wpkhAddr h &&-            verifyHashSig (theTxSigHash so sh Nothing) sig (pubKeyPoint pub)-        (PayWitnessScriptHash h, Just rdm@(PayPK pub), SpendPK (TxSignature sig sh)) ->-            payToWitnessScriptAddress rdm == p2wshAddr h &&-            verifyHashSig (theTxSigHash so sh $ Just rdm) sig (pubKeyPoint pub)-        (PayWitnessScriptHash h, Just rdm@(PayPKHash kh), SpendPKHash (TxSignature sig sh) pub) ->-            payToWitnessScriptAddress rdm == p2wshAddr h &&-            addressHash (encode pub) == kh &&-            verifyHashSig (theTxSigHash so sh $ Just rdm) sig (pubKeyPoint pub)-        (PayWitnessScriptHash h, Just rdm@(PayMulSig pubs r), SpendMulSig sigs) ->-            payToWitnessScriptAddress rdm == p2wshAddr h &&-            countMulSig' (\sh -> theTxSigHash so sh $ Just rdm) (pubKeyPoint <$> pubs) sigs == r-        _ -> False-      where out = encodeOutput so--    verifyNestedInput so so' x = case so of-        PayScriptHash h -> payToScriptAddress so' == p2shAddr h && verifySegwitInput so' x-        _               -> False--    inp             = scriptInput $ txIn tx !! i-    theTxSigHash so = makeSigHash net tx i so val--    ws | length (txWitness tx) > i = txWitness tx !! i-       | otherwise                 = []-    wp so = decodeWitnessInput net =<< viewWitnessProgram net so ws--    nestedScriptOutput = scriptOps <$> decode inp >>= \case-        [OP_PUSHDATA bs _] -> decodeOutputBS bs-        _                  -> Left "nestedScriptOutput: not a nested output"---- | Count the number of valid signatures for a multi-signature transaction.-countMulSig ::-       Network-    -> Tx-    -> Script-    -> Word64-    -> Int-    -> [PubKey]-    -> [TxSignature]-    -> Int-countMulSig net tx out val i = countMulSig' h-  where-    h = txSigHash net tx out val i--countMulSig' :: (SigHash -> Hash256) -> [PubKey] -> [TxSignature] -> Int-countMulSig' h [] _ = 0-countMulSig' h _ [] = 0-countMulSig' h (_:pubs) (TxSignatureEmpty:sigs) = countMulSig' h pubs sigs-countMulSig' h (pub:pubs) sigs@(TxSignature sig sh : sigs')-    | verifyHashSig (h sh) sig pub = 1 + countMulSig' h pubs sigs'-    | otherwise                    = countMulSig' h pubs sigs
− src/Network/Haskoin/Transaction/Builder/Sign.hs
@@ -1,271 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE LambdaCase        #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Transaction.Builder.Sign-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Types and logic for signing transactions.--}-module Network.Haskoin.Transaction.Builder.Sign-    ( SigInput (..)-    , makeSignature-    , makeSigHash-    , signTx-    , findInputIndex-    , signInput-    , buildInput-    , sigKeys-    ) where--import           Control.DeepSeq                    (NFData)-import           Control.Monad                      (foldM, mzero, when)-import           Data.Aeson                         (FromJSON, ToJSON,-                                                     Value (Object), object,-                                                     parseJSON, toJSON, (.:),-                                                     (.:?), (.=))-import           Data.Either                        (rights)-import           Data.Hashable                      (Hashable)-import           Data.List                          (find, nub)-import           Data.Maybe                         (catMaybes, fromMaybe,-                                                     mapMaybe, maybeToList)-import qualified Data.Serialize                     as S-import           Data.Word                          (Word64)-import           GHC.Generics                       (Generic)--import           Network.Haskoin.Address            (getAddrHash160, pubKeyAddr)-import           Network.Haskoin.Constants          (Network)-import           Network.Haskoin.Crypto             (Hash256, SecKey)-import           Network.Haskoin.Crypto.Signature   (signHash, verifyHashSig)-import           Network.Haskoin.Keys.Common        (PubKeyI (..), SecKeyI (..),-                                                     derivePubKeyI, wrapSecKey)-import           Network.Haskoin.Script.Common      (ScriptOutput (..),-                                                     encodeOutput,-                                                     encodeOutputBS, opPushData)-import           Network.Haskoin.Script.SigHash     (SigHash, TxSignature (..),-                                                     decodeTxSig, txSigHash,-                                                     txSigHashForkId)-import           Network.Haskoin.Script.Standard    (RedeemScript,-                                                     ScriptInput (..),-                                                     SimpleInput (..),-                                                     decodeInputBS,-                                                     encodeInputBS)-import           Network.Haskoin.Transaction.Common (OutPoint, Tx (..),-                                                     TxIn (..), WitnessData)-import           Network.Haskoin.Transaction.Segwit (WitnessProgram (..),-                                                     calcWitnessProgram,-                                                     isSegwit, toWitnessStack)-import           Network.Haskoin.Util               (matchTemplate, updateIndex)---- | Data type used to specify the signing parameters of a transaction input.--- To sign an input, the previous output script, outpoint and sighash are--- required. When signing a pay to script hash output, an additional redeem--- script is required.-data SigInput = SigInput-    { sigInputScript :: !ScriptOutput         -- ^ output script to spend-    , sigInputValue  :: !Word64               -- ^ output script value-    , sigInputOP     :: !OutPoint             -- ^ outpoint to spend-    , sigInputSH     :: !SigHash              -- ^ signature type-    , sigInputRedeem :: !(Maybe RedeemScript) -- ^ redeem script-    } deriving (Eq, Show, Read, Generic, Hashable, NFData)--instance ToJSON SigInput where-    toJSON (SigInput so val op sh rdm) = object $-        [ "pkscript" .= so-        , "value"    .= val-        , "outpoint" .= op-        , "sighash"  .= sh-        ] ++ [ "redeem" .= r | r <- maybeToList rdm ]--instance FromJSON SigInput where-    parseJSON (Object o) = do-        so  <- o .: "pkscript"-        val <- o .: "value"-        op  <- o .: "outpoint"-        sh  <- o .: "sighash"-        rdm <- o .:? "redeem"-        return $ SigInput so val op sh rdm-    parseJSON _ = mzero---- | Sign a transaction by providing the 'SigInput' signing parameters and a--- list of private keys. The signature is computed deterministically as defined--- in RFC-6979.-signTx :: Network-       -> Tx                 -- ^ transaction to sign-       -> [(SigInput, Bool)] -- ^ signing parameters, with nesting flag-       -> [SecKey]           -- ^ private keys to sign with-       -> Either String Tx   -- ^ signed transaction-signTx net otx sigis allKeys-    | null ti   = Left "signTx: Transaction has no inputs"-    | otherwise = foldM go otx $ findInputIndex (sigInputOP . fst) sigis ti-  where-    ti = txIn otx-    go tx (sigi@(SigInput so _ _ _ rdmM, _), i) = do-        keys <- sigKeys so rdmM allKeys-        foldM (\t k -> signInput net t i sigi k) tx keys---- | Sign a single input in a transaction deterministically (RFC-6979).  The--- nesting flag only affects the behavior of segwit inputs.-signInput ::-       Network-    -> Tx-    -> Int-    -> (SigInput, Bool) -- ^ boolean flag: nest input-    -> SecKeyI-    -> Either String Tx-signInput net tx i (sigIn@(SigInput so val _ sh rdmM), nest) key = do-    let sig = makeSignature net tx i sigIn key-    si <- buildInput net tx i so val rdmM sig $ derivePubKeyI key-    w  <- updatedWitnessData tx i so si-    return tx { txIn      = nextTxIn so si-              , txWitness = w-              }-  where-    f si x = x {scriptInput = encodeInputBS si}-    g so x = x {scriptInput = S.encode . opPushData $ encodeOutputBS so}-    txis = txIn tx-    nextTxIn so si-        | isSegwit so && nest = updateIndex i txis (g so)-        | isSegwit so         = txIn tx-        | otherwise           = updateIndex i txis (f si)---- | Add the witness data of the transaction given segwit parameters for an input.------ @since 0.11.0.0-updatedWitnessData :: Tx -> Int -> ScriptOutput -> ScriptInput -> Either String WitnessData-updatedWitnessData tx i so si-    | isSegwit so = updateWitness . toWitnessStack =<< calcWitnessProgram so si-    | otherwise   = return $ txWitness tx-  where-    updateWitness w-        | null $ txWitness tx        = return $ updateIndex i defaultStack (const w)-        | length (txWitness tx) /= n = Left "Invalid number of witness stacks"-        | otherwise                  = return $ updateIndex i (txWitness tx) (const w)-    defaultStack = replicate n $ toWitnessStack EmptyWitnessProgram-    n = length $ txIn tx---- | Associate an input index to each value in a list-findInputIndex ::-       (a -> OutPoint) -- ^ extract an outpoint-    -> [a]             -- ^ input list-    -> [TxIn]          -- ^ reference list of inputs-    -> [(a, Int)]-findInputIndex getOutPoint as ti =-    mapMaybe g $ zip (matchTemplate as ti f) [0..]-  where-    f s txin = getOutPoint s == prevOutput txin-    g (Just s, i)  = Just (s,i)-    g (Nothing, _) = Nothing---- | Find from the list of provided private keys which one is required to sign--- the 'ScriptOutput'.-sigKeys ::-       ScriptOutput-    -> Maybe RedeemScript-    -> [SecKey]-    -> Either String [SecKeyI]-sigKeys so rdmM keys =-    case (so, rdmM) of-        (PayPK p, Nothing) ->-            return . map fst . maybeToList $ find ((== p) . snd) zipKeys-        (PayPKHash h, Nothing) -> return $ keyByHash h-        (PayMulSig ps r, Nothing) ->-            return $ map fst $ take r $ filter ((`elem` ps) . snd) zipKeys-        (PayScriptHash _, Just rdm) -> sigKeys rdm Nothing keys-        (PayWitnessPKHash h, _) -> return $ keyByHash h-        (PayWitnessScriptHash _, Just rdm) -> sigKeys rdm Nothing keys-        _ -> Left "sigKeys: Could not decode output script"-  where-    zipKeys =-        [ (prv, pub)-        | k <- keys-        , t <- [True, False]-        , let prv = wrapSecKey t k-        , let pub = derivePubKeyI prv-        ]-    keyByHash h = fmap fst . maybeToList . findKey h $ zipKeys-    findKey h   = find $ (== h) . getAddrHash160 . pubKeyAddr . snd---- | Construct an input for a transaction given a signature, public key and data--- about the previous output.-buildInput ::-       Network-    -> Tx                 -- ^ transaction where input will be added-    -> Int                -- ^ input index where signature will go-    -> ScriptOutput       -- ^ output script being spent-    -> Word64             -- ^ amount of previous output-    -> Maybe RedeemScript -- ^ redeem script if pay-to-script-hash-    -> TxSignature-    -> PubKeyI-    -> Either String ScriptInput-buildInput net tx i so val rdmM sig pub = do-    when (i >= length (txIn tx)) $ Left "buildInput: Invalid input index"-    case (so, rdmM) of-        (PayScriptHash _, Just rdm)        -> buildScriptHashInput rdm-        (PayWitnessScriptHash _, Just rdm) -> buildScriptHashInput rdm-        (PayWitnessPKHash _, Nothing)      -> return . RegularInput $ SpendPKHash sig pub-        (_, Nothing)                       -> buildRegularInput so-        _ -> Left "buildInput: Invalid output/redeem script combination"-  where-    buildRegularInput = \case-        PayPK _ -> return $ RegularInput $ SpendPK sig-        PayPKHash _ -> return $ RegularInput $ SpendPKHash sig pub-        PayMulSig msPubs r -> do-            let mSigs   = take r $ catMaybes $ matchTemplate allSigs msPubs f-                allSigs = nub $ sig : parseExistingSigs net tx so i-            return $ RegularInput $ SpendMulSig mSigs-        _ -> Left "buildInput: Invalid output/redeem script combination"-    buildScriptHashInput rdm = do-        inp <- buildRegularInput rdm-        return $ ScriptHashInput (getRegularInput inp) rdm-    f (TxSignature x sh) p =-        verifyHashSig (makeSigHash net tx i so val sh rdmM) x (pubKeyPoint p)-    f TxSignatureEmpty _ = False---- | Apply heuristics to extract the signatures for a particular input that are--- embedded in the transaction.------ @since 0.11.0.0-parseExistingSigs :: Network -> Tx -> ScriptOutput -> Int -> [TxSignature]-parseExistingSigs net tx so i = insSigs <> witSigs-  where-    insSigs = case decodeInputBS net scp of-            Right (ScriptHashInput (SpendMulSig xs) _) -> xs-            Right (RegularInput (SpendMulSig xs))      -> xs-            _                                          -> []-    scp = scriptInput $ txIn tx !! i-    witSigs-        | not $ isSegwit so   = []-        | null $ txWitness tx = []-        | otherwise           = rights $ decodeTxSig net <$> (txWitness tx !! i)---- | Produce a structured representation of a deterministic (RFC-6979) signature over an input.-makeSignature :: Network -> Tx -> Int -> SigInput -> SecKeyI -> TxSignature-makeSignature net tx i (SigInput so val _ sh rdmM) key = TxSignature (signHash (secKeyData key) m) sh-  where-    m = makeSigHash net tx i so val sh rdmM---- | A function which selects the digest algorithm and parameters as appropriate------ @since 0.11.0.0-makeSigHash ::-       Network-    -> Tx-    -> Int-    -> ScriptOutput-    -> Word64-    -> SigHash-    -> Maybe RedeemScript-    -> Hash256-makeSigHash net tx i so val sh rdmM = h net tx (encodeOutput so') val i sh-  where-    so' = case so of-        PayWitnessPKHash h -> PayPKHash h-        _                  -> fromMaybe so rdmM-    h | isSegwit so = txSigHashForkId-      | otherwise   = txSigHash
− src/Network/Haskoin/Transaction/Common.hs
@@ -1,303 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Transaction.Common-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Code related to transactions parsing and serialization.--}-module Network.Haskoin.Transaction.Common-    ( Tx(..)-    , TxIn(..)-    , TxOut(..)-    , OutPoint(..)-    , TxHash(..)-    , WitnessData-    , WitnessStack-    , WitnessStackItem-    , txHash-    , hexToTxHash-    , txHashToHex-    , nosigTxHash-    , nullOutPoint-    , genesisTx-    ) where--import           Control.Applicative            ((<|>))-import           Control.DeepSeq-import           Control.Monad                  (forM_, guard, liftM2, mzero,-                                                 replicateM, (<=<))-import           Data.Aeson                     as A-import           Data.ByteString                (ByteString)-import qualified Data.ByteString                as B-import           Data.Hashable                  (Hashable)-import           Data.Maybe                     (fromMaybe, maybe)-import           Data.Serialize                 as S-import           Data.String                    (IsString, fromString)-import           Data.String.Conversions        (cs)-import           Data.Text                      (Text)-import           Data.Word                      (Word32, Word64)-import           GHC.Generics                   (Generic)-import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Network.Common-import           Network.Haskoin.Script.Common-import           Network.Haskoin.Util-import           Text.Read                      as R---- | Transaction id: hash of transaction excluding witness data.-newtype TxHash = TxHash { getTxHash :: Hash256 }-    deriving (Eq, Ord, Generic, Hashable, Serialize, NFData)--instance Show TxHash where-    showsPrec _ = shows . txHashToHex--instance Read TxHash where-    readPrec = do-        R.String str <- R.lexP-        maybe R.pfail return $ hexToTxHash $ cs str--instance IsString TxHash where-    fromString s =-        let e = error "Could not read transaction hash from hex string"-        in fromMaybe e $ hexToTxHash $ cs s--instance FromJSON TxHash where-    parseJSON = withText "txid" $-        maybe mzero return . hexToTxHash--instance ToJSON TxHash where-    toJSON = A.String . txHashToHex---- | Transaction hash excluding signatures.-nosigTxHash :: Tx -> TxHash-nosigTxHash tx =-    TxHash $ doubleSHA256 $ S.encode tx { txIn = map clearInput $ txIn tx }-  where-    clearInput ti = ti { scriptInput = B.empty }---- | Convert transaction hash to hex form, reversing bytes.-txHashToHex :: TxHash -> Text-txHashToHex (TxHash h) = encodeHex (B.reverse (S.encode h))---- | Convert transaction hash from hex, reversing bytes.-hexToTxHash :: Text -> Maybe TxHash-hexToTxHash hex = do-    bs <- B.reverse <$> decodeHex hex-    h <- either (const Nothing) Just (S.decode bs)-    return $ TxHash h---- | Witness stack for SegWit transactions.-type WitnessData = [WitnessStack]--- | Witness stack for SegWit transactions.-type WitnessStack = [WitnessStackItem]--- | Witness stack item for SegWit transactions.-type WitnessStackItem = ByteString---- | Data type representing a transaction.-data Tx = Tx-    { -- | transaction data format version-      txVersion  :: !Word32-      -- | list of transaction inputs-    , txIn       :: ![TxIn]-      -- | list of transaction outputs-    , txOut      :: ![TxOut]-      -- | witness data for the transaction-    , txWitness  :: !WitnessData-      -- | earliest mining height or time-    , txLockTime :: !Word32-    } deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)---- | Compute transaction hash.-txHash :: Tx -> TxHash-txHash tx = TxHash (doubleSHA256 (S.encode tx {txWitness = []}))--instance IsString Tx where-    fromString =-        fromMaybe e . (eitherToMaybe . S.decode <=< decodeHex) . cs-      where-        e = error "Could not read transaction from hex string"--instance Serialize Tx where-    get = parseWitnessTx <|> parseLegacyTx-    put tx-        | null (txWitness tx) = putLegacyTx tx-        | otherwise = putWitnessTx tx--putInOut :: Tx -> Put-putInOut tx = do-    putVarInt $ length (txIn tx)-    forM_ (txIn tx) put-    putVarInt $ length (txOut tx)-    forM_ (txOut tx) put---- | Non-SegWit transaction serializer.-putLegacyTx :: Tx -> Put-putLegacyTx tx = do-    putWord32le (txVersion tx)-    putInOut tx-    putWord32le (txLockTime tx)---- | Witness transaciton serializer.-putWitnessTx :: Tx -> Put-putWitnessTx tx = do-    putWord32le (txVersion tx)-    putWord8 0x00-    putWord8 0x01-    putInOut tx-    putWitnessData (txWitness tx)-    putWord32le (txLockTime tx)---- | Non-SegWit transaction deseralizer.-parseLegacyTx :: Get Tx-parseLegacyTx = do-    v <- getWord32le-    is <- replicateList =<< S.get-    os <- replicateList =<< S.get-    l <- getWord32le-    return-        Tx-        {txVersion = v, txIn = is, txOut = os, txWitness = [], txLockTime = l}-  where-    replicateList (VarInt c) = replicateM (fromIntegral c) S.get---- | Witness transaction deserializer.-parseWitnessTx :: Get Tx-parseWitnessTx = do-    v <- getWord32le-    m <- getWord8-    f <- getWord8-    guard $ m == 0x00-    guard $ f == 0x01-    is <- replicateList =<< S.get-    os <- replicateList =<< S.get-    w <- parseWitnessData $ length is-    l <- getWord32le-    return-        Tx {txVersion = v, txIn = is, txOut = os, txWitness = w, txLockTime = l}-  where-    replicateList (VarInt c) = replicateM (fromIntegral c) S.get---- | Witness data deserializer. Requires count of inputs.-parseWitnessData :: Int -> Get WitnessData-parseWitnessData n = replicateM n parseWitnessStack-  where-    parseWitnessStack = do-        VarInt i <- S.get-        replicateM (fromIntegral i) parseWitnessStackItem-    parseWitnessStackItem = do-        VarInt i <- S.get-        getByteString $ fromIntegral i---- | Witness data serializer.-putWitnessData :: WitnessData -> Put-putWitnessData = mapM_ putWitnessStack-  where-    putWitnessStack ws = do-        putVarInt $ length ws-        mapM_ putWitnessStackItem ws-    putWitnessStackItem bs = do-        putVarInt $ B.length bs-        putByteString bs--instance FromJSON Tx where-    parseJSON = withText "Tx" $-        maybe mzero return . (eitherToMaybe . S.decode <=< decodeHex)--instance ToJSON Tx where-    toJSON = A.String . encodeHex . S.encode---- | Data type representing a transaction input.-data TxIn =-    TxIn {-           -- | output being spent-           prevOutput   :: !OutPoint-           -- | signatures and redeem script-         , scriptInput  :: !ByteString-           -- | lock-time using sequence numbers (BIP-68)-         , txInSequence :: !Word32-         } deriving (Eq, Show, Read, Ord, Generic, Hashable, NFData)--instance Serialize TxIn where-    get =-        TxIn <$> S.get <*> (readBS =<< S.get) <*> getWord32le-      where-        readBS (VarInt len) = getByteString $ fromIntegral len--    put (TxIn o s q) = do-        put o-        putVarInt $ B.length s-        putByteString s-        putWord32le q---- | Data type representing a transaction output.-data TxOut =-    TxOut {-            -- | value of output is satoshi-            outValue     :: !Word64-            -- | pubkey script-          , scriptOutput :: !ByteString-          } deriving (Eq, Show, Read, Ord, Generic, Hashable, NFData)--instance Serialize TxOut where-    get = do-        val <- getWord64le-        (VarInt len) <- S.get-        TxOut val <$> getByteString (fromIntegral len)--    put (TxOut o s) = do-        putWord64le o-        putVarInt $ B.length s-        putByteString s---- | The 'OutPoint' refers to a transaction output being spent.-data OutPoint = OutPoint-    { -- | hash of previous transaction-      outPointHash  :: !TxHash-      -- | position of output in previous transaction-    , outPointIndex :: !Word32-    } deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)--instance FromJSON OutPoint where-    parseJSON = withText "OutPoint" $-        maybe mzero return . (eitherToMaybe . S.decode <=< decodeHex)--instance ToJSON OutPoint where-    toJSON = A.String . encodeHex . S.encode--instance Serialize OutPoint where-    get = do-        (h,i) <- liftM2 (,) S.get getWord32le-        return $ OutPoint h i-    put (OutPoint h i) = put h >> putWord32le i---- | Outpoint used in coinbase transactions.-nullOutPoint :: OutPoint-nullOutPoint =-    OutPoint-    { outPointHash =-          "0000000000000000000000000000000000000000000000000000000000000000"-    , outPointIndex = maxBound-    }---- | Transaction from Genesis block.-genesisTx :: Tx-genesisTx =-    Tx 1 [txin] [txout] [] locktime-  where-    txin = TxIn outpoint inputBS maxBound-    txout = TxOut 5000000000 (encodeOutputBS output)-    locktime = 0-    outpoint = OutPoint z maxBound-    Just inputBS = decodeHex $ fromString $-        "04ffff001d0104455468652054696d65732030332f4a616e2f323030392043686" ++-        "16e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f" ++-        "757420666f722062616e6b73"-    output = PayPK $ fromString $-        "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb" ++-        "649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"-    z = "0000000000000000000000000000000000000000000000000000000000000000"
− src/Network/Haskoin/Transaction/Partial.hs
@@ -1,496 +0,0 @@-{-# LANGUAGE DeriveGeneric              #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE NamedFieldPuns             #-}-{-# LANGUAGE OverloadedStrings          #-}-{-# LANGUAGE ScopedTypeVariables        #-}-{-|-Module      : Network.Haskoin.Transaction.Partial-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : matt@bitnomial.com-Stability   : experimental-Portability : POSIX--Code related to PSBT parsing and serialization.--}-module Network.Haskoin.Transaction.Partial-    ( PartiallySignedTransaction (..)-    , Input (..)-    , Output (..)-    , UnknownMap (..)-    , Key (..)-    , merge-    , mergeInput-    , mergeOutput-    , complete-    , finalTransaction-    , emptyPSBT-    , emptyInput-    , emptyOutput-    ) where--import           Control.Applicative         ((<|>))-import           Control.DeepSeq-import           Control.Monad               (guard, replicateM, void)-import           Data.ByteString             (ByteString)-import qualified Data.ByteString             as B-import           Data.Hashable               (Hashable)-import           Data.HashMap.Strict         (HashMap)-import qualified Data.HashMap.Strict         as HashMap-import           Data.List                   (foldl')-import           Data.Maybe                  (fromMaybe, isJust)-import           Data.Serialize              as S-import           GHC.Generics                (Generic)-import           GHC.Word                    (Word32, Word8)-import           Network.Haskoin.Address     (Address (..), pubKeyAddr)-import           Network.Haskoin.Keys        (Fingerprint, KeyIndex, PubKeyI)-import           Network.Haskoin.Network     (VarInt (..), VarString (..),-                                              putVarInt)-import           Network.Haskoin.Script      (Script (..), ScriptOp (..),-                                              ScriptOutput (..), SigHash,-                                              decodeOutput, decodeOutputBS,-                                              encodeOutputBS, isPayScriptHash,-                                              opPushData, toP2SH, toP2WSH)-import           Network.Haskoin.Transaction (Tx (..), TxOut, WitnessStack,-                                              outPointIndex, prevOutput,-                                              scriptInput, scriptOutput)-import           Network.Haskoin.Util        (eitherToMaybe)---- | PSBT data type as specified in [BIP-174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki). This--- contains an unsigned transaction, inputs and outputs, and unspecified extra data. There is one input per input in the--- unsigned transaction, and one output per output in the unsigned transaction. The inputs and outputs in the--- 'PartiallySignedTransaction' line up by index with the inputs and outputs in the unsigned transaction.-data PartiallySignedTransaction = PartiallySignedTransaction-    { unsignedTransaction :: Tx-    , globalUnknown       :: UnknownMap-    , inputs              :: [Input]-    , outputs             :: [Output]-    } deriving (Show, Eq, Generic)--instance NFData PartiallySignedTransaction---- | Inputs contain all of the data needed to sign a transaction and all of the resulting signature data after signing.-data Input = Input-    { nonWitnessUtxo     :: Maybe Tx-    , witnessUtxo        :: Maybe TxOut-    , partialSigs        :: HashMap PubKeyI ByteString-    , sigHashType        :: Maybe SigHash-    , inputRedeemScript  :: Maybe Script-    , inputWitnessScript :: Maybe Script-    , inputHDKeypaths    :: HashMap PubKeyI (Fingerprint, [KeyIndex])-    , finalScriptSig     :: Maybe Script-    , finalScriptWitness :: Maybe WitnessStack-    , inputUnknown       :: UnknownMap-    } deriving (Show, Eq, Generic)--instance NFData Input---- | Outputs can contain information needed to spend the output at a later date.-data Output = Output-    { outputRedeemScript  :: Maybe Script-    , outputWitnessScript :: Maybe Script-    , outputHDKeypaths    :: HashMap PubKeyI (Fingerprint, [KeyIndex])-    , outputUnknown       :: UnknownMap-    } deriving (Show, Eq, Generic)--instance NFData Output---- | A map of raw PSBT keys to byte strings for extra data. The 'keyType' field cannot overlap with any of the reserved--- 'keyType' fields specified in the PSBT specification.-newtype UnknownMap = UnknownMap { unknownMap :: HashMap Key ByteString }-    deriving (Show, Eq, Semigroup, Monoid, Generic)--instance NFData UnknownMap---- | Raw keys for the map type used in PSBTs.-data Key = Key-    { keyType :: Word8-    , key     :: ByteString-    } deriving (Show, Eq, Generic)--instance NFData Key--instance Hashable Key---- | Take two 'PartiallySignedTransaction's and merge them. The 'unsignedTransaction' field in both must be the same.-merge :: PartiallySignedTransaction -> PartiallySignedTransaction -> Maybe PartiallySignedTransaction-merge psbt1 psbt2-    | unsignedTransaction psbt1 == unsignedTransaction psbt2-    = Just $ psbt1-        { globalUnknown = globalUnknown psbt1 <> globalUnknown psbt2-        , inputs = zipWith mergeInput (inputs psbt1) (inputs psbt2)-        , outputs = zipWith mergeOutput (outputs psbt1) (outputs psbt2)-        }-merge _ _ = Nothing--mergeInput :: Input -> Input -> Input-mergeInput a b = Input-    { nonWitnessUtxo = if isJust witUtx then Nothing else nonWitnessUtxo a <|> nonWitnessUtxo b-    , witnessUtxo = witUtx-    , sigHashType = sigHashType a <|> sigHashType b-    , partialSigs = partialSigs a <> partialSigs b-    , inputHDKeypaths = inputHDKeypaths a <> inputHDKeypaths b-    , inputUnknown = inputUnknown a <> inputUnknown b-    , inputRedeemScript = inputRedeemScript a <|> inputRedeemScript b-    , inputWitnessScript = inputWitnessScript a <|> inputWitnessScript b-    , finalScriptSig = finalScriptSig a <|> finalScriptSig b-    , finalScriptWitness = finalScriptWitness a <|> finalScriptWitness b-    }-  where-    witUtx = witnessUtxo a <|> witnessUtxo b--mergeOutput :: Output -> Output -> Output-mergeOutput a b = Output-    { outputRedeemScript = outputRedeemScript a <|> outputRedeemScript b-    , outputWitnessScript = outputWitnessScript a <|> outputWitnessScript b-    , outputHDKeypaths = outputHDKeypaths a <> outputHDKeypaths b-    , outputUnknown = outputUnknown a <> outputUnknown b-    }---- | Take partial signatures from all of the 'Input's and finalize the signature.-complete :: PartiallySignedTransaction -> PartiallySignedTransaction-complete psbt = psbt { inputs = map (completeInput . analyzeInputs) (indexed $ inputs psbt) }-  where-    analyzeInputs (i, input) = (outputScript =<< witnessUtxo input <|> nonWitScript, input)-      where-        nonWitScript = getPrevOut i =<< nonWitnessUtxo input--    getPrevOut i tx =-       (txOut tx !!?) . fromIntegral . outPointIndex . prevOutput =<< txIn (unsignedTransaction psbt) !!? i-    xs !!? i = lookup i $ indexed xs--    outputScript = eitherToMaybe . decodeOutputBS . scriptOutput--    completeInput (Nothing, input)     = input-    completeInput (Just script, input) = completeSig input script--    indexed :: [a] -> [(Word32, a)]-    indexed = zip [0..]--completeSig :: Input -> ScriptOutput -> Input-completeSig input (PayPK k) =-    input { finalScriptSig = eitherToMaybe . S.decode =<< HashMap.lookup k (partialSigs input) }-completeSig input (PayPKHash h)-    | [(k, sig)] <- HashMap.toList $ partialSigs input-    , PubKeyAddress h == pubKeyAddr k-    = input { finalScriptSig = Just $ Script [opPushData sig, opPushData (S.encode k)] }-completeSig input (PayMulSig pubKeys m) | length sigs >= m = input { finalScriptSig = finalSig }-  where-    sigs = collectSigs m pubKeys input-    finalSig = Script . (OP_0 :) . (map opPushData sigs <>) . pure . opPushData . S.encode <$> inputRedeemScript input-completeSig input (PayScriptHash h)-    | Just rdmScript <- inputRedeemScript input-    , PayScriptHash h == toP2SH rdmScript-    , Right decodedScript <- decodeOutput rdmScript-    , not (isPayScriptHash decodedScript)-    = completeSig input decodedScript-completeSig input (PayWitnessPKHash h)-    | [(k, sig)] <- HashMap.toList $ partialSigs input-    , PubKeyAddress h == pubKeyAddr k-    = input { finalScriptWitness = Just [sig, S.encode k]-            , finalScriptSig = Script . pure . opPushData . S.encode <$> inputRedeemScript input-            }-completeSig input (PayWitnessScriptHash h)-    | Just witScript <- inputWitnessScript input-    , PayWitnessScriptHash h == toP2WSH witScript-    , Right decodedScript <- decodeOutput witScript-    = completeWitnessSig input decodedScript-completeSig input _ = input--completeWitnessSig :: Input -> ScriptOutput -> Input-completeWitnessSig input script@(PayMulSig pubKeys m) | length sigs >= m = input-    { finalScriptWitness = Just finalWit-    , finalScriptSig = finalSig-    }-  where-    sigs = collectSigs m pubKeys input-    finalSig = Script . pure . opPushData . S.encode <$> inputRedeemScript input-    finalWit = mempty : sigs <> [encodeOutputBS script]-completeWitnessSig input _ = input--collectSigs :: Int -> [PubKeyI] -> Input -> [ByteString]-collectSigs m pubKeys input = take m . reverse $ foldl' lookupKey [] pubKeys-  where-    lookupKey sigs key = maybe sigs (:sigs) $ HashMap.lookup key (partialSigs input)---- | Take a finalized 'PartiallySignedTransaction' and produce the signed final transaction.  You may need to call--- 'complete' on the 'PartiallySignedTransaction' before producing the final transaction.-finalTransaction :: PartiallySignedTransaction -> Tx-finalTransaction psbt = setInputs . foldl' finalizeInput ([], []) $ zip (txIn tx) (inputs psbt)-  where-    tx = unsignedTransaction psbt-    hasWitness = any (isJust . finalScriptWitness) (inputs psbt)-    setInputs (ins, witData) = tx { txIn = reverse ins, txWitness = if hasWitness then reverse witData else [] }-    finalizeInput (ins, witData) (txInput, psbtInput) = maybe finalWitness finalScript $ finalScriptSig psbtInput-      where-        finalScript script = (txInput { scriptInput = encode script }:ins, []:witData)-        finalWitness = (ins, fromMaybe [] (finalScriptWitness psbtInput):witData)---- | Take an unsigned transaction and produce an empty 'PartiallySignedTransaction'-emptyPSBT :: Tx -> PartiallySignedTransaction-emptyPSBT tx = PartiallySignedTransaction-    { unsignedTransaction = tx-    , globalUnknown = mempty-    , inputs = replicate (length (txIn tx)) emptyInput-    , outputs = replicate (length (txOut tx)) emptyOutput-    }--emptyInput :: Input-emptyInput = Input-    Nothing Nothing HashMap.empty Nothing-    Nothing Nothing HashMap.empty-    Nothing Nothing (UnknownMap HashMap.empty)--emptyOutput :: Output-emptyOutput = Output Nothing Nothing HashMap.empty (UnknownMap HashMap.empty)--instance Serialize PartiallySignedTransaction where-    get = do-        magic <- getBytes 4-        guard $ magic == "psbt"-        headerSep <- getWord8-        guard $ headerSep == 0xff--        keySize <- getWord8-        guard $ keySize == 1-        globalUnsignedTxType <- getWord8-        guard $ globalUnsignedTxType == 0x00-        unsignedTransaction <- getSizedBytes-        guard $ all (B.null . scriptInput) (txIn unsignedTransaction)-        guard $ null (txWitness unsignedTransaction)--        globalUnknown <- get-        globalEnd <- getWord8-        guard $ globalEnd == 0x00--        inputs <- replicateM (length $ txIn unsignedTransaction) get-        outputs <- replicateM (length $ txOut unsignedTransaction) get--        return PartiallySignedTransaction { unsignedTransaction, globalUnknown, inputs, outputs }--    put PartiallySignedTransaction{ unsignedTransaction, globalUnknown, inputs, outputs } = do-        putByteString "psbt"-        putWord8 0xff -- Header separator--        putWord8 0x01 -- Key size-        putWord8 0x00 -- Unsigned Transaction type-        putSizedBytes unsignedTransaction-        put globalUnknown-        putWord8 0x00 -- Global end--        mapM_ put inputs-        mapM_ put outputs--instance Serialize Key where-    get = do-        VarInt keySize <- get-        guard $ keySize > 0-        t <- getWord8-        k <- getBytes (fromIntegral keySize - 1)-        return (Key t k)--    put (Key t k) = do-        putVarInt $ 1 + B.length k-        putWord8 t-        putByteString k--instance Serialize UnknownMap where-    get = go HashMap.empty-      where-        getItem m = do-            k <- get-            VarString v <- get-            go $ HashMap.insert k v m-        go m = do-            isEnd <- lookAhead getWord8-            if isEnd == 0x00-                then return (UnknownMap m)-                else getItem m--    put (UnknownMap m) = void $ HashMap.traverseWithKey (\k v -> put k >> put (VarString v)) m--instance Serialize Input where-    get = getMap getInputItem setInputUnknown emptyInput-      where-        setInputUnknown f input = input { inputUnknown = UnknownMap $ f (unknownMap $ inputUnknown input) }--    put Input { nonWitnessUtxo, witnessUtxo, partialSigs, sigHashType-                  , inputRedeemScript, inputWitnessScript, inputHDKeypaths-                  , finalScriptSig, finalScriptWitness, inputUnknown-                  } = do-        whenJust (putKeyValue InNonWitnessUtxo) nonWitnessUtxo-        whenJust (putKeyValue InWitnessUtxo) witnessUtxo-        putPartialSig partialSigs-        whenJust putSigHash sigHashType-        whenJust (putKeyValue InRedeemScript) inputRedeemScript-        whenJust (putKeyValue InWitnessScript) inputWitnessScript-        putHDPath InBIP32Derivation inputHDKeypaths-        whenJust (putKeyValue InFinalScriptSig) finalScriptSig-        whenJust (putKeyValue InFinalScriptWitness) finalScriptWitness-        put inputUnknown-        putWord8 0x00-      where-        putPartialSig = putPubKeyMap InPartialSig . fmap VarString-        putSigHash sigHash = do-            putKey InSigHashType-            putWord8 0x04-            putWord32le (fromIntegral sigHash)--instance Serialize Output where-    get = getMap getOutputItem setOutputUnknown emptyOutput-      where-        setOutputUnknown f output = output { outputUnknown = UnknownMap $ f (unknownMap $ outputUnknown output) }--    put Output{ outputRedeemScript, outputWitnessScript, outputHDKeypaths, outputUnknown } = do-        whenJust (putKeyValue OutRedeemScript) outputRedeemScript-        whenJust (putKeyValue OutWitnessScript) outputWitnessScript-        putHDPath OutBIP32Derivation outputHDKeypaths-        put outputUnknown-        putWord8 0x00--putSizedBytes :: Serialize a => a -> Put-putSizedBytes a = putVarInt (B.length bs) >> putByteString bs-  where bs = encode a--getSizedBytes :: Serialize a => Get a-getSizedBytes = getNested (fromIntegral . getVarInt <$> get) get--putKeyValue :: (Enum t, Serialize v) => t -> v -> Put-putKeyValue t v = putKey t >> putSizedBytes v--putKey :: Enum t => t -> Put-putKey t = putVarInt (1 :: Word8) >> putWord8 (enumWord8 t)--getMap :: (Bounded t, Enum t)-       => (Int -> a -> t -> Get a)-       -> ((HashMap Key ByteString -> HashMap Key ByteString) -> a -> a)-       -> a -> Get a-getMap getMapItem setUnknown = go-  where-    getItem keySize m (Right t) = getMapItem (fromIntegral keySize - 1) m t >>= go-    getItem keySize m (Left t)  = do-        k <- getBytes (fromIntegral keySize - 1)-        VarString v <- get-        go $ setUnknown (HashMap.insert (Key t k) v) m-    go m = do-        keySize <- getVarInt <$> get-        if keySize == 0-            then return m-            else getItem keySize m =<< (word8Enum <$> getWord8)--data InputType-    = InNonWitnessUtxo-    | InWitnessUtxo-    | InPartialSig-    | InSigHashType-    | InRedeemScript-    | InWitnessScript-    | InBIP32Derivation-    | InFinalScriptSig-    | InFinalScriptWitness-    deriving (Show, Eq, Enum, Bounded, Generic)--instance NFData InputType--data OutputType-    = OutRedeemScript-    | OutWitnessScript-    | OutBIP32Derivation-    deriving (Show, Eq, Enum, Bounded, Generic)--instance NFData OutputType--getInputItem :: Int -> Input -> InputType -> Get Input-getInputItem 0 input@Input{nonWitnessUtxo = Nothing} InNonWitnessUtxo = do-    utxo <- getSizedBytes-    return $ input { nonWitnessUtxo = Just utxo }-getInputItem 0 input@Input{witnessUtxo = Nothing} InWitnessUtxo = do-    utxo <- getSizedBytes-    return $ input { witnessUtxo = Just utxo }-getInputItem keySize input InPartialSig = do-    (k, v) <- getPartialSig-    return $ input { partialSigs = HashMap.insert k v (partialSigs input) }-  where-    getPartialSig = (,) <$> isolate keySize get <*> (getVarString <$> get)-getInputItem 0 input@Input{sigHashType = Nothing} InSigHashType = do-    VarInt size <- get-    guard $ size == 0x04-    sigHash <- fromIntegral <$> getWord32le-    return $ input { sigHashType = Just sigHash }-getInputItem 0 input@Input{inputRedeemScript = Nothing} InRedeemScript = do-    script <- getSizedBytes-    return $ input { inputRedeemScript = Just script }-getInputItem 0 input@Input{inputWitnessScript = Nothing} InWitnessScript = do-    script <- getSizedBytes-    return $ input { inputWitnessScript = Just script }-getInputItem keySize input InBIP32Derivation = do-    (k, v) <- getHDPath keySize-    return $ input { inputHDKeypaths = HashMap.insert k v (inputHDKeypaths input) }-getInputItem 0 input@Input{finalScriptSig = Nothing} InFinalScriptSig = do-    script <- getSizedBytes-    return $ input { finalScriptSig = Just script }-getInputItem 0 input@Input{finalScriptWitness = Nothing} InFinalScriptWitness = do-    scripts <- map getVarString <$> getVarIntList-    return $ input { finalScriptWitness = Just scripts }-  where-    getVarIntList = do-        VarInt n <- get-        replicateM (fromIntegral n) get-getInputItem keySize input inputType = fail $-    "Incorrect key size for input item or item already existed: " <>-    show (keySize, input, inputType)--getOutputItem :: Int -> Output -> OutputType -> Get Output-getOutputItem 0 output@Output{outputRedeemScript = Nothing} OutRedeemScript = do-    script <- getSizedBytes-    return $ output { outputRedeemScript = Just script }-getOutputItem 0 output@Output{outputWitnessScript = Nothing} OutWitnessScript = do-    script <- getSizedBytes-    return $ output { outputWitnessScript = Just script }-getOutputItem keySize output OutBIP32Derivation = do-    (k, v) <- getHDPath keySize-    return $ output { outputHDKeypaths = HashMap.insert k v (outputHDKeypaths output) }-getOutputItem keySize output outputType = fail $-    "Incorrect key size for output item or item already existed: " <>-    show (keySize, output, outputType)--getHDPath :: Int -> Get (PubKeyI, (Fingerprint, [KeyIndex]))-getHDPath keySize = (,) <$> isolate keySize get <*> (unPSBTHDPath <$> get)--putHDPath :: Enum t => t -> HashMap PubKeyI (Fingerprint, [KeyIndex]) -> Put-putHDPath t = putPubKeyMap t . fmap PSBTHDPath--newtype PSBTHDPath = PSBTHDPath { unPSBTHDPath :: (Fingerprint, [KeyIndex]) }-    deriving (Show, Eq, Generic)--instance NFData PSBTHDPath--instance Serialize PSBTHDPath where-    get = do-        VarInt valueSize <- get-        guard $ valueSize `mod` 4 == 0-        let numIndices = (fromIntegral valueSize - 4) `div` 4-        PSBTHDPath <$>-            isolate-                (fromIntegral valueSize)-                ((,) <$> getWord32le <*> getKeyIndexList numIndices)-      where-        getKeyIndexList n = replicateM n getWord32le-    put (PSBTHDPath (fp, kis)) = putVarInt (B.length bs) >> putByteString bs-      where-        bs = runPut $ putWord32le fp >> mapM_ putWord32le kis--putPubKeyMap :: (Serialize a, Enum t) => t -> HashMap PubKeyI a -> Put-putPubKeyMap t = void . HashMap.traverseWithKey putItem-  where-    putItem k v = put (Key (enumWord8 t) (encode k)) >> put v--enumWord8 :: Enum a => a -> Word8-enumWord8 = fromIntegral . fromEnum--word8Enum :: forall a. (Bounded a, Enum a) => Word8 -> Either Word8 a-word8Enum n | n <= enumWord8 (maxBound :: a) = Right . toEnum $ fromIntegral n-word8Enum n = Left n--whenJust :: Monad m => (a -> m ()) -> Maybe a -> m ()-whenJust = maybe (return ())
− src/Network/Haskoin/Transaction/Segwit.hs
@@ -1,141 +0,0 @@-{-# LANGUAGE LambdaCase        #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TupleSections     #-}-{-|-Module      : Network.Haskoin.Transaction.Segwit-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Types to represent segregated witness data and auxilliary functions to-manipulate it.  See [BIP 141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki)-and [BIP 143](https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki) for-details.--}-module Network.Haskoin.Transaction.Segwit-    ( WitnessProgram (..)-    , WitnessProgramPKH (..)-    , WitnessProgramSH (..)-    , isSegwit--    , viewWitnessProgram-    , decodeWitnessInput--    , calcWitnessProgram-    , simpleInputStack-    , toWitnessStack-    ) where--import           Data.ByteString                    (ByteString)-import qualified Data.Serialize                     as S-import           Network.Haskoin.Constants          (Network)-import           Network.Haskoin.Keys.Common        (PubKeyI)-import           Network.Haskoin.Script.Common      (Script (..),-                                                     ScriptOutput (..),-                                                     decodeOutput, encodeOutput)-import           Network.Haskoin.Script.SigHash     (TxSignature (..),-                                                     decodeTxSig, encodeTxSig)-import           Network.Haskoin.Script.Standard    (ScriptInput (..),-                                                     SimpleInput (..))-import           Network.Haskoin.Transaction.Common (WitnessStack)---- | Test if a 'ScriptOutput' is P2WPKH or P2WSH------ @since 0.11.0.0-isSegwit :: ScriptOutput -> Bool-isSegwit = \case-    PayWitnessPKHash{}     -> True-    PayWitnessScriptHash{} -> True-    _                      -> False---- | High level represenation of a (v0) witness program------ @since 0.11.0.0-data WitnessProgram = P2WPKH WitnessProgramPKH | P2WSH WitnessProgramSH | EmptyWitnessProgram-    deriving (Eq, Show)---- | Encode a witness program------ @since 0.11.0.0-toWitnessStack :: WitnessProgram -> WitnessStack-toWitnessStack = \case-    P2WPKH (WitnessProgramPKH sig key) -> [encodeTxSig sig, S.encode key]-    P2WSH (WitnessProgramSH stack scr) -> stack <> [S.encode scr]-    EmptyWitnessProgram                -> mempty---- | High level representation of a P2WPKH witness------ @since 0.11.0.0-data WitnessProgramPKH = WitnessProgramPKH-    { witnessSignature :: !TxSignature-    , witnessPubKey    :: !PubKeyI-    } deriving (Eq, Show)---- | High-level representation of a P2WSH witness------ @since 0.11.0.0-data WitnessProgramSH = WitnessProgramSH-    { witnessScriptHashStack  :: ![ByteString]-    , witnessScriptHashScript :: !Script-    } deriving (Eq, Show)---- | Calculate the witness program from the transaction data------ @since 0.11.0.0-viewWitnessProgram :: Network -> ScriptOutput -> WitnessStack -> Either String WitnessProgram-viewWitnessProgram net so witness = case so of-    PayWitnessPKHash _ | length witness == 2 -> do-        sig    <- decodeTxSig net $ head witness-        pubkey <- S.decode $ witness !! 1-        return . P2WPKH $ WitnessProgramPKH sig pubkey-    PayWitnessScriptHash _ | not (null witness) -> do-        redeemScript <- S.decode $ last witness-        return . P2WSH $ WitnessProgramSH (init witness) redeemScript-    _ | null witness -> return EmptyWitnessProgram-      | otherwise    -> Left "viewWitnessProgram: Invalid witness program"---- | Analyze the witness, trying to match it with standard input structures------ @since 0.11.0.0-decodeWitnessInput :: Network -> WitnessProgram -> Either String (Maybe ScriptOutput, SimpleInput)-decodeWitnessInput net = \case-    P2WPKH (WitnessProgramPKH sig key) -> return (Nothing, SpendPKHash sig key)-    P2WSH (WitnessProgramSH st scr) -> do-        so <- decodeOutput scr-        fmap (Just so, ) $ case (so, st) of-            (PayPK k, [sigBS]) ->-                SpendPK <$> decodeTxSig net sigBS-            (PayPKHash h, [sigBS, keyBS]) ->-                SpendPKHash <$> decodeTxSig net sigBS <*> S.decode keyBS-            (PayMulSig ps r, "" : sigsBS) ->-                SpendMulSig <$> traverse (decodeTxSig net) sigsBS-            _ -> Left "decodeWitnessInput: Non-standard script output"-    EmptyWitnessProgram -> Left "decodeWitnessInput: Empty witness program"---- | Create the witness program for a standard input------ @since 0.11.0.0-calcWitnessProgram :: ScriptOutput -> ScriptInput -> Either String WitnessProgram-calcWitnessProgram so si = case (so, si) of-    (PayWitnessPKHash{}, RegularInput (SpendPKHash sig pk)) -> p2wpkh sig pk-    (PayScriptHash{}, RegularInput (SpendPKHash sig pk))    -> p2wpkh sig pk-    (PayWitnessScriptHash{}, ScriptHashInput i o)           -> p2wsh i o-    (PayScriptHash{}, ScriptHashInput i o)                  -> p2wsh i o-    _ -> Left "calcWitnessProgram: Invalid segwit SigInput"-  where-    p2wpkh sig = return . P2WPKH . WitnessProgramPKH sig-    p2wsh i o  = return . P2WSH $ WitnessProgramSH (simpleInputStack i) (encodeOutput o)---- | Create the witness stack required to spend a standard P2WSH input------ @since 0.11.0.0-simpleInputStack :: SimpleInput -> [ByteString]-simpleInputStack = \case-    SpendPK sig       -> [f sig]-    SpendPKHash sig k -> [f sig, S.encode k]-    SpendMulSig sigs  -> "" : fmap f sigs-  where-    f TxSignatureEmpty = ""-    f sig              = encodeTxSig sig
− src/Network/Haskoin/Util.hs
@@ -1,196 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses #-}-{-|-Module      : Network.Haskoin.Util-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--This module defines various utility functions used across the library.--}-module Network.Haskoin.Util-    (-      -- * ByteString Helpers-      bsToInteger-    , integerToBS-    , encodeHex-    , decodeHex-    , getBits--      -- * Maybe & Either Helpers-    , eitherToMaybe-    , maybeToEither-    , liftEither-    , liftMaybe--      -- * Other Helpers-    , updateIndex-    , matchTemplate-    , convertBits--      -- * Triples-    , fst3-    , snd3-    , lst3--      -- * JSON Utilities-    , dropFieldLabel-    , dropSumLabels--    ) where--import           Control.Monad          (guard)-import           Control.Monad.Except   (ExceptT (..), liftEither)-import           Data.Aeson.Types       (Options (..), SumEncoding (..),-                                         defaultOptions, defaultTaggedObject)-import           Data.Bits-import           Data.ByteString        (ByteString)-import qualified Data.ByteString        as BS-import qualified Data.ByteString.Base16 as B16-import           Data.Char              (toLower)-import           Data.List-import           Data.Text              (Text)-import qualified Data.Text.Encoding     as E-import           Data.Word              (Word8)---- ByteString helpers---- | Decode a big endian 'Integer' from a 'ByteString'.-bsToInteger :: ByteString -> Integer-bsToInteger = BS.foldr f 0 . BS.reverse-  where-    f w n = toInteger w .|. shiftL n 8---- | Encode an 'Integer' to a 'ByteString' as big endian.-integerToBS :: Integer -> ByteString-integerToBS 0 = BS.pack [0]-integerToBS i-    | i > 0     = BS.reverse $ BS.unfoldr f i-    | otherwise = error "integerToBS not defined for negative values"-  where-    f 0 = Nothing-    f x = Just (fromInteger x :: Word8, x `shiftR` 8)---- | Encode as string of human-readable hex characters.-encodeHex :: ByteString -> Text-encodeHex = E.decodeUtf8 . B16.encode---- | Decode string of human-readable hex characters.-decodeHex :: Text -> Maybe ByteString-decodeHex text =-    let (x, b) = B16.decode (E.encodeUtf8 text)-    in guard (b == BS.empty) >> return x---- | Obtain 'Int' bits from beginning of 'ByteString'. Resulting 'ByteString'--- will be smallest required to hold that many bits, padded with zeroes to the--- right.-getBits :: Int -> ByteString -> ByteString-getBits b bs-    | r == 0 = BS.take q bs-    | otherwise = i `BS.snoc` l-  where-    (q, r) = b `quotRem` 8-    s = BS.take (q + 1) bs-    i = BS.init s-    l = BS.last s .&. (0xff `shiftL` (8 - r)) -- zero unneeded bits---- Maybe and Either monad helpers---- | Transform an 'Either' value into a 'Maybe' value. 'Right' is mapped to--- 'Just' and 'Left' is mapped to 'Nothing'. The value inside 'Left' is lost.-eitherToMaybe :: Either a b -> Maybe b-eitherToMaybe (Right b) = Just b-eitherToMaybe _         = Nothing---- | Transform a 'Maybe' value into an 'Either' value. 'Just' is mapped to--- 'Right' and 'Nothing' is mapped to 'Left'. Default 'Left' required.-maybeToEither :: b -> Maybe a -> Either b a-maybeToEither err = maybe (Left err) Right---- | Lift a 'Maybe' computation into the 'ExceptT' monad.-liftMaybe :: Monad m => b -> Maybe a -> ExceptT b m a-liftMaybe err = liftEither . maybeToEither err---- Various helpers---- | Applies a function to only one element of a list defined by its index.  If--- the index is out of the bounds of the list, the original list is returned.-updateIndex :: Int      -- ^ index of the element to change-            -> [a]      -- ^ list of elements-            -> (a -> a) -- ^ function to apply-            -> [a]      -- ^ result with one element changed-updateIndex i xs f-    | i < 0 || i >= length xs = xs-    | otherwise = l ++ (f h : r)-  where-    (l,h:r) = splitAt i xs---- | Use the list @[b]@ as a template and try to match the elements of @[a]@--- against it. For each element of @[b]@ return the (first) matching element of--- @[a]@, or 'Nothing'. Output list has same size as @[b]@ and contains results--- in same order. Elements of @[a]@ can only appear once.-matchTemplate :: [a]              -- ^ input list-              -> [b]              -- ^ list to serve as a template-              -> (a -> b -> Bool) -- ^ comparison function-              -> [Maybe a]-matchTemplate [] bs _ = replicate (length bs) Nothing-matchTemplate _  [] _ = []-matchTemplate as (b:bs) f = case break (`f` b) as of-    (l,r:rs) -> Just r  : matchTemplate (l ++ rs) bs f-    _        -> Nothing : matchTemplate as bs f---- | Returns the first value of a triple.-fst3 :: (a,b,c) -> a-fst3 (a,_,_) = a---- | Returns the second value of a triple.-snd3 :: (a,b,c) -> b-snd3 (_,b,_) = b---- | Returns the last value of a triple.-lst3 :: (a,b,c) -> c-lst3 (_,_,c) = c---- | Field label goes lowercase and first @n@ characters get removed.-dropFieldLabel :: Int -> Options-dropFieldLabel n = defaultOptions-    { fieldLabelModifier = map toLower . drop n-    }---- | Transformation from 'dropFieldLabel' is applied with argument @f@, plus--- constructor tags are lowercased and first @c@ characters removed. @tag@ is--- used as the name of the object field name that will hold the transformed--- constructor tag as its value.-dropSumLabels :: Int -> Int -> String -> Options-dropSumLabels c f tag = (dropFieldLabel f)-    { constructorTagModifier = map toLower . drop c-    , sumEncoding = defaultTaggedObject { tagFieldName = tag }-    }---- | Convert from one power-of-two base to another, as long as it fits in a--- 'Word'.-convertBits :: Bool -> Int -> Int -> [Word] -> ([Word], Bool)-convertBits pad frombits tobits i = (reverse yout, rem')-  where-    (xacc, xbits, xout) = foldl' outer (0, 0, []) i-    (yout, rem')-        | pad && xbits /= 0 =-            let xout' = (xacc `shiftL` (tobits - xbits)) .&. maxv : xout-            in (xout', False)-        | pad = (xout, False)-        | xbits /= 0 = (xout, True)-        | otherwise = (xout, False)-    maxv = 1 `shiftL` tobits - 1-    max_acc = 1 `shiftL` (frombits + tobits - 1) - 1-    outer (acc, bits, out) it =-        let acc' = ((acc `shiftL` frombits) .|. it) .&. max_acc-            bits' = bits + frombits-            (out', bits'') = inner acc' out bits'-        in (acc', bits'', out')-    inner acc out bits-        | bits >= tobits =-            let bits' = bits - tobits-                out' = ((acc `shiftR` bits') .&. maxv) : out-            in inner acc out' bits'-        | otherwise = (out, bits)
+ test/Haskoin/Address/Bech32Spec.hs view
@@ -0,0 +1,244 @@+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedStrings #-}++module Haskoin.Address.Bech32Spec (spec) where++import Control.Monad+import Data.Bits (xor)+import Data.ByteString (ByteString)+import Data.ByteString qualified as ByteString+import Data.Char (chr, ord, toLower)+import Data.Maybe+import Data.String.Conversions+import Data.Text (Text, append, pack, snoc, uncons)+import Data.Text qualified as Text+import Data.Word (Word8)+import Haskoin.Address+import Haskoin.Address.Bech32+import Haskoin.Util+import Test.HUnit+import Test.Hspec++spec = do+  describe "bech32 checksum" $ do+    it "should be valid" $+      forM_ validChecksums (uncurry testValidChecksum)+    it "should be invalid" $+      forM_ invalidChecksums testInvalidChecksum+    it "should be case-insensitive" $+      all ((== Just "test12hrzfj") . flip (bech32Encode Bech32) []) hrpCaseVariants+  describe "bech32 address" $ do+    it "should be valid" $+      forM_ validChecksums (uncurry testValidChecksum)+    it "should be invalid" $+      forM_ invalidChecksums testInvalidChecksum+    it "should be case-insensitive" $+      all ((== Just "test12hrzfj") . flip (bech32Encode Bech32) []) hrpCaseVariants+  describe "bech32 encoding/decoding" $ do+    it "should not encode long data string" $+      assert . isNothing $+        bech32Encode Bech32 "bc" (replicate 82 (word5 (1 :: Word8)))+    it "should not encode bad version number" $+      assert $+        isNothing $+          segwitEncode "bc" 17 []+    it "should not encode invalid length for version 0" $+      assert $+        isNothing $+          segwitEncode "bc" 0 (replicate 30 1)+    it "should relax length restrictions for versions other than 0" $+      assert $+        isJust $+          segwitEncode "bc" 1 (replicate 30 1)+    it "should not encode another long data string" $+      assert $+        isNothing $+          segwitEncode "bc" 1 (replicate 41 1)+    it "should not encode empty human readable part" $+      assert $+        isNothing $+          bech32Encode Bech32 "" []+    it "should not decode empty human-readable part" $+      assert $+        isNothing $+          bech32Decode "10a06t8"+    it "human-readable part should be case-insensitive" $+      bech32Encode Bech32 "HRP" [] `shouldBe` bech32Encode Bech32 "hrp" []++testValidChecksum :: Bech32Encoding -> Bech32 -> Assertion+testValidChecksum enc checksum = case bech32Decode checksum of+  Nothing -> assertFailure (show checksum)+  Just (enc', resultHRP, resultData) -> do+    assertEqual (show checksum ++ " encoding incorrect") enc enc'+    -- test that a corrupted checksum fails decoding.+    let (hrp, rest) = Text.breakOnEnd "1" checksum+        Just (first, rest') = uncons rest+        checksumCorrupted = (hrp `snoc` chr (ord first `xor` 1)) `append` rest'+    assertBool (show checksum ++ " corrupted") $+      isNothing (bech32Decode checksumCorrupted)+    -- test that re-encoding the decoded checksum results in the same checksum.+    let checksumEncoded = bech32Encode enc' resultHRP resultData+        expectedChecksum = Just $ Text.toLower checksum+    assertEqual+      (show checksum ++ " re-encode")+      expectedChecksum+      checksumEncoded++testInvalidChecksum :: Bech32 -> Assertion+testInvalidChecksum checksum =+  assertBool (show checksum) (isNothing $ bech32Decode checksum)++testValidAddress :: (Text, Text) -> Assertion+testValidAddress (address, hexscript) = do+  let address' = Text.toLower address+      hrp = Text.take 2 address'+  case segwitDecode hrp address of+    Nothing ->+      assertFailure (Text.unpack address <> ": decode failed")+    Just (witver, witprog) -> do+      assertEqual+        (show address)+        (decodeHex hexscript)+        (Just $ segwitScriptPubkey witver witprog)+      assertEqual+        (show address)+        (Just address')+        (segwitEncode hrp witver witprog)++testInvalidAddress :: Text -> Assertion+testInvalidAddress address = do+  assertBool (show address) (isNothing $ segwitDecode "bc" address)+  assertBool (show address) (isNothing $ segwitDecode "tb" address)++segwitScriptPubkey :: Word8 -> [Word8] -> ByteString+segwitScriptPubkey witver witprog =+  ByteString.pack $ witver' : fromIntegral (length witprog) : witprog+  where+    witver' = if witver == 0 then 0 else witver + 0x50++validChecksums :: [(Bech32Encoding, Text)]+validChecksums =+  [ ( Bech32,+      "A12UEL5L"+    ),+    ( Bech32,+      "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs"+    ),+    ( Bech32,+      "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"+    ),+    ( Bech32,+      "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j"+    ),+    ( Bech32,+      "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w"+    ),+    ( Bech32m,+      "A1LQFN3A"+    ),+    ( Bech32m,+      "a1lqfn3a"+    ),+    ( Bech32m,+      "an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6"+    ),+    ( Bech32m,+      "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx"+    ),+    ( Bech32m,+      "11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8"+    ),+    ( Bech32m,+      "split1checkupstagehandshakeupstreamerranterredcaperredlc445v"+    ),+    ( Bech32m,+      "?1v759aa"+    )+  ]++invalidChecksums :: [Text]+invalidChecksums =+  [ " 1nwldj5",+    "\DEL1axkwrx",+    "an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx",+    "pzry9x0s0muk",+    "1pzry9x0s0muk",+    "x1b4n0q5v",+    "li1dgmt3",+    "de1lg7wt\xFF"+  ]++validAddresses :: [(Text, Text)]+validAddresses =+  [ ( "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4",+      "0014751e76e8199196d454941c45d1b3a323f1433bd6"+    ),+    ( "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",+      "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"+    ),+    ( "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy",+      "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433"+    ),+    ( "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4",+      "0014751e76e8199196d454941c45d1b3a323f1433bd6"+    ),+    ( "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",+      "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"+    ),+    ( "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y",+      "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6"+    ),+    ( "BC1SW50QGDZ25J",+      "6002751e"+    ),+    ( "bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs",+      "5210751e76e8199196d454941c45d1b3a323"+    ),+    ( "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy",+      "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433"+    ),+    ( "tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c",+      "5120000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433"+    ),+    ( "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0",+      "512079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"+    )+  ]++invalidAddresses :: [Text]+invalidAddresses =+  [ "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty",+    "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5",+    "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2",+    "bc1rw5uspcuh",+    "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90",+    "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P",+    "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7",+    "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du",+    "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv",+    "bc1gmk9yu",+    "tc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq5zuyut",+    "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd",+    "tb1z0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqglt7rf",+    "BC1S0XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ54WELL",+    "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh",+    "tb1q0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq24jc47",+    "bc1p38j9r5y49hruaue7wxjce0updqjuyyx0kh56v8s25huc6995vvpql3jow4",+    "BC130XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ7ZWS8R",+    "bc1pw5dgrnzv",+    "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav253zgeav",+    "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq47Zagq",+    "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v07qwwzcrf",+    "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vpggkg4j"+  ]++hrpCaseVariants :: [Text]+hrpCaseVariants = map Text.pack hrpTestPermutations++hrpTestPermutations :: [String]+hrpTestPermutations = do+  a <- ['t', 'T']+  b <- ['e', 'E']+  c <- ['s', 'S']+  d <- ['t', 'T']+  return [a, b, c, d]
+ test/Haskoin/Address/CashAddrSpec.hs view
@@ -0,0 +1,315 @@+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedStrings #-}++module Haskoin.Address.CashAddrSpec (spec) where++import Control.Monad+import Data.ByteString.Char8 qualified as Char8+import Data.Maybe+import Data.String.Conversions+import Data.Text (Text)+import Haskoin.Address+import Haskoin.Network.Constants+import Haskoin.Util+import Test.HUnit+import Test.Hspec++spec :: Spec+spec = do+  describe "cashaddr checksum test vectors" $ do+    it "prefix:x64nx6hz" $ do+      let mpb = cash32decode "prefix:x64nx6hz"+      mpb `shouldBe` Just ("prefix", "")+    it "p:gpf8m4h7" $ do+      let mpb = cash32decode "p:gpf8m4h7"+      mpb `shouldBe` Just ("p", "")+    it "bitcoincash:qpzry9x8gf2tvdw0s3jn54khce6mua7lcw20ayyn" $ do+      let mpb =+            cash32decode+              "bitcoincash:qpzry9x8gf2tvdw0s3jn54khce6mua7lcw20ayyn"+      mpb+        `shouldBe` Just+          ( "bitcoincash",+            "\NULD2\DC4\199BT\182\&5\207\132e:V\215\198u\190w\223"+          )+    it "bchtest:testnetaddress4d6njnut" $ do+      let mpb = cash32decode "bchtest:testnetaddress4d6njnut"+      mpb `shouldBe` Just ("bchtest", "^`\185\229}kG\152")+    it "bchreg:555555555555555555555555555555555555555555555udxmlmrz" $ do+      let mpb =+            cash32decode+              "bchreg:555555555555555555555555555555555555555555555udxmlmrz"+      mpb+        `shouldBe` Just+          ( "bchreg",+            "\165)JR\148\165)JR\148\165)JR\148\165)JR\148\165)JR\148\165)J"+          )+  describe "cashaddr to base58 translation test vectors" $ do+    it "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu" $ do+      let addr =+            addrToText bch+              =<< textToAddr btc "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu"+      addr+        `shouldBe` Just "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"+    it "1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR" $ do+      let addr =+            addrToText bch+              =<< textToAddr btc "1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR"+      addr+        `shouldBe` Just "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy"+    it "16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb" $ do+      let addr =+            addrToText bch+              =<< textToAddr btc "16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb"+      addr+        `shouldBe` Just "bitcoincash:qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r"+    it "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC" $ do+      let addr =+            addrToText bch+              =<< textToAddr btc "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC"+      addr+        `shouldBe` Just "bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq"+    it "3LDsS579y7sruadqu11beEJoTjdFiFCdX4" $ do+      let addr =+            addrToText bch+              =<< textToAddr btc "3LDsS579y7sruadqu11beEJoTjdFiFCdX4"+      addr+        `shouldBe` Just "bitcoincash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e"+    it "31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw" $ do+      let addr =+            addrToText bch+              =<< textToAddr btc "31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw"+      addr+        `shouldBe` Just "bitcoincash:pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37"+  describe "base58 to cashaddr translation test vectors" $ do+    it "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a" $ do+      let addr =+            addrToText btc+              =<< textToAddr+                bch+                "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"+      addr `shouldBe` Just "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu"+    it "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy" $ do+      let addr =+            addrToText btc+              =<< textToAddr+                bch+                "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy"+      addr `shouldBe` Just "1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR"+    it "bitcoincash:qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r" $ do+      let addr =+            addrToText btc+              =<< textToAddr+                bch+                "bitcoincash:qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r"+      addr `shouldBe` Just "16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb"+    it "bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq" $ do+      let addr =+            addrToText btc+              =<< textToAddr+                bch+                "bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq"+      addr `shouldBe` Just "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC"+    it "bitcoincash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e" $ do+      let addr =+            addrToText btc+              =<< textToAddr+                bch+                "bitcoincash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e"+      addr `shouldBe` Just "3LDsS579y7sruadqu11beEJoTjdFiFCdX4"+    it "bitcoincash:pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37" $ do+      let addr =+            addrToText btc+              =<< textToAddr+                bch+                "bitcoincash:pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37"+      addr `shouldBe` Just "31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw"+  describe "cashaddr larger test vectors" $+    forM_ (zip [0 ..] vectors) $ \(i, vec) ->+      it ("cashaddr test vector " <> show (i :: Int)) $ testCashAddr vec++{- Various utilities -}++testCashAddr :: (Int, CashVersion, Cash32, Text) -> Assertion+testCashAddr (len, typ, addr, hex) = do+  let mbs = decodeHex hex+  assertBool "Could not decode hex payload from test vector" (isJust mbs)+  let mlow = cash32decode addr+  assertBool "Could not decode low level address" (isJust mlow)+  let Just (_, lbs) = mlow+  assertEqual "Low-level payload size incorrect" len (Char8.length lbs - 1)+  assertEqual "Low-level payload doesn't match" bs (Char8.tail lbs)+  let mdec = cash32decodeType addr+  assertBool ("Could not decode test address: " <> cs addr) (isJust mdec)+  assertEqual "Length doesn't match" len (Char8.length pay)+  assertEqual "Version doesn't match" typ ver+  assertEqual "Payload doesn't match" bs pay+  where+    Just bs = decodeHex hex+    Just (_, ver, pay) = cash32decodeType addr++-- | All vectors starting with @pref@ had the wrong version in the spec+-- document.+vectors :: [(Int, CashVersion, Text, Text)]+vectors =+  [ ( 20,+      0,+      "bitcoincash:qr6m7j9njldwwzlg9v7v53unlr4jkmx6eylep8ekg2",+      "F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9"+    ),+    ( 20,+      1,+      "bchtest:pr6m7j9njldwwzlg9v7v53unlr4jkmx6eyvwc0uz5t",+      "F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9"+    ),+    ( 20,+      1,+      "pref:pr6m7j9njldwwzlg9v7v53unlr4jkmx6ey65nvtks5",+      "F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9"+    ),+    ( 20,+      15,+      "prefix:0r6m7j9njldwwzlg9v7v53unlr4jkmx6ey3qnjwsrf",+      "F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9"+    ),+    ( 24,+      0,+      "bitcoincash:q9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2ws4mr9g0",+      "7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA"+    ),+    ( 24,+      1,+      "bchtest:p9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2u94tsynr",+      "7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA"+    ),+    ( 24,+      1,+      "pref:p9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2khlwwk5v",+      "7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA"+    ),+    ( 24,+      15,+      "prefix:09adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2p29kc2lp",+      "7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA"+    ),+    ( 28,+      0,+      "bitcoincash:qgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcw59jxxuz",+      "3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B"+    ),+    ( 28,+      1,+      "bchtest:pgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcvs7md7wt",+      "3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B"+    ),+    ( 28,+      1,+      "pref:pgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcrsr6gzkn",+      "3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B"+    ),+    ( 28,+      15,+      "prefix:0gagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkc5djw8s9g",+      "3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B"+    ),+    ( 32,+      0,+      "bitcoincash:qvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq5nlegake",+      "3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060"+    ),+    ( 32,+      1,+      "bchtest:pvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq7fqng6m6",+      "3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060"+    ),+    ( 32,+      1,+      "pref:pvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq4k9m7qf9",+      "3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060"+    ),+    ( 32,+      15,+      "prefix:0vch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxqsh6jgp6w",+      "3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060"+    ),+    ( 40,+      0,+      "bitcoincash:qnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklv39gr3uvz",+      "C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB"+    ),+    ( 40,+      1,+      "bchtest:pnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklvmgm6ynej",+      "C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB"+    ),+    ( 40,+      1,+      "pref:pnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklv0vx5z0w3",+      "C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB"+    ),+    ( 40,+      15,+      "prefix:0nq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklvwsvctzqy",+      "C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB"+    ),+    ( 48,+      0,+      "bitcoincash:qh3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqex2w82sl",+      "E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C"+    ),+    ( 48,+      1,+      "bchtest:ph3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqnzf7mt6x",+      "E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C"+    ),+    ( 48,+      1,+      "pref:ph3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqjntdfcwg",+      "E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C"+    ),+    ( 48,+      15,+      "prefix:0h3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqakcssnmn",+      "E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C"+    ),+    ( 56,+      0,+      "bitcoincash:qmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqscw8jd03f",+      "D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041"+    ),+    ( 56,+      1,+      "bchtest:pmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqs6kgdsg2g",+      "D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041"+    ),+    ( 56,+      1,+      "pref:pmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqsammyqffl",+      "D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041"+    ),+    ( 56,+      15,+      "prefix:0mvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqsgjrqpnw8",+      "D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041"+    ),+    ( 64,+      0,+      "bitcoincash:qlg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96mtky5sv5w",+      "D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B"+    ),+    ( 64,+      1,+      "bchtest:plg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96mc773cwez",+      "D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B"+    ),+    ( 64,+      1,+      "pref:plg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96mg7pj3lh8",+      "D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B"+    ),+    ( 64,+      15,+      "prefix:0lg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96ms92w6845",+      "D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B"+    )+  ]
+ test/Haskoin/AddressSpec.hs view
@@ -0,0 +1,197 @@+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedStrings #-}++module Haskoin.AddressSpec (spec) where++import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Default (def)+import Data.Maybe (fromJust, isJust)+import Data.Text (Text)+import Data.Text qualified as T+import Haskoin.Address+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.HUnit+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck++identityTests :: IdentityTests+identityTests =+  def+    { readTests = [ReadBox arbitraryAddressAll],+      serialTests = [SerialBox arbitraryAddressAll],+      marshalJsonTests = [MarshalJsonBox arbitraryNetAddress]+    }++spec :: Spec+spec = prepareContext $ \ctx -> do+  testIdentity identityTests+  describe "Address properties" $ do+    prop "encodes and decodes base58 bytestring" $+      forAll arbitraryBS $ \bs ->+        decodeBase58 (encodeBase58 bs) == Just bs+    prop "encodes and decodes base58 bytestring with checksum" $+      forAll arbitraryBS $ \bs ->+        decodeBase58Check (encodeBase58Check bs) == Just bs+    prop "textToAddr . addrToText identity" $+      forAll arbitraryNetAddress $ \(net, a) ->+        (textToAddr net =<< addrToText net a) == Just a+    prop "outputAddress . addressToOutput identity" $+      forAll arbitraryAddress $ \a ->+        outputAddress ctx (addressToOutput a) == Just a+  describe "Address vectors" $ do+    it "Passes Base58 vectors 1" $+      mapM_ testVector vectors+    it "Passes Base58 vectors 2" $+      mapM_ testBase58Vector base58Vectors+    it "Passes Base58 invalid decoding vectors" $+      mapM_ testBase58InvalidVector base58InvalidVectors+    it "Passes Base58Check invalid decoding vectors" $+      mapM_ testBase58ChkInvalidVector base58ChkInvalidVectors+    it "Passes addresses witness p2sh(pwpkh) vectors" $+      mapM_ (testCompatWitnessVector ctx) compatWitnessVectors++testVector :: (ByteString, Text, Text) -> Assertion+testVector (bs, e, chk) = do+  assertEqual "encodeBase58" e b58+  assertEqual "encodeBase58Check" chk b58Chk+  assertEqual "decodeBase58" (Just bs) (decodeBase58 b58)+  assertEqual "decodeBase58Check" (Just bs) (decodeBase58Check b58Chk)+  where+    b58 = encodeBase58 bs+    b58Chk = encodeBase58Check bs++vectors :: [(ByteString, Text, Text)]+vectors =+  [ (B.empty, "", "3QJmnh"),+    (B.pack [0], "1", "1Wh4bh"),+    (B.pack [0, 0, 0, 0], "1111", "11114bdQda"),+    (B.pack [0, 0, 1, 0, 0], "11LUw", "113CUwsFVuo"),+    (B.pack [255], "5Q", "VrZDWwe"),+    ( B.pack [0, 0, 0, 0] `B.append` B.pack [1 .. 255],+      "1111cWB5HCBdLjAuqGGReWE3R3CguuwSjw6RHn39s2yuDRTS5N\+      \sBgNiFpWgAnEx6VQi8csexkgYw3mdYrMHr8x9i7aEwP8kZ7vcc\+      \XWqKDvGv3u1GxFKPuAkn8JCPPGDMf3vMMnbzm6Nh9zh1gcNsMv\+      \H3ZNLmP5fSG6DGbbi2tuwMWPthr4boWwCxf7ewSgNQeacyozhK\+      \DDQQ1qL5fQFUW52QKUZDZ5fw3KXNQJMcNTcaB723LchjeKun7M\+      \uGW5qyCBZYzA1KjofN1gYBV3NqyhQJ3Ns746GNuf9N2pQPmHz4\+      \xpnSrrfCvy6TVVz5d4PdrjeshsWQwpZsZGzvbdAdN8MKV5QsBDY",+      "111151KWPPBRzdWPr1ASeu172gVgLf1YfUp6VJyk6K9t4cLqYt\+      \FHcMa2iX8S3NJEprUcW7W5LvaPRpz7UG7puBj5STE3nKhCGt5e\+      \ckYq7mMn5nT7oTTic2BAX6zDdqrmGCnkszQkzkz8e5QLGDjf7K\+      \eQgtEDm4UER6DMSdBjFQVa6cHrrJn9myVyyhUrsVnfUk2WmNFZ\+      \vkWv3Tnvzo2cJ1xW62XDfUgYz1pd97eUGGPuXvDFfLsBVd1dfd\+      \UhPwxW7pMPgdWHTmg5uqKGFF6vE4xXpAqZTbTxRZjCDdTn68c2\+      \wrcxApm8hq3JX65Hix7VtcD13FF8b7BzBtwjXq1ze6NMjKgUcq\+      \pJTN9vt"+    )+  ]++-- Test vectors from:+-- https://github.com/bitcoin/bitcoin/blob/master/src/test/data/base58_encode_decode.json++testBase58Vector :: (Text, Text) -> Assertion+testBase58Vector (a, b) = do+  assertEqual "encodeBase58 match" b (encodeBase58 bsA)+  assertEqual "decodeBase58 match" a (encodeHex bsB)+  assertEqual "bytestring match" bsA bsB+  where+    bsA = fromJust $ decodeHex a+    bsB = fromJust $ decodeBase58 b++base58Vectors :: [(Text, Text)]+base58Vectors =+  [ ("", ""),+    ("61", "2g"),+    ("626262", "a3gV"),+    ("636363", "aPEr"),+    ( "73696d706c792061206c6f6e6720737472696e67",+      "2cFupjhnEsSn59qHXstmK2ffpLv2"+    ),+    ( "00eb15231dfceb60925886b67d065299925915aeb172c06647",+      "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L"+    ),+    ("516b6fcd0f", "ABnLTmg"),+    ("bf4f89001e670274dd", "3SEo3LWLoPntC"),+    ("572e4794", "3EFU7m"),+    ("ecac89cad93923c02321", "EJDM8drfXA6uyA"),+    ("10c8511e", "Rt5zm"),+    ("00000000000000000000", "1111111111"),+    ( "000111d38e5fc9071ffcd20b4a763cc9ae4f252bb4e48fd66a835e252a\+      \da93ff480d6dd43dc62a641155a5",+      "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"+    ),+    ( "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c\+      \1d1e1f202122232425262728292a2b2c2d2e2f30313233343536373839\+      \3a3b3c3d3e3f404142434445464748494a4b4c4d4e4f50515253545556\+      \5758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f70717273\+      \7475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f90\+      \9192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacad\+      \aeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9ca\+      \cbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7\+      \e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",+      "1cWB5HCBdLjAuqGGReWE3R3CguuwSjw6RHn39s2yuDRTS5NsBgNiFpWgAn\+      \Ex6VQi8csexkgYw3mdYrMHr8x9i7aEwP8kZ7vccXWqKDvGv3u1GxFKPuAk\+      \n8JCPPGDMf3vMMnbzm6Nh9zh1gcNsMvH3ZNLmP5fSG6DGbbi2tuwMWPthr\+      \4boWwCxf7ewSgNQeacyozhKDDQQ1qL5fQFUW52QKUZDZ5fw3KXNQJMcNTc\+      \aB723LchjeKun7MuGW5qyCBZYzA1KjofN1gYBV3NqyhQJ3Ns746GNuf9N2\+      \pQPmHz4xpnSrrfCvy6TVVz5d4PdrjeshsWQwpZsZGzvbdAdN8MKV5QsBDY"+    )+  ]++-- Test vectors from:+-- https://github.com/bitcoin/bitcoin/blob/master/src/test/base58_tests.cpp++testBase58InvalidVector :: (Text, Maybe Text) -> Assertion+testBase58InvalidVector (a, resM) =+  assertEqual "decodeBase58 invalid match" resM (encodeHex <$> decodeBase58 a)++base58InvalidVectors :: [(Text, Maybe Text)]+base58InvalidVectors =+  [ ("invalid", Nothing),+    ("\0invalid", Nothing),+    ("good", Just "768320"),+    ("bad0IOl", Nothing),+    ("goodbad0IOl", Nothing),+    ("good\0bad0IOl", Nothing)+    --  Haskoin does not remove white spaces before decoding base58 strings+    -- , (" \t\n\v\f\r skip \r\f\v\n\t a", Nothing)+    -- , (" \t\n\v\f\r skip \r\f\v\n\t ", Just "971a55")+  ]++testBase58ChkInvalidVector :: (Text, Maybe Text) -> Assertion+testBase58ChkInvalidVector (a, resM) =+  assertEqual+    "decodeBase58Check invalid match"+    resM+    (encodeHex <$> decodeBase58Check a)++base58ChkInvalidVectors :: [(Text, Maybe Text)]+base58ChkInvalidVectors =+  [ ("3vQB7B6MrGQZaxCuFg4oh", Just "68656c6c6f20776f726c64"),+    ("3vQB7B6MrGQZaxCuFg4oi", Nothing),+    ("3vQB7B6MrGQZaxCuFg4oh0IOl", Nothing),+    ("3vQB7B6MrGQZaxCuFg4oh\00IOl", Nothing)+  ]++testCompatWitnessVector :: Ctx -> (Network, Text, Text) -> Assertion+testCompatWitnessVector ctx (net, seckey, addr) = do+  let seckeyM = fromWif net seckey+  assertBool "decode seckey" (isJust seckeyM)+  let pubkey = derivePublicKey ctx (fromJust seckeyM)+  let addrM = addrToText btcTest (pubKeyCompatWitnessAddr ctx pubkey)+  assertBool "address can be encoded" (isJust addrM)+  assertEqual "witness address matches" addr (fromJust addrM)++compatWitnessVectors :: [(Network, Text, Text)]+compatWitnessVectors =+  [ ( btcTest,+      "cNUnpYpMsJXYCERYBciJnsWBpcYEFjdcbq6dxj4SskGhs7uHuJ7Q",+      "2N6PDTueBHvXzW61B4oe5SW1D3v2Z3Vpbvw"+    )+  ]
+ test/Haskoin/BlockSpec.hs view
@@ -0,0 +1,421 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}++module Haskoin.BlockSpec+  ( spec,+  )+where++import Control.Monad+import Control.Monad.State.Strict+import Data.Default (def)+import Data.Either (fromRight)+import Data.Maybe (fromJust)+import Data.String (fromString)+import Data.String.Conversions (cs)+import Data.Text (Text)+import Data.Word (Word32)+import Haskoin.Block+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Transaction+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.HUnit hiding (State)+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck+import Text.Printf (printf)++identityTests :: Ctx -> IdentityTests+identityTests ctx =+  def+    { readTests =+        [ ReadBox (flip arbitraryBlock ctx =<< arbitraryNetwork),+          ReadBox arbitraryBlockHash,+          ReadBox arbitraryBlockHeader,+          ReadBox arbitraryGetBlocks,+          ReadBox arbitraryGetHeaders,+          ReadBox arbitraryHeaders,+          ReadBox arbitraryMerkleBlock,+          ReadBox arbitraryBlockNode,+          ReadBox arbitraryHeaderMemory+        ],+      jsonTests =+        [ JsonBox (flip arbitraryBlock ctx =<< arbitraryNetwork),+          JsonBox arbitraryBlockHash,+          JsonBox arbitraryBlockHeader+        ],+      serialTests =+        [ SerialBox (flip arbitraryBlock ctx =<< arbitraryNetwork),+          SerialBox arbitraryBlockHash,+          SerialBox arbitraryBlockHeader,+          SerialBox arbitraryGetBlocks,+          SerialBox arbitraryGetHeaders,+          SerialBox arbitraryHeaders,+          SerialBox arbitraryMerkleBlock,+          SerialBox arbitraryBlockNode+        ]+    }++myTime :: Timestamp+myTime = 1499083075++withChain :: Network -> State HeaderMemory a -> a+withChain net f = evalState f (initialChain net)++chain :: (BlockHeaders m) => Network -> BlockHeader -> Int -> m ()+chain net bh i = do+  bnsE <- connectBlocks net myTime bhs+  either error (const $ return ()) bnsE+  where+    bhs = appendBlocks net 6 bh i++spec :: Spec+spec = prepareContext $ \ctx -> do+  testIdentity $ identityTests ctx+  describe "blockchain headers" $ do+    it "gets best block on bchRegTest" $+      let net = bchRegTest+          bb =+            withChain net $ do+              chain net net.genesisHeader 100+              getBestBlockHeader+       in bb.height `shouldBe` 100+    it "builds a block locator on bchRegTest" $+      let net = bchRegTest+          loc =+            withChain net $ do+              chain net net.genesisHeader 100+              bb <- getBestBlockHeader+              blockLocatorNodes bb+          heights = map (.height) loc+       in heights `shouldBe` [100, 99 .. 90] <> [88, 84, 76, 60, 28, 0]+    it "follows split chains on bchRegTest" $+      let net = bchRegTest+          bb = withChain net $ splitChain net >> getBestBlockHeader+       in bb.height `shouldBe` 4035+  describe "block hash" $ do+    prop "encodes and decodes block hash" $+      forAll arbitraryBlockHash $ \h ->+        hexToBlockHash (blockHashToHex h) == Just h+    prop "from string block hash" $+      forAll arbitraryBlockHash $ \h ->+        fromString (cs $ blockHashToHex h) == h+  describe "merkle trees" $ do+    prop "builds tree of right width at height 1" testTreeWidth+    prop "builds tree of right width at height 0" testBaseWidth+    prop "builds and extracts partial merkle tree" $+      forAll arbitraryNetwork $ \net ->+        forAll+          (listOf1 ((,) <$> arbitraryTxHash <*> arbitrary))+          (buildExtractTree net)+    it "merkle root test vectors" $ mapM_ runMerkleVector merkleVectors+  describe "compact number" $ do+    it "compact number local vectors" testCompact+    it "compact number imported vectors" testCompactBitcoinCore+  describe "asert" $+    mapM_+      ( \x ->+          asertTests $+            "test_vectors_aserti3-2d_run" ++ printf "%02d" x ++ ".txt"+      )+      [(1 :: Int) .. 12]+  describe "helper functions" $ do+    it "computes bitcoin block subsidy correctly" (testSubsidy btc)+    it "computes regtest block subsidy correctly" (testSubsidy btcRegTest)++-- 0 → → 2015 → → → → → → → 4031+--       ↓+--       → → 2035 → → → → → → 4035*+--           ↓+--           → → 2185+splitChain :: Network -> State HeaderMemory ()+splitChain net = do+  start <- go 1 net.genesisHeader 2015+  e 2015 (head start)+  tail1 <- go 2 (head start).header 2016+  e 4031 (head tail1)+  tail2 <- go 3 (head start).header 20+  e 2035 (head tail2)+  tail3 <- go 4 (head tail2).header 2000+  e 4035 (head tail3)+  tail4 <- go 5 (head tail2).header 150+  e 2185 (head tail4)+  sp1 <- splitPoint (head tail1) (head tail3)+  unless (sp1 == head start) $+    error $+      "Split point wrong between blocks 4031 and 4035: "+        ++ show sp1.height+  sp2 <- splitPoint (head tail4) (head tail3)+  unless (sp2 == head tail2) $+    error $+      "Split point wrong between blocks 2185 and 4035: "+        ++ show sp2.height+  where+    e n bn@BlockNode {} =+      unless (bn.height == n) $+        error $+          "Node height "+            ++ show bn.height+            ++ " of first chunk should be "+            ++ show n+    go seed start n = do+      let bhs = appendBlocks net seed start n+      bnE <- connectBlocks net myTime bhs+      case bnE of+        Right bn -> return bn+        Left ex -> error ex++{- Merkle Trees -}++testTreeWidth :: Int -> Property+testTreeWidth i = i /= 0 ==> calcTreeWidth (abs i) (calcTreeHeight $ abs i) == 1++testBaseWidth :: Int -> Property+testBaseWidth i = i /= 0 ==> calcTreeWidth (abs i) 0 == abs i++buildExtractTree :: Network -> [(TxHash, Bool)] -> Bool+buildExtractTree net txs =+  r == buildMerkleRoot (map fst txs) && m == map fst (filter snd txs)+  where+    (f, h) = buildPartialMerkle txs+    (r, m) =+      fromRight (error "Could not extract matches from Merkle tree") $+        extractMatches net f h (length txs)++testCompact :: Assertion+testCompact = do+  assertEqual "vector 1" 0x05123456 (encodeCompact 0x1234560000)+  assertEqual "vector 2" (0x1234560000, False) (decodeCompact 0x05123456)+  assertEqual "vector 3" 0x0600c0de (encodeCompact 0xc0de000000)+  assertEqual "vector 4" (0xc0de000000, False) (decodeCompact 0x0600c0de)+  assertEqual "vector 5" 0x05c0de00 (encodeCompact (-0x40de000000))+  assertEqual "vector 6" (-0x40de000000, False) (decodeCompact 0x05c0de00)++testCompactBitcoinCore :: Assertion+testCompactBitcoinCore = do+  assertEqual "zero" (0, False) (decodeCompact 0x00000000)+  assertEqual+    "zero (encode · decode)"+    0x00000000+    (encodeCompact . fst $ decodeCompact 0x00000000)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x00123456)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x01003456)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x02000056)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x03000000)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x04000000)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x00923456)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x01803456)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x02800056)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x03800000)+  assertEqual "rounds to zero" (0, False) (decodeCompact 0x04800000)+  assertEqual "vector 1 (decode)" (0x12, False) (decodeCompact 0x01123456)+  assertEqual+    "vector 1 (encode · decode)"+    0x01120000+    (encodeCompact . fst $ decodeCompact 0x01123456)+  assertEqual "0x80 bit set" 0x02008000 (encodeCompact 0x80)+  assertEqual+    "vector 2 (negative) (decode)"+    (-0x7e, False)+    (decodeCompact 0x01fedcba)+  assertEqual+    "vector 2 (negative) (encode · decode)"+    0x01fe0000+    (encodeCompact . fst $ decodeCompact 0x01fedcba)+  assertEqual "vector 3 (decode)" (0x1234, False) (decodeCompact 0x02123456)+  assertEqual+    "vector 3 (encode · decode)"+    0x02123400+    (encodeCompact . fst $ decodeCompact 0x02123456)+  assertEqual "vector 4 (decode)" (0x123456, False) (decodeCompact 0x03123456)+  assertEqual+    "vector 4 (encode · decode)"+    0x03123456+    (encodeCompact . fst $ decodeCompact 0x03123456)+  assertEqual+    "vector 5 (decode)"+    (0x12345600, False)+    (decodeCompact 0x04123456)+  assertEqual+    "vector 5 (encode · decode)"+    0x04123456+    (encodeCompact . fst $ decodeCompact 0x04123456)+  assertEqual+    "vector 6 (decode)"+    (-0x12345600, False)+    (decodeCompact 0x04923456)+  assertEqual+    "vector 6 (encode · decode)"+    0x04923456+    (encodeCompact . fst $ decodeCompact 0x04923456)+  assertEqual+    "vector 7 (decode)"+    (0x92340000, False)+    (decodeCompact 0x05009234)+  assertEqual+    "vector 7 (encode · decode)"+    0x05009234+    (encodeCompact . fst $ decodeCompact 0x05009234)+  assertEqual+    "vector 8 (decode)"+    ( 0x1234560000000000000000000000000000000000000000000000000000000000,+      False+    )+    (decodeCompact 0x20123456)+  assertEqual+    "vector 8 (encode · decode)"+    0x20123456+    (encodeCompact . fst $ decodeCompact 0x20123456)+  assertBool "vector 9 (decode) (overflow)" (snd $ decodeCompact 0xff123456)+  assertBool+    "vector 9 (decode) (positive)"+    ((> 0) . fst $ decodeCompact 0xff123456)++runMerkleVector :: (Text, [Text]) -> Assertion+runMerkleVector (r, hs) =+  assertBool "merkle vector" $+    buildMerkleRoot (map f hs) == (f r).get+  where+    f = fromJust . hexToTxHash++merkleVectors :: [(Text, [Text])]+merkleVectors =+  -- Block 000000000000cd7e8cf6510303dde76121a1a791c15dba0be4be7022b07cf9e1+  [ ( "fb6698ac95b754256c5e71b4fbe07638cb6ca83ee67f44e181b91727f09f4b1f",+      [ "dd96fdcfaec994bf583af650ff6022980ee0ba1686d84d0a3a2d24eabf34bc52",+        "1bc216f786a564378710ae589916fc8e092ddfb9f24fe6c47b733550d476d5d9",+        "a1db0b0194426064b067899ff2d975fb277fd52dbb1a38370800c76dd6503d41",+        "d69f7fb0e668fbd437d1bf5211cc34d7eb8746f50cfddf705fe10bc2f8f7035f",+        "5b4057cd80be7df5ed2ac42b776897ed3c26e3a01e4072075b8129c587094ef6",+        "ed6dabcfba0ef43c50d89a8a0e4b236b1bc6585d4c3bbf49728b55f44312d6bc",+        "056aaa9a3c635909c794e9b0acc7dccb0456c59a84c6b08417335bee4515e3d3",+        "05bae5f1d1c874171692e1fc06f664e63eb143d3f096601ef938e4a9012eee66",+        "b5e48e94e3f2fba197b3f591e01f47e185d7834d669529d44078e41c671aab0f",+        "3b56aeadfc0c5484fd507bc89f13f2e5f61c42e0a4ae9062eda9a9aeef7db6a4",+        "2affa187e1ebb94a2a86578b9f64951e854ff3d346fef259acfb6d0f5212e0d3"+      ]+    ),+    -- Block 00000000000007cc4b6f07bfed72bccc1ed8dd031a93969a4c22211f784457d4++    ( "886fea311d2dc64c315519f2d647e43998d780d2170f77e53dc0d85bf2ee680c",+      [ "c9c9e5211512629fd111cc071d745b8c79bf486b4ea95489eb5de08b5d786b8e",+        "20beb0ee30dfd323ade790ce9a46ae7a174f9ea44ce22a17c4d4eb23b7016f51",+        "d4cb7dd741e78a8f57e12f6c8ddb0361ff2a5bf9365bd7d7df761060847daf9a",+        "ddbfa6fdd29d4b47aeaadf82a4bf0a93d58cd7d8401fabf860a1ae8eeb51f42e",+        "9d82bafe44abee248b968c86f165051c8413482c232659795335c52922dab471",+        "86035372d31b53efd848cea7231aa9738c209aff64d3c59b1619341afb5b6ba3",+        "11e7a7393d9658813dfaebc04fa6d4b73bac8d641bffa7067da879523d43d030",+        "2f676b9aa5bc0ebf3395032c84c466e40cac29f80434cd1138e31c2d0fcc5c13",+        "37567d559fbfae07fda9a90de0ce30b202128bc8ebdfef5ad2b53e865a3478c2",+        "0b8e6c1200c454361e94e261738429e9c9b8dcffd85ec8511bbf5dc7e2e0ada8"+      ]+    ),+    -- Block 00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048++    ( "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098",+      ["0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098"]+    ),+    -- Block 000000000004d160ac1f7b775d7c1823345aeadd5fcb29ca2ad2403bb7babd4c++    ( "aae018650f513fc42d55b2210ec3ceeeb194fb1261d37989de07451fc0cbac5c",+      [ "a4454f22831acd7904a9902c5070a3ee4bf4c2b13bc6b2dc66735dd3c4414028",+        "45297f334278885108dd38a0b689ed95a4373dd3f7e4413e6aebdc2654fb771b"+      ]+    ),+    -- Block 000000000001d1b13a7e86ddb20da178f20d6da5cd037a29c2a15b8b84cc774e++    ( "ca3580505feb87544760ac14a5859659e23be05f765bbed9f86a3c9aad1a5d0c",+      [ "60702384c6e9d34ff03c2b3e726bdc649befe603216815bd0a2974921d0d9549",+        "11f40f58941d2a81a1616a3b84b7dd8b9d07e68750827de488c11a18f54220bb",+        "d78e82527aa8cf16e375010bc666362c0258d3c0da1885a1871121706da8b633"+      ]+    ),+    -- Block 0000000000000630a4e2266a31776e952a19b7c99a6387917d9de9032f608021++    ( "dcce8be0a9a41e7bb726c5b49d957d90b5308e3dc5dce070ccbc8996e265a6c2",+      [ "c0f58ff12cd1023b05f8f7035cc62bf50958ddb216a4e0eb5471deb7ef25fe81",+        "24e5bbf9008641b8fcf3d076fef66c28c695362ba9f6a6042f8275a98414ee92",+        "e8e1f72abad5e34dabc0f6de46a484b17a9af857d1c41de19482fadf6f7f4b27",+        "540e4d34d9fd9e5ec02853054be7ad9260379bc23388489049cca1b0f7cf518a",+        "324444835c5fe0545f98c4240011b75e6ea1bb76f41829e4cfbe7f75b6cee924",+        "e7d31437ac21bceb0c222a82b2723e2b8a7654147e33397679f041537022a4b2",+        "a8b5768d8b33525ee89d546a6a6897f8e42ba9d56a2c5e871a5d2ab40258dc95",+        "7ba712b31bae8d45810a5cda3838c7e7fb9abd6e88bb4b3ee79be9ea2f714bb4",+        "2ae1c4d927b06edaa626b230976ad8062bbae24da9378d1de2409da5ab08a26d",+        "3c417dc8087d6878003624b74431e17fec9ca761389034b1b1e0f32cbfb11f4f",+        "de6de7beae8d8c98c7d46b4409d5460e58e3204d8b4caed256c7471998595909",+        "c7c3c211402b7c4379f7b01fadc67260ee58d11e8d0bcce3d68cb45f3467e99d",+        "77aa2717e727a096d81074bd46ae59462692d20a1acc1a01b2535518ae5aeb53",+        "4859a710bb673aca46208bbd59d1000ae990dafff5f70b56f0853aeeaea3948b",+        "38deca6991988e461b83aa0d49ffef0f304c4b760371682d152eeb8c56a48174",+        "648f4f50dada3574e2dfe2dc68956b01dd97d543859a3540bbe1ef5418d0e494",+        "9cd7be42c2f0cd8bf38738c162cd05108e213ec7958bf2571cb627872963f5c4",+        "6740e0dd8b97e23864af41839fc197238d2f0dbefce9a82c657556be65c465fa",+        "f75c2e4b70db4b0aabc44b77af1ae75d305340fcf6e7b5f806ddcba4aa42b55d",+        "e125c488636749da68e6696b97525a77146c0777c7946927e37afd513d74a4e6",+        "c20526f119aea10880af631eba7f0b60385a22e0b0c402fe8508d41952e58be9",+        "6456c023c7e245f5c57a168633a23f57f4fadb651115f807694a6bed14ae3b55",+        "98b26e364e2888c9f264e4b5e13103c89608609774eb07ce933d8a2a45d19776",+        "2efaa4f167bb65ba5684f8076cd9279fd67fd9c67388c8862809bab5542e637d",+        "ec44eeb84d8d976d77079a822710b4dfdb11a2d9a03d8cc00bab0ae424e84666",+        "410730d9f807d81ac48b8eafac6f1d36642c1c370241b367a35f0bac6ac7c05f",+        "e95a7d0d477fd3db22756a3fd390a50c7bc48dc9e946fea9d24bd0866b3bb0e9",+        "a72fec99d14939216628aaf7a0afc4c017113bcae964e777e6b508864eeaacc4",+        "8548433310fcf75dbbc042121e8318c678e0a017534786dd322a91cebe8d213f"+      ]+    )+  ]++testSubsidy :: Network -> Assertion+testSubsidy net = go (2 * 50 * 100 * 1000 * 1000) 0+  where+    go previous_subsidy halvings = do+      let height = halvings * net.halvingInterval+          subsidy = computeSubsidy net height+      if halvings >= 64+        then subsidy `shouldBe` 0+        else do+          subsidy `shouldBe` (previous_subsidy `div` 2)+          go subsidy (halvings + 1)++data AsertBlock = AsertBlock Int Integer Integer Word32++data AsertVector = AsertVector String Integer Integer Word32 [AsertBlock]++readAsertVector :: FilePath -> IO AsertVector+readAsertVector p = do+  (d : ah : apt : ab : _ : _ : _ : _ : xs) <- lines <$> readFile ("data/" ++ p)+  let desc = drop 16 d+      anchor_height = read (words ah !! 3)+      anchor_parent_time = read (words apt !! 4)+      anchor_nbits = read (words ab !! 3)+      blocks = map (f . words) (init xs)+  return $+    AsertVector+      desc+      anchor_height+      anchor_parent_time+      anchor_nbits+      blocks+  where+    f [i, h, t, g] = AsertBlock (read i) (read h) (read t) (read g)+    f _ = undefined++asertTests :: FilePath -> SpecWith ()+asertTests file = do+  v@(AsertVector d _ _ _ _) <- runIO $ readAsertVector file+  it d $ testAsertBits v++testAsertBits :: AsertVector -> Assertion+testAsertBits (AsertVector _ anchor_height anchor_parent_time anchor_bits blocks) =+  forM_ blocks $ \(AsertBlock _ h t g) ->+    computeAsertBits+      (2 * 24 * 60 * 60)+      anchor_bits+      (t - anchor_parent_time)+      (h - anchor_height)+      `shouldBe` g
+ test/Haskoin/Crypto/HashSpec.hs view
@@ -0,0 +1,401 @@+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoFieldSelectors #-}++module Haskoin.Crypto.HashSpec (spec) where++import Data.Bits+import Data.ByteString (ByteString)+import Data.ByteString.Builder+import Data.ByteString.Char8 qualified as Char8+import Data.ByteString.Lazy qualified as Lazy+import Data.ByteString.Short qualified as Short+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Default (def)+import Data.Maybe (fromJust)+import Data.String (fromString)+import Data.String.Conversions+import Data.Text (Text)+import Data.Word+import Haskoin.Block+import Haskoin.Crypto+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.HUnit+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck++identityTests :: IdentityTests+identityTests =+  def+    { readTests =+        [ ReadBox arbitraryHash160,+          ReadBox arbitraryHash256,+          ReadBox arbitraryHash512,+          ReadBox arbitraryCheckSum32+        ],+      serialTests =+        [ SerialBox arbitraryHash160,+          SerialBox arbitraryHash256,+          SerialBox arbitraryHash512,+          SerialBox arbitraryCheckSum32+        ]+    }++spec :: Spec+spec =+  describe "Hash" $ do+    testIdentity identityTests+    describe "Property Tests" $ do+      prop "join512( split512(h) ) == h" $+        forAll arbitraryHash256 $+          forAll arbitraryHash256 . joinSplit512+      prop "decodeCompact . encodeCompact i == i" decEncCompact+      prop "from string Hash512" $+        forAll arbitraryHash512 $ \h ->+          fromString (cs $ encodeHex $ runPutS $ serialize h) == h+      prop "from string Hash256" $+        forAll arbitraryHash256 $ \h ->+          fromString (cs $ encodeHex $ runPutS $ serialize h) == h+      prop "from string Hash160" $+        forAll arbitraryHash160 $ \h ->+          fromString (cs $ encodeHex $ runPutS $ serialize h) == h+    describe "Test Vectors" $ do+      it "Passes RIPEMD160 test vectors" $+        mapM_ (testVector ripemd160 (.get)) ripemd160Vectors+      it "Passes SHA1 test vectors" $+        mapM_ (testVector sha1 (.get)) sha1Vectors+      it "Passes SHA256 test vectors" $+        mapM_ (testVector sha256 (.get)) sha256Vectors+      it "Passes SHA512 test vectors" $+        mapM_ (testVector sha512 (.get)) sha512Vectors+      it "Passes HMAC_SHA256 test vectors" $+        mapM_ (testHMACVector hmac256 (.get)) hmacSha256Vectors+      it "Passes HMAC_SHA512 test vectors" $+        mapM_ (testHMACVector hmac512 (.get)) hmacSha512Vectors++joinSplit512 :: Hash256 -> Hash256 -> Bool+joinSplit512 a b = split512 (join512 (a, b)) == (a, b)++-- After encoding and decoding, we may loose precision so the new result is >=+-- to the old one.+decEncCompact :: Integer -> Bool+decEncCompact i+  -- Integer completely fits inside the mantisse+  | abs i <= 0x007fffff = decodeCompact (encodeCompact i) == (i, False)+  -- Otherwise precision will be lost and the decoded result will+  -- be smaller than the original number+  | i >= 0 = fst (decodeCompact (encodeCompact i)) < i+  | otherwise = fst (decodeCompact (encodeCompact i)) > i++-- Test vectors from:+-- https://github.com/bitcoin/bitcoin/blob/master/src/test/crypto_tests.cpp++testVector ::+  (ByteString -> a) ->+  (a -> Short.ShortByteString) ->+  (ByteString, Text) ->+  Assertion+testVector f1 f2 (i, res) =+  assertEqual "Hash matches" res (encodeHex (Short.fromShort $ f2 $ f1 i))++testHMACVector ::+  (ByteString -> ByteString -> a) ->+  (a -> Short.ShortByteString) ->+  (Text, Text, Text) ->+  Assertion+testHMACVector f1 f2 (k, m, res) =+  assertEqual "Hash matches" res (encodeHex (Short.fromShort $ f2 $ f1 bsK bsM))+  where+    bsK = fromJust $ decodeHex k+    bsM = fromJust $ decodeHex m++longTestString :: ByteString+longTestString =+  Lazy.toStrict $! toLazyByteString $! go [0 .. 199999]+  where+    go :: [Word32] -> Builder+    go [] = mempty+    go (i : is) =+      let i1 = fromIntegral $! i+          i2 = fromIntegral $! i `shiftR` 4+          i3 = fromIntegral $! i `shiftR` 8+          i4 = fromIntegral $! i `shiftR` 12+          i5 = fromIntegral $! i `shiftR` 16+       in word8 i1 <> word8 i2 <> word8 i3 <> word8 i4 <> word8 i5 <> go is++ripemd160Vectors :: [(ByteString, Text)]+ripemd160Vectors =+  [ ("", "9c1185a5c5e9fc54612808977ee8f548b2258d31"),+    ("abc", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"),+    ("message digest", "5d0689ef49d2fae572b881b123a85ffa21595f36"),+    ("secure hash algorithm", "20397528223b6a5f4cbc2808aba0464e645544f9"),+    ( "RIPEMD160 is considered to be safe",+      "a7d78608c7af8a8e728778e81576870734122b66"+    ),+    ( "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",+      "12a053384a9c0c88e405a06c27dcf49ada62eb2b"+    ),+    ( "For this sample, this 63-byte string will be used as input data",+      "de90dbfee14b63fb5abf27c2ad4a82aaa5f27a11"+    ),+    ( "This is exactly 64 bytes long, not counting the terminating byte",+      "eda31d51d3a623b81e19eb02e24ff65d27d67b37"+    ),+    (Char8.replicate 1000000 'a', "52783243c1697bdbe16d37f97f68f08325dc1528"),+    (longTestString, "464243587bd146ea835cdf57bdae582f25ec45f1")+  ]++sha1Vectors :: [(ByteString, Text)]+sha1Vectors =+  [ ("", "da39a3ee5e6b4b0d3255bfef95601890afd80709"),+    ("abc", "a9993e364706816aba3e25717850c26c9cd0d89d"),+    ("message digest", "c12252ceda8be8994d5fa0290a47231c1d16aae3"),+    ("secure hash algorithm", "d4d6d2f0ebe317513bbd8d967d89bac5819c2f60"),+    ( "SHA1 is considered to be safe",+      "f2b6650569ad3a8720348dd6ea6c497dee3a842a"+    ),+    ( "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",+      "84983e441c3bd26ebaae4aa1f95129e5e54670f1"+    ),+    ( "For this sample, this 63-byte string will be used as input data",+      "4f0ea5cd0585a23d028abdc1a6684e5a8094dc49"+    ),+    ( "This is exactly 64 bytes long, not counting the terminating byte",+      "fb679f23e7d1ce053313e66e127ab1b444397057"+    ),+    (Char8.replicate 1000000 'a', "34aa973cd4c4daa4f61eeb2bdbad27316534016f"),+    (longTestString, "b7755760681cbfd971451668f32af5774f4656b5")+  ]++sha256Vectors :: [(ByteString, Text)]+sha256Vectors =+  [ ("", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"),+    ( "abc",+      "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"+    ),+    ( "message digest",+      "f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650"+    ),+    ( "secure hash algorithm",+      "f30ceb2bb2829e79e4ca9753d35a8ecc00262d164cc077080295381cbd643f0d"+    ),+    ( "SHA256 is considered to be safe",+      "6819d915c73f4d1e77e4e1b52d1fa0f9cf9beaead3939f15874bd988e2a23630"+    ),+    ( "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",+      "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"+    ),+    ( "For this sample, this 63-byte string will be used as input data",+      "f08a78cbbaee082b052ae0708f32fa1e50c5c421aa772ba5dbb406a2ea6be342"+    ),+    ( "This is exactly 64 bytes long, not counting the terminating byte",+      "ab64eff7e88e2e46165e29f2bce41826bd4c7b3552f6b382a9e7d3af47c245f8"+    ),+    ( "As Bitcoin relies on 80 byte header hashes, we want to have an example for that.",+      "7406e8de7d6e4fffc573daef05aefb8806e7790f55eab5576f31349743cca743"+    ),+    ( Char8.replicate 1000000 'a',+      "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"+    ),+    ( longTestString,+      "a316d55510b49662420f49d145d42fb83f31ef8dc016aa4e32df049991a91e26"+    )+  ]++sha512Vectors :: [(ByteString, Text)]+sha512Vectors =+  [ ( "",+      "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d1\+      \3c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"+    ),+    ( "abc",+      "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a219299\+      \2a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"+    ),+    ( "message digest",+      "107dbf389d9e9f71a3a95f6c055b9251bc5268c2be16d6c13492ea45b0199f3309e164\+      \55ab1e96118e8a905d5597b72038ddb372a89826046de66687bb420e7c"+    ),+    ( "secure hash algorithm",+      "7746d91f3de30c68cec0dd693120a7e8b04d8073cb699bdce1a3f64127bca7a3d5db50\+      \2e814bb63c063a7a5043b2df87c61133395f4ad1edca7fcf4b30c3236e"+    ),+    ( "SHA512 is considered to be safe",+      "099e6468d889e1c79092a89ae925a9499b5408e01b66cb5b0a3bd0dfa51a99646b4a39\+      \01caab1318189f74cd8cf2e941829012f2449df52067d3dd5b978456c2"+    ),+    ( "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",+      "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15\+      \c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445"+    ),+    ( "For this sample, this 63-byte string will be used as input data",+      "b3de4afbc516d2478fe9b518d063bda6c8dd65fc38402dd81d1eb7364e72fb6e6663cf\+      \6d2771c8f5a6da09601712fb3d2a36c6ffea3e28b0818b05b0a8660766"+    ),+    ( "This is exactly 64 bytes long, not counting the terminating byte",+      "70aefeaa0e7ac4f8fe17532d7185a289bee3b428d950c14fa8b713ca09814a387d2458\+      \70e007a80ad97c369d193e41701aa07f3221d15f0e65a1ff970cedf030"+    ),+    ( "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmn\+      \opjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",+      "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d28\+      \9e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"+    ),+    ( Char8.replicate 1000000 'a',+      "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff2\+      \44877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"+    ),+    ( longTestString,+      "40cac46c147e6131c5193dd5f34e9d8bb4951395f27b08c558c65ff4ba2de59437de8c\+      \3ef5459d76a52cedc02dc499a3c9ed9dedbfb3281afd9653b8a112fafc"+    )+  ]++-- test cases 1, 2, 3, 4, 6 and 7 of RFC 4231+hmacSha256Vectors :: [(Text, Text, Text)]+hmacSha256Vectors =+  [ ( "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",+      "4869205468657265",+      "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"+    ),+    ( "4a656665",+      "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+      "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"+    ),+    ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",+      "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\+      \dddddddddddddddddddddddddddddd",+      "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"+    ),+    ( "0102030405060708090a0b0c0d0e0f10111213141516171819",+      "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd\+      \cdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",+      "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"+    ),+    ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",+      "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a65204b\+      \6579202d2048617368204b6579204669727374",+      "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"+    ),+    ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",+      "5468697320697320612074657374207573696e672061206c6172676572207468616e20\+      \626c6f636b2d73697a65206b657920616e642061206c6172676572207468616e20626c\+      \6f636b2d73697a6520646174612e20546865206b6579206e6565647320746f20626520\+      \686173686564206265666f7265206265696e6720757365642062792074686520484d41\+      \4320616c676f726974686d2e",+      "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"+    ),+    -- Test case with key length 63 bytes.++    ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566\+      \654a6566654a6566654a6566654a6566654a6566654a6566654a6566",+      "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+      "9de4b546756c83516720a4ad7fe7bdbeac4298c6fdd82b15f895a6d10b0769a6"+    ),+    -- Test case with key length 64 bytes.++    ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566\+      \654a6566654a6566654a6566654a6566654a6566654a6566654a656665",+      "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+      "528c609a4c9254c274585334946b7c2661bad8f1fc406b20f6892478d19163dd"+    ),+    -- Test case with key length 65 bytes.++    ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566\+      \654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a",+      "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+      "d06af337f359a2330deffb8e3cbe4b5b7aa8ca1f208528cdbd245d5dc63c4483"+    )+  ]++-- test cases 1, 2, 3, 4, 6 and 7 of RFC 4231+hmacSha512Vectors :: [(Text, Text, Text)]+hmacSha512Vectors =+  [ ( "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",+      "4869205468657265",+      "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde\+      \daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"+    ),+    ( "4a656665",+      "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+      "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554\+      \9758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"+    ),+    ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",+      "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\+      \dddddddddddddddddddddddddddddddddddd",+      "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39\+      \bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb"+    ),+    ( "0102030405060708090a0b0c0d0e0f10111213141516171819",+      "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd\+      \cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd",+      "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3db\+      \a91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd"+    ),+    ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaa",+      "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a\+      \65204b6579202d2048617368204b6579204669727374",+      "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f352\+      \6b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598"+    ),+    ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\+      \aaaaaa",+      "5468697320697320612074657374207573696e672061206c6172676572207468\+      \616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074\+      \68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565\+      \647320746f20626520686173686564206265666f7265206265696e6720757365\+      \642062792074686520484d414320616c676f726974686d2e",+      "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944\+      \b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58"+    ),+    -- Test case with key length 127 bytes.++    ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566",+      "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+      "267424dfb8eeb999f3e5ec39a4fe9fd14c923e6187e0897063e5c9e02b2e624a\+      \c04413e762977df71a9fb5d562b37f89dfdfb930fce2ed1fa783bbc2a203d80e"+    ),+    -- Test case with key length 128 bytes.++    ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665",+      "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+      "43aaac07bb1dd97c82c04df921f83b16a68d76815cd1a30d3455ad43a3d80484\+      \2bb35462be42cc2e4b5902de4d204c1c66d93b47d1383e3e13a3788687d61258"+    ),+    -- Test case with key length 129 bytes.++    ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\+      \4a",+      "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+      "0b273325191cfc1b4b71d5075c8fcad67696309d292b1dad2cd23983a35feb8e\+      \fb29795e79f2ef27f68cb1e16d76178c307a67beaad9456fac5fdffeadb16e2c"+    )+  ]
+ test/Haskoin/Crypto/Keys/ExtendedSpec.hs view
@@ -0,0 +1,602 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}++module Haskoin.Crypto.Keys.ExtendedSpec (spec) where++import Control.Monad (forM_)+import Data.Aeson as A+import Data.Bits ((.&.))+import Data.ByteString.Lazy.Char8 qualified as B8+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Default (def)+import Data.Either (isLeft)+import Data.Maybe (fromJust, isJust, isNothing)+import Data.String (fromString)+import Data.String.Conversions (cs)+import Data.Text (Text)+import Data.Word (Word32)+import Haskoin.Address+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.HUnit+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck hiding ((.&.))++identityTests :: Ctx -> IdentityTests+identityTests ctx =+  def+    { readTests =+        [ ReadBox arbitraryXPrvKey,+          ReadBox (arbitraryXPubKey ctx),+          ReadBox arbitraryDerivPath,+          ReadBox arbitraryHardPath,+          ReadBox arbitrarySoftPath,+          ReadBox arbitraryParsedPath,+          ReadBox arbitraryBip32PathIndex+        ],+      jsonTests =+        [ JsonBox arbitraryDerivPath,+          JsonBox arbitraryHardPath,+          JsonBox arbitrarySoftPath,+          JsonBox arbitraryParsedPath+        ],+      marshalJsonTests =+        [ MarshalJsonBox $ (,) <$> arbitraryNetwork <*> arbitraryXPrvKey,+          MarshalJsonBox $+            (,) <$> ((,ctx) <$> arbitraryNetwork) <*> arbitraryXPubKey ctx+        ],+      serialTests =+        [ SerialBox arbitraryDerivPath,+          SerialBox arbitraryHardPath,+          SerialBox arbitrarySoftPath+        ],+      marshalTests =+        [ MarshalBox $ (,) <$> arbitraryNetwork <*> arbitraryXPrvKey,+          MarshalBox $+            (,) <$> ((,ctx) <$> arbitraryNetwork) <*> arbitraryXPubKey ctx+        ]+    }++spec :: Spec+spec = prepareContext $ \ctx -> do+  testIdentity $ identityTests ctx+  describe "Custom identity tests" $ do+    prop "encodes and decodes extended private key" $+      forAll arbitraryNetwork $ \net ->+        forAll arbitraryXPrvKey $+          customCerealID (marshalGet net) (marshalPut net)+    prop "encodes and decodes extended public key" $+      forAll arbitraryNetwork $ \net ->+        forAll (arbitraryXPubKey ctx) $+          customCerealID (marshalGet (net, ctx)) (marshalPut (net, ctx))+  describe "bip32 subkey derivation vector 1" $ vectorSpec ctx m1 vector1+  describe "bip32 subkey derivation vector 2" $ vectorSpec ctx m2 vector2+  describe "bip32 subkey derivation vector 3" $ vectorSpec ctx m3 vector3+  describe "bip32 subkey derivation using string path" $ do+    it "either derivations" $ testApplyPath ctx+    it "either derivations" $ testBadApplyPath ctx+    it "dublic derivations" $ testDerivePubPath ctx+    it "private derivations" $ testDerivePrvPath ctx+    it "path parsing" testParsePath+    it "from json" testFromJsonPath+    it "to json" testToJsonPath+  describe "Derivation Paths" $ do+    prop "from string derivation path" $+      forAll arbitraryDerivPath $+        \p -> fromString (cs $ pathToStr p) == p+    prop "from string hard derivation path" $+      forAll arbitraryHardPath $+        \p -> fromString (cs $ pathToStr p) == p+    prop "from string soft derivation path" $+      forAll arbitrarySoftPath $+        \p -> fromString (cs $ pathToStr p) == p+    prop "from and to lists of derivation paths" $+      forAll arbitraryDerivPath $+        \p -> listToPath (pathToList p) == p+    prop "from and to lists of hard derivation paths" $+      forAll arbitraryHardPath $ \p ->+        toHard (listToPath $ pathToList p) == Just p+    prop "from and to lists of soft derivation paths" $+      forAll arbitrarySoftPath $ \p ->+        toSoft (listToPath $ pathToList p) == Just p+  describe "Extended Keys" $ do+    let net = btc+    prop "computes pubkey of a subkey is subkey of the pubkey" $+      forAll arbitraryXPrvKey $+        pubKeyOfSubKeyIsSubKeyOfPubKey ctx+    prop "exports and imports extended private key" $+      forAll arbitraryXPrvKey $ \k ->+        xPrvImport net (xPrvExport net k) == Just k+    prop "exports and imports extended public key" $+      forAll (arbitraryXPubKey ctx) $ \k ->+        xPubImport net ctx (xPubExport net ctx k) == Just k++pubKeyOfSubKeyIsSubKeyOfPubKey :: Ctx -> XPrvKey -> Word32 -> Bool+pubKeyOfSubKeyIsSubKeyOfPubKey ctx k i =+  deriveXPubKey ctx (prvSubKey ctx k i') == pubSubKey ctx (deriveXPubKey ctx k) i'+  where+    i' = fromIntegral $ i .&. 0x7fffffff -- make it a public derivation++testFromJsonPath :: Assertion+testFromJsonPath =+  sequence_ $ do+    path <- jsonPathVectors+    return $+      assertEqual+        path+        (Just [fromString path :: DerivPath])+        (A.decode $ B8.pack $ "[\"" ++ path ++ "\"]")++testToJsonPath :: Assertion+testToJsonPath =+  sequence_ $ do+    path <- jsonPathVectors+    return $+      assertEqual+        path+        (B8.pack $ "[\"" ++ path ++ "\"]")+        (A.encode [fromString path :: ParsedPath])++jsonPathVectors :: [String]+jsonPathVectors =+  [ "m",+    "m/0",+    "m/0'",+    "M/0'",+    "m/2147483647",+    "M/2147483647",+    "m/1/2/3/4/5/6/7/8",+    "M/1/2/3/4/5/6/7/8",+    "m/1'/2'/3/4",+    "M/1'/2'/3/4"+  ]++testParsePath :: Assertion+testParsePath =+  sequence_ $ do+    (path, t) <- parsePathVectors+    return $ assertBool path (t $ parsePath path)++parsePathVectors :: [(String, Maybe ParsedPath -> Bool)]+parsePathVectors =+  [ ("m", isJust),+    ("m/0'", isJust),+    ("M/0'", isJust),+    ("m/2147483648", isNothing),+    ("m/2147483647", isJust),+    ("M/2147483648", isNothing),+    ("M/2147483647", isJust),+    ("M/-1", isNothing),+    ("M/-2147483648", isNothing),+    ("m/1/2/3/4/5/6/7/8", isJust),+    ("M/1/2/3/4/5/6/7/8", isJust),+    ("m/1'/2'/3/4", isJust),+    ("M/1'/2'/3/4", isJust),+    ("m/1/2'/3/4'", isJust),+    ("M/1/2'/3/4'", isJust),+    ("meh", isNothing),+    ("infinity", isNothing),+    ("NaN", isNothing)+  ]++testApplyPath :: Ctx -> Assertion+testApplyPath ctx =+  sequence_ $ do+    (key, path, final) <- applyPathVectors ctx+    return $+      assertEqual path final $+        applyPath ctx (fromJust $ parsePath path) key++testBadApplyPath :: Ctx -> Assertion+testBadApplyPath ctx =+  sequence_ $ do+    (key, path) <- badApplyPathVectors ctx+    return $+      assertBool path $+        isLeft $+          applyPath ctx (fromJust $ parsePath path) key++testDerivePubPath :: Ctx -> Assertion+testDerivePubPath ctx =+  sequence_ $ do+    (key, path, final) <- derivePubPathVectors ctx+    return $+      assertEqual path final $+        derivePubPath ctx (fromString path :: SoftPath) key++testDerivePrvPath :: Ctx -> Assertion+testDerivePrvPath ctx =+  sequence_ $ do+    (key, path, final) <- derivePrvPathVectors ctx+    return $+      assertEqual path final $+        derivePath ctx (fromString path :: DerivPath) key++derivePubPathVectors :: Ctx -> [(XPubKey, String, XPubKey)]+derivePubPathVectors ctx =+  [ (xpub, "M", xpub),+    (xpub, "M/8", pubSubKey ctx xpub 8),+    (xpub, "M/8/30/1", foldl (pubSubKey ctx) xpub [8, 30, 1])+  ]+  where+    xprv =+      fromJust $+        xPrvImport+          btc+          "xprv9s21ZrQH143K46iDVRSyFfGfMgQjzC4BV3ZUfNbG7PHQrJjE53ofAn5gYkp6KQ\+          \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB"+    xpub = deriveXPubKey ctx xprv++derivePrvPathVectors :: Ctx -> [(XPrvKey, String, XPrvKey)]+derivePrvPathVectors ctx =+  [ (xprv, "m", xprv),+    (xprv, "M", xprv),+    (xprv, "m/8'", hardSubKey ctx xprv 8),+    (xprv, "M/8'", hardSubKey ctx xprv 8),+    ( xprv,+      "m/8'/30/1",+      foldl (prvSubKey ctx) (hardSubKey ctx xprv 8) [30, 1]+    ),+    ( xprv,+      "M/8'/30/1",+      foldl (prvSubKey ctx) (hardSubKey ctx xprv 8) [30, 1]+    ),+    ( xprv,+      "m/3/20",+      foldl (prvSubKey ctx) xprv [3, 20]+    ),+    ( xprv,+      "M/3/20",+      foldl (prvSubKey ctx) xprv [3, 20]+    )+  ]+  where+    xprv =+      fromJust $+        xPrvImport+          btc+          "xprv9s21ZrQH143K46iDVRSyFfGfMgQjzC4BV3ZUfNbG7PHQrJjE53ofAn5gYkp6KQ\+          \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB"++applyPathVectors :: Ctx -> [(XKey, String, Either String XKey)]+applyPathVectors ctx =+  [ (XPrv xprv btc, "m", Right (XPrv xprv btc)),+    (XPrv xprv btc, "M", Right (XPub xpub btc)),+    (XPrv xprv btc, "m/8'", Right (XPrv (hardSubKey ctx xprv 8) btc)),+    ( XPrv xprv btc,+      "M/8'",+      Right (XPub (deriveXPubKey ctx (hardSubKey ctx xprv 8)) btc)+    ),+    ( XPrv xprv btc,+      "m/8'/30/1",+      Right (XPrv (foldl (prvSubKey ctx) (hardSubKey ctx xprv 8) [30, 1]) btc)+    ),+    ( XPrv xprv btc,+      "M/8'/30/1",+      Right+        ( XPub+            (deriveXPubKey ctx (foldl (prvSubKey ctx) (hardSubKey ctx xprv 8) [30, 1]))+            btc+        )+    ),+    (XPrv xprv btc, "m/3/20", Right (XPrv (foldl (prvSubKey ctx) xprv [3, 20]) btc)),+    ( XPrv xprv btc,+      "M/3/20",+      Right (XPub (deriveXPubKey ctx (foldl (prvSubKey ctx) xprv [3, 20])) btc)+    ),+    ( XPub xpub btc,+      "M/3/20",+      Right (XPub (deriveXPubKey ctx (foldl (prvSubKey ctx) xprv [3, 20])) btc)+    )+  ]+  where+    xprv =+      fromJust $+        xPrvImport+          btc+          "xprv9s21ZrQH143K46iDVRSyFfGfMgQjzC4BV3ZUfNbG7PHQrJjE53ofAn5gYkp6KQ\+          \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB"+    xpub = deriveXPubKey ctx xprv++badApplyPathVectors :: Ctx -> [(XKey, String)]+badApplyPathVectors ctx =+  [ (XPub xpub btc, "m/8'"),+    (XPub xpub btc, "M/8'"),+    (XPub xpub btc, "M/1/2/3'/4/5")+  ]+  where+    xprv =+      fromJust $+        xPrvImport+          btc+          "xprv9s21ZrQH143K46iDVRSyFfGfMgQjzC4BV3ZUfNbG7PHQrJjE53ofAn5gYkp6KQ\+          \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB"+    xpub = deriveXPubKey ctx xprv++-- BIP 0032 Test Vectors+-- https://en.bitcoin.it/wiki/BIP_0032_TestVectors++bip44Addr :: DerivPath+bip44Addr = Deriv :| 44 :| 0 :| 0 :/ 0 :/ 0++vectorSpec :: Ctx -> TestKey -> [TestVector] -> Spec+vectorSpec ctx mTxt vecTxt =+  forM_ (parseVector ctx mTxt vecTxt) $ \(d, m, v) ->+    it ("chain " <> cs d) $ runVector ctx m v++runVector :: Ctx -> XPrvKey -> TestVector -> Assertion+runVector ctx m v = do+  assertBool "xPrvID" $ encodeHex ((runPutS . serialize) (xPrvID ctx m)) == head v+  assertBool "xPrvFP" $ encodeHex ((runPutS . serialize) (xPrvFP ctx m)) == v !! 1+  assertBool "xPrvAddr" $+    addrToText btc (xPubAddr ctx $ deriveXPubKey ctx m) == Just (v !! 2)+  assertBool "bip44Addr" $+    addrToText btc (xPubAddr ctx $ deriveXPubKey ctx $ derivePath ctx bip44Addr m)+      == Just (v !! 3)+  assertBool "prvKey" $ encodeHex m.key.get == v !! 4+  assertBool "xPrvWIF" $ xPrvWif btc m == v !! 5+  assertBool "pubKey" $+    encodeHex (exportPubKey ctx True (deriveXPubKey ctx m).key) == v !! 6+  assertBool "chain code" $ encodeHex (runPutS (serialize m.chain)) == v !! 7+  assertBool "Hex PubKey" $+    encodeHex (marshal (btc, ctx) (deriveXPubKey ctx m)) == v !! 8+  assertBool "Hex PrvKey" $ encodeHex (marshal btc m) == v !! 9+  assertBool "Base58 PubKey" $ xPubExport btc ctx (deriveXPubKey ctx m) == v !! 10+  assertBool "Base58 PrvKey" $ xPrvExport btc m == v !! 11++-- This function was used to generate addition data for the test vectors+genVector :: Ctx -> XPrvKey -> [(Text, Text)]+genVector ctx m =+  [ ("xPrvID", encodeHex (runPutS . serialize $ xPrvID ctx m)),+    ("xPrvFP", encodeHex (runPutS . serialize $ xPrvFP ctx m)),+    ("xPrvAddr", fromJust $ addrToText btc (xPubAddr ctx $ deriveXPubKey ctx m)),+    ( "bip44Addr",+      fromJust $+        addrToText btc (xPubAddr ctx $ deriveXPubKey ctx $ derivePath ctx bip44Addr m)+    ),+    ("prvKey", encodeHex m.key.get),+    ("xPrvWIF", xPrvWif btc m),+    ("pubKey", encodeHex (exportPubKey ctx True (deriveXPubKey ctx m).key)),+    ("chain code", encodeHex ((runPutS . serialize) m.chain)),+    ("Hex PubKey", encodeHex (marshal (btc, ctx) (deriveXPubKey ctx m))),+    ("Hex PrvKey", encodeHex (marshal btc m))+  ]++parseVector :: Ctx -> TestKey -> [TestVector] -> [(Text, XPrvKey, TestVector)]+parseVector ctx mTxt vs =+  go <$> vs+  where+    mast = makeXPrvKey $ fromJust $ decodeHex mTxt+    go (d : vec) =+      let deriv = ((.get) . fromJust . parsePath) (cs d)+       in (d, derivePath ctx deriv mast, vec)+    go _ = undefined++type TestVector = [Text]++type TestKey = Text++m1 :: TestKey+m1 = "000102030405060708090a0b0c0d0e0f"++vector1 :: [TestVector]+vector1 =+  [ [ "m",+      "3442193e1bb70916e914552172cd4e2dbc9df811",+      "3442193e",+      "15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma",+      "1NQpH6Nf8QtR2HphLRcvuVqfhXBXsiWn8r",+      "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35",+      "L52XzL2cMkHxqxBXRyEpnPQZGUs3uKiL3R11XbAdHigRzDozKZeW",+      "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2",+      "873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508",+      "0488b21e000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d5080339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2",+      "0488ade4000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d50800e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35",+      "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",+      "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"+    ],+    [ "m/0'",+      "5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7",+      "5c1bd648",+      "19Q2WoS5hSS6T8GjhK8KZLMgmWaq4neXrh",+      "1DDVw6BRKUv9U8Hzg5rGsia13nDrgJQpBd",+      "edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea",+      "L5BmPijJjrKbiUfG4zbiFKNqkvuJ8usooJmzuD7Z8dkRoTThYnAT",+      "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56",+      "47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141",+      "0488b21e013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56",+      "0488ade4013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae623614100edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea",+      "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw",+      "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7"+    ],+    [ "m/0'/1",+      "bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe",+      "bef5a2f9",+      "1JQheacLPdM5ySCkrZkV66G2ApAXe1mqLj",+      "1KMg6dRggXSkpz9fFyU76ru83TUSwPePEZ",+      "3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368",+      "KyFAjQ5rgrKvhXvNMtFB5PCSKUYD1yyPEe3xr3T34TZSUHycXtMM",+      "03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c",+      "2a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19",+      "0488b21e025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c1903501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c",+      "0488ade4025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19003c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368",+      "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ",+      "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"+    ],+    [ "m/0'/1/2'",+      "ee7ab90cde56a8c0e2bb086ac49748b8db9dce72",+      "ee7ab90c",+      "1NjxqbA9aZWnh17q1UW3rB4EPu79wDXj7x",+      "1WykKhR25y7VDT21nZEwUUKSKDz9pENJh",+      "cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca",+      "L43t3od1Gh7Lj55Bzjj1xDAgJDcL7YFo2nEcNaMGiyRZS1CidBVU",+      "0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2",+      "04466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f",+      "0488b21e03bef5a2f98000000204466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2",+      "0488ade403bef5a2f98000000204466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f00cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca",+      "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5",+      "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM"+    ],+    [ "m/0'/1/2'/2",+      "d880d7d893848509a62d8fb74e32148dac68412f",+      "d880d7d8",+      "1LjmJcdPnDHhNTUgrWyhLGnRDKxQjoxAgt",+      "1asQ3smHhv2nv5R6hPpiUfkEorJpsdwwx",+      "0f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4",+      "KwjQsVuMjbCP2Zmr3VaFaStav7NvevwjvvkqrWd5Qmh1XVnCteBR",+      "02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29",+      "cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd",+      "0488b21e04ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29",+      "0488ade404ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd000f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4",+      "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV",+      "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334"+    ],+    [ "m/0'/1/2'/2/1000000000",+      "d69aa102255fed74378278c7812701ea641fdf32",+      "d69aa102",+      "1LZiqrop2HGR4qrH1ULZPyBpU6AUP49Uam",+      "1HXJog342VFdc68AB9Cb6LwVmCjvcLMiwm",+      "471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8",+      "Kybw8izYevo5xMh1TK7aUr7jHFCxXS1zv8p3oqFz3o2zFbhRXHYs",+      "022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011",+      "c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e",+      "0488b21e05d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011",+      "0488ade405d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e00471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8",+      "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy",+      "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76"+    ]+  ]++m2 :: TestKey+m2 = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"++vector2 :: [TestVector]+vector2 =+  [ [ "m",+      "bd16bee53961a47d6ad888e29545434a89bdfe95",+      "bd16bee5",+      "1JEoxevbLLG8cVqeoGKQiAwoWbNYSUyYjg",+      "148CGtv7bwcC933EHtcDfzDQVneur1R8Y1",+      "4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e",+      "KyjXhyHF9wTphBkfpxjL8hkDXDUSbE3tKANT94kXSyh6vn6nKaoy",+      "03cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7",+      "60499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689",+      "0488b21e00000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd968903cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7",+      "0488ade400000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689004b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e",+      "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB",+      "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U"+    ],+    [ "m/0",+      "5a61ff8eb7aaca3010db97ebda76121610b78096",+      "5a61ff8e",+      "19EuDJdgfRkwCmRzbzVBHZWQG9QNWhftbZ",+      "1KVyTSpsBGYs7NdyZmArEpVTfWJQSgiDCx",+      "abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e",+      "L2ysLrR6KMSAtx7uPqmYpoTeiRzydXBattRXjXz5GDFPrdfPzKbj",+      "02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea",+      "f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c",+      "0488b21e01bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea",+      "0488ade401bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c00abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e",+      "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH",+      "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt"+    ],+    [ "m/0/2147483647'",+      "d8ab493736da02f11ed682f88339e720fb0379d1",+      "d8ab4937",+      "1Lke9bXGhn5VPrBuXgN12uGUphrttUErmk",+      "14MFLsfx1nc4RKiaH9khqDTNL9CRz3q347",+      "877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93",+      "L1m5VpbXmMp57P3knskwhoMTLdhAAaXiHvnGLMribbfwzVRpz2Sr",+      "03c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b",+      "be17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d9",+      "0488b21e025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d903c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b",+      "0488ade4025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d900877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93",+      "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a",+      "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9"+    ],+    [ "m/0/2147483647'/1",+      "78412e3a2296a40de124307b6485bd19833e2e34",+      "78412e3a",+      "1BxrAr2pHpeBheusmd6fHDP2tSLAUa3qsW",+      "19ou31MGyGW9VFx7woKBqwLe5JHhQBYaDD",+      "704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7",+      "KzyzXnznxSv249b4KuNkBwowaN3akiNeEHy5FWoPCJpStZbEKXN2",+      "03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9",+      "f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb",+      "0488b21e03d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9",+      "0488ade403d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb00704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7",+      "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon",+      "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef"+    ],+    [ "m/0/2147483647'/1/2147483646'",+      "31a507b815593dfc51ffc7245ae7e5aee304246e",+      "31a507b8",+      "15XVotxCAV7sRx1PSCkQNsGw3W9jT9A94R",+      "18GYmRm4nyjk8ydvoVXFxMWQvxhksEFDZR",+      "f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d",+      "L5KhaMvPYRW1ZoFmRjUtxxPypQ94m6BcDrPhqArhggdaTbbAFJEF",+      "02d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0",+      "637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e29",+      "0488b21e0478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2902d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0",+      "0488ade40478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2900f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d",+      "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL",+      "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc"+    ],+    [ "m/0/2147483647'/1/2147483646'/2",+      "26132fdbe7bf89cbc64cf8dafa3f9f88b8666220",+      "26132fdb",+      "14UKfRV9ZPUp6ZC9PLhqbRtxdihW9em3xt",+      "1758mgwNZhyzpRLe4u7FjqpJtqKpaGhXh7",+      "bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23",+      "L3WAYNAZPxx1fr7KCz7GN9nD5qMBnNiqEJNJMU1z9MMaannAt4aK",+      "024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c",+      "9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271",+      "0488b21e0531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c",+      "0488ade40531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed27100bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23",+      "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt",+      "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j"+    ]+  ]++m3 :: TestKey+m3 = "4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be"++vector3 :: [TestVector]+vector3 =+  [ [ "m",+      "41d63b50d8dd5e730cdf4c79a56fc929a757c548",+      "41d63b50",+      "1717ZYpXhZW5CqAbWSjDJbCey3FyKUmCSf",+      "17rxURoF96VhmkcEGCj5LNQkmN9HVhWb7F",+      "00ddb80b067e0d4993197fe10f2657a844a384589847602d56f0c629c81aae32",+      "KwFPqAq9SKx1sPg15Qk56mqkHwrfGPuywtLUxoWPkiTSBoxCs8am",+      "03683af1ba5743bdfc798cf814efeeab2735ec52d95eced528e692b8e34c4e5669",+      "01d28a3e53cffa419ec122c968b3259e16b65076495494d97cae10bbfec3c36f",+      "0488b21e00000000000000000001d28a3e53cffa419ec122c968b3259e16b65076495494d97cae10bbfec3c36f03683af1ba5743bdfc798cf814efeeab2735ec52d95eced528e692b8e34c4e5669",+      "0488ade400000000000000000001d28a3e53cffa419ec122c968b3259e16b65076495494d97cae10bbfec3c36f0000ddb80b067e0d4993197fe10f2657a844a384589847602d56f0c629c81aae32",+      "xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13",+      "xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6"+    ],+    [ "m/0'",+      "c61368bb50e066acd95bd04a0b23d3837fb75698",+      "c61368bb",+      "1K4L3YxEwg8HkSEapM4iSiGuR6HeQ53KPX",+      "13QeQVJNNakdUU55P1fc8xPMkkeYVzn4o6",+      "491f7a2eebc7b57028e0d3faa0acda02e75c33b03c48fb288c41e2ea44e1daef",+      "KyfrPaeirL5yYAoZvfzyoKXSdszeLqg5vb6dNy9ymvjzZrMZY8GW",+      "026557fdda1d5d43d79611f784780471f086d58e8126b8c40acb82272a7712e7f2",+      "e5fea12a97b927fc9dc3d2cb0d1ea1cf50aa5a1fdc1f933e8906bb38df3377bd",+      "0488b21e0141d63b5080000000e5fea12a97b927fc9dc3d2cb0d1ea1cf50aa5a1fdc1f933e8906bb38df3377bd026557fdda1d5d43d79611f784780471f086d58e8126b8c40acb82272a7712e7f2",+      "0488ade40141d63b5080000000e5fea12a97b927fc9dc3d2cb0d1ea1cf50aa5a1fdc1f933e8906bb38df3377bd00491f7a2eebc7b57028e0d3faa0acda02e75c33b03c48fb288c41e2ea44e1daef",+      "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y",+      "xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L"+    ]+  ]
+ test/Haskoin/Crypto/Keys/MnemonicSpec.hs view
@@ -0,0 +1,637 @@+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedStrings #-}++module Haskoin.Crypto.Keys.MnemonicSpec (spec) where++import Control.Monad (forM_, zipWithM_)+import Data.Bits (shiftR, (.&.))+import Data.ByteString qualified as BS+import Data.Either (fromRight)+import Data.List (isPrefixOf)+import Data.Maybe (fromJust)+import Data.Serialize (Serialize, encode)+import Data.Text (Text)+import Data.Text qualified as T+import Data.Word (Word32, Word64)+import Haskoin (btc)+import Haskoin.Crypto+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.HUnit+import Test.Hspec+import Test.QuickCheck hiding ((.&.))++spec :: Spec+spec =+  describe "mnemonic" $ do+    it "entropy to mnemonic sentence" toMnemonicTest+    it "mnemonic sentence to entropy" fromMnemonicTest+    it "mnemonic sentence to seed" mnemonicToSeedTest+    it "mnemonic sentence with invalid checksum" fromMnemonicInvalidTest+    it "empty mnemonic sentence is invalid" $ sequence_ [emptyMnemonicTest]+    it "generate 12 words" $ property toMnemonic128+    it "generate 18 words" $ property toMnemonic160+    it "generate 24 words" $ property toMnemonic256+    it "generate 48 words" $ property toMnemonic512+    it "generate any number of words" $ property toMnemonicVar+    it "encode and decode 128-bit entropy" $ property fromToMnemonic128+    it "encode and decode 160-bit entropy" $ property fromToMnemonic160+    it "encode and decode 256-bit entropy" $ property fromToMnemonic256+    it "encode and decode 512-bit entropy" $ property fromToMnemonic512+    it "encode and decode n-bit entropy" $ property fromToMnemonicVar+    it "convert 128-bit mnemonic to seed" $ property mnemonicToSeed128+    it "convert 160-bit mnemonic to seed" $ property mnemonicToSeed160+    it "convert 256-bit mnemonic to seed" $ property mnemonicToSeed256+    it "convert 512-bit mnemonic to seed" $ property mnemonicToSeed512+    it "convert n-bit mnemonic to seed" $ property mnemonicToSeedVar+    it "get bits" $ property getBitsByteCount+    it "get end bits" $ property getBitsEndBits+    it "passes the Trezor vectors" trezorVectorTests++toMnemonicTest :: Assertion+toMnemonicTest = zipWithM_ f ents mss+  where+    f e m = assertEqual "" m . h $ e+    h =+      fromRight (error "Could not decode mnemonic sentence")+        . toMnemonic+        . fromJust+        . decodeHex++fromMnemonicTest :: Assertion+fromMnemonicTest = zipWithM_ f ents mss+  where+    f e = assertEqual "" e . h+    h =+      encodeHex+        . fromRight (error "Could not decode mnemonic sentence")+        . fromMnemonic++mnemonicToSeedTest :: Assertion+mnemonicToSeedTest = zipWithM_ f mss seeds+  where+    f m s = assertEqual "" s . h $ m+    h =+      encodeHex+        . fromRight (error "Could not decode mnemonic seed")+        . mnemonicToSeed "TREZOR"++fromMnemonicInvalidTest :: Assertion+fromMnemonicInvalidTest = mapM_ f invalidMss+  where+    f = assertBool "" . h+    h m = case fromMnemonic m of+      Right _ -> False+      Left err -> "fromMnemonic: checksum failed:" `isPrefixOf` err++emptyMnemonicTest :: Assertion+emptyMnemonicTest =+  assertBool "" $+    case fromMnemonic "" of+      Right _ -> False+      Left err -> "fromMnemonic: empty mnemonic" `isPrefixOf` err++trezorVectorTests :: Assertion+trezorVectorTests =+  forM_ trezorVectors $ \(entTxt, mnem, seedTxt, xprvTxt) -> do+    let ent = fromJust $ decodeHex entTxt+        seed = fromJust $ decodeHex seedTxt+        xprv = fromJust $ xPrvImport btc xprvTxt+    assertEqual "toMnemonic" (Right mnem) $ toMnemonic ent+    assertEqual "mnemonicToSeed" (Right seed) $ mnemonicToSeed "TREZOR" mnem+    assertEqual "xPrv" xprv $ makeXPrvKey seed++ents :: [Text]+ents =+  [ "00000000000000000000000000000000",+    "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+    "80808080808080808080808080808080",+    "ffffffffffffffffffffffffffffffff",+    "000000000000000000000000000000000000000000000000",+    "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+    "808080808080808080808080808080808080808080808080",+    "ffffffffffffffffffffffffffffffffffffffffffffffff",+    "0000000000000000000000000000000000000000000000000000000000000000",+    "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+    "8080808080808080808080808080808080808080808080808080808080808080",+    "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",+    "77c2b00716cec7213839159e404db50d",+    "b63a9c59a6e641f288ebc103017f1da9f8290b3da6bdef7b",+    "3e141609b97933b66a060dcddc71fad1d91677db872031e85f4c015c5e7e8982",+    "0460ef47585604c5660618db2e6a7e7f",+    "72f60ebac5dd8add8d2a25a797102c3ce21bc029c200076f",+    "2c85efc7f24ee4573d2b81a6ec66cee209b2dcbd09d8eddc51e0215b0b68e416",+    "eaebabb2383351fd31d703840b32e9e2",+    "7ac45cfe7722ee6c7ba84fbc2d5bd61b45cb2fe5eb65aa78",+    "4fa1a8bc3e6d80ee1316050e862c1812031493212b7ec3f3bb1b08f168cabeef",+    "18ab19a9f54a9274f03e5209a2ac8a91",+    "18a2e1d81b8ecfb2a333adcb0c17a5b9eb76cc5d05db91a4",+    "15da872c95a13dd738fbf50e427583ad61f18fd99f628c417a61cf8343c90419"+  ]++mss :: [Mnemonic]+mss =+  [ "abandon abandon abandon abandon abandon abandon abandon abandon abandon\+    \ abandon abandon about",+    "legal winner thank year wave sausage worth useful legal winner thank\+    \ yellow",+    "letter advice cage absurd amount doctor acoustic avoid letter advice\+    \ cage above",+    "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong",+    "abandon abandon abandon abandon abandon abandon abandon abandon abandon\+    \ abandon abandon abandon abandon abandon abandon abandon abandon agent",+    "legal winner thank year wave sausage worth useful legal winner thank\+    \ year wave sausage worth useful legal will",+    "letter advice cage absurd amount doctor acoustic avoid letter advice\+    \ cage absurd amount doctor acoustic avoid letter always",+    "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\+    \ when",+    "abandon abandon abandon abandon abandon abandon abandon abandon abandon\+    \ abandon abandon abandon abandon abandon abandon abandon abandon abandon\+    \ abandon abandon abandon abandon abandon art",+    "legal winner thank year wave sausage worth useful legal winner thank\+    \ year wave sausage worth useful legal winner thank year wave sausage\+    \ worth title",+    "letter advice cage absurd amount doctor acoustic avoid letter advice\+    \ cage absurd amount doctor acoustic avoid letter advice cage absurd\+    \ amount doctor acoustic bless",+    "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\+    \ zoo zoo zoo zoo zoo vote",+    "jelly better achieve collect unaware mountain thought cargo oxygen act\+    \ hood bridge",+    "renew stay biology evidence goat welcome casual join adapt armor shuffle\+    \ fault little machine walk stumble urge swap",+    "dignity pass list indicate nasty swamp pool script soccer toe leaf photo\+    \ multiply desk host tomato cradle drill spread actor shine dismiss\+    \ champion exotic",+    "afford alter spike radar gate glance object seek swamp infant panel\+    \ yellow",+    "indicate race push merry suffer human cruise dwarf pole review arch keep\+    \ canvas theme poem divorce alter left",+    "clutch control vehicle tonight unusual clog visa ice plunge glimpse\+    \ recipe series open hour vintage deposit universe tip job dress radar\+    \ refuse motion taste",+    "turtle front uncle idea crush write shrug there lottery flower risk\+    \ shell",+    "kiss carry display unusual confirm curtain upgrade antique rotate hello\+    \ void custom frequent obey nut hole price segment",+    "exile ask congress lamp submit jacket era scheme attend cousin alcohol\+    \ catch course end lucky hurt sentence oven short ball bird grab wing top",+    "board flee heavy tunnel powder denial science ski answer betray cargo\+    \ cat",+    "board blade invite damage undo sun mimic interest slam gaze truly\+    \ inherit resist great inject rocket museum chief",+    "beyond stage sleep clip because twist token leaf atom beauty genius food\+    \ business side grid unable middle armed observe pair crouch tonight away\+    \ coconut"+  ]++seeds :: [Text]+seeds =+  [ "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a69\+    \87599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04",+    "2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe1\+    \296106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607",+    "d71de856f81a8acc65e6fc851a38d4d7ec216fd0796d0a6827a3ad6ed5511a30fa280f1\+    \2eb2e47ed2ac03b5c462a0358d18d69fe4f985ec81778c1b370b652a8",+    "ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a133325729\+    \17f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069",+    "035895f2f481b1b0f01fcf8c289c794660b289981a78f8106447707fdd9666ca06da5a9\+    \a565181599b79f53b844d8a71dd9f439c52a3d7b3e8a79c906ac845fa",+    "f2b94508732bcbacbcc020faefecfc89feafa6649a5491b8c952cede496c214a0c7b3c3\+    \92d168748f2d4a612bada0753b52a1c7ac53c1e93abd5c6320b9e95dd",+    "107d7c02a5aa6f38c58083ff74f04c607c2d2c0ecc55501dadd72d025b751bc27fe913f\+    \fb796f841c49b1d33b610cf0e91d3aa239027f5e99fe4ce9e5088cd65",+    "0cd6e5d827bb62eb8fc1e262254223817fd068a74b5b449cc2f667c3f1f985a76379b43\+    \348d952e2265b4cd129090758b3e3c2c49103b5051aac2eaeb890a528",+    "bda85446c68413707090a52022edd26a1c9462295029f2e60cd7c4f2bbd3097170af7a4\+    \d73245cafa9c3cca8d561a7c3de6f5d4a10be8ed2a5e608d68f92fcc8",+    "bc09fca1804f7e69da93c2f2028eb238c227f2e9dda30cd63699232578480a4021b146a\+    \d717fbb7e451ce9eb835f43620bf5c514db0f8add49f5d121449d3e87",+    "c0c519bd0e91a2ed54357d9d1ebef6f5af218a153624cf4f2da911a0ed8f7a09e2ef61a\+    \f0aca007096df430022f7a2b6fb91661a9589097069720d015e4e982f",+    "dd48c104698c30cfe2b6142103248622fb7bb0ff692eebb00089b32d22484e1613912f0\+    \a5b694407be899ffd31ed3992c456cdf60f5d4564b8ba3f05a69890ad",+    "b5b6d0127db1a9d2226af0c3346031d77af31e918dba64287a1b44b8ebf63cdd52676f6\+    \72a290aae502472cf2d602c051f3e6f18055e84e4c43897fc4e51a6ff",+    "9248d83e06f4cd98debf5b6f010542760df925ce46cf38a1bdb4e4de7d21f5c39366941\+    \c69e1bdbf2966e0f6e6dbece898a0e2f0a4c2b3e640953dfe8b7bbdc5",+    "ff7f3184df8696d8bef94b6c03114dbee0ef89ff938712301d27ed8336ca89ef9635da2\+    \0af07d4175f2bf5f3de130f39c9d9e8dd0472489c19b1a020a940da67",+    "65f93a9f36b6c85cbe634ffc1f99f2b82cbb10b31edc7f087b4f6cb9e976e9faf76ff41\+    \f8f27c99afdf38f7a303ba1136ee48a4c1e7fcd3dba7aa876113a36e4",+    "3bbf9daa0dfad8229786ace5ddb4e00fa98a044ae4c4975ffd5e094dba9e0bb289349db\+    \e2091761f30f382d4e35c4a670ee8ab50758d2c55881be69e327117ba",+    "fe908f96f46668b2d5b37d82f558c77ed0d69dd0e7e043a5b0511c48c2f1064694a956f\+    \86360c93dd04052a8899497ce9e985ebe0c8c52b955e6ae86d4ff4449",+    "bdfb76a0759f301b0b899a1e3985227e53b3f51e67e3f2a65363caedf3e32fde42a66c4\+    \04f18d7b05818c95ef3ca1e5146646856c461c073169467511680876c",+    "ed56ff6c833c07982eb7119a8f48fd363c4a9b1601cd2de736b01045c5eb8ab4f57b079\+    \403485d1c4924f0790dc10a971763337cb9f9c62226f64fff26397c79",+    "095ee6f817b4c2cb30a5a797360a81a40ab0f9a4e25ecd672a3f58a0b5ba0687c096a6b\+    \14d2c0deb3bdefce4f61d01ae07417d502429352e27695163f7447a8c",+    "6eff1bb21562918509c73cb990260db07c0ce34ff0e3cc4a8cb3276129fbcb300bddfe0\+    \05831350efd633909f476c45c88253276d9fd0df6ef48609e8bb7dca8",+    "f84521c777a13b61564234bf8f8b62b3afce27fc4062b51bb5e62bdfecb23864ee6ecf0\+    \7c1d5a97c0834307c5c852d8ceb88e7c97923c0a3b496bedd4e5f88a9",+    "b15509eaa2d09d3efd3e006ef42151b30367dc6e3aa5e44caba3fe4d3e352e65101fbdb\+    \86a96776b91946ff06f8eac594dc6ee1d3e82a42dfe1b40fef6bcc3fd"+  ]++invalidMss :: [Mnemonic]+invalidMss =+  [ "abandon abandon abandon abandon abandon abandon abandon abandon abandon\+    \ abandon abandon abandon",+    "legal winner thank year wave sausage worth useful legal winner thank\+    \ thank",+    "letter advice cage absurd amount doctor acoustic avoid letter advice\+    \ cage sausage",+    "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo",+    "abandon abandon abandon abandon abandon abandon abandon abandon abandon\+    \ abandon abandon abandon abandon abandon abandon abandon abandon abandon",+    "legal winner thank year wave sausage worth useful legal winner thank\+    \ year wave sausage worth useful legal letter",+    "letter advice cage absurd amount doctor acoustic avoid letter advice\+    \ cage absurd amount doctor acoustic avoid letter abandon",+    "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\+    \ zoo",+    "abandon abandon abandon abandon abandon abandon abandon abandon abandon\+    \ abandon abandon abandon abandon abandon abandon abandon abandon abandon\+    \ abandon abandon abandon abandon abandon abandon",+    "legal winner thank year wave sausage worth useful legal winner thank\+    \ year wave sausage worth useful legal winner thank year wave sausage\+    \ worth letter",+    "letter advice cage absurd amount doctor acoustic avoid letter advice\+    \ cage absurd amount doctor acoustic avoid letter advice cage absurd\+    \ amount doctor acoustic letter",+    "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\+    \ zoo zoo zoo zoo zoo zoo",+    "jelly better achieve collect unaware mountain thought cargo oxygen act\+    \ hood zoo",+    "renew stay biology evidence goat welcome casual join adapt armor shuffle\+    \ fault little machine walk stumble urge zoo",+    "dignity pass list indicate nasty swamp pool script soccer toe leaf photo\+    \ multiply desk host tomato cradle drill spread actor shine dismiss\+    \ champion zoo",+    "afford alter spike radar gate glance object seek swamp infant panel\+    \ zoo",+    "indicate race push merry suffer human cruise dwarf pole review arch keep\+    \ canvas theme poem divorce alter zoo",+    "clutch control vehicle tonight unusual clog visa ice plunge glimpse\+    \ recipe series open hour vintage deposit universe tip job dress radar\+    \ refuse motion zoo",+    "turtle front uncle idea crush write shrug there lottery flower risk\+    \ zoo",+    "kiss carry display unusual confirm curtain upgrade antique rotate hello\+    \ void custom frequent obey nut hole price zoo",+    "exile ask congress lamp submit jacket era scheme attend cousin alcohol\+    \ catch course end lucky hurt sentence oven short ball bird grab wing zoo",+    "board flee heavy tunnel powder denial science ski answer betray cargo\+    \ zoo",+    "board blade invite damage undo sun mimic interest slam gaze truly\+    \ inherit resist great inject rocket museum zoo",+    "beyond stage sleep clip because twist token leaf atom beauty genius food\+    \ business side grid unable middle armed observe pair crouch tonight away\+    \ zoo"+  ]++-- Vectors from https://github.com/trezor/python-mnemonic/blob/master/vectors.json+trezorVectors :: [(Text, Mnemonic, Text, Text)]+trezorVectors =+  [ ( "00000000000000000000000000000000",+      "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",+      "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04",+      "xprv9s21ZrQH143K3h3fDYiay8mocZ3afhfULfb5GX8kCBdno77K4HiA15Tg23wpbeF1pLfs1c5SPmYHrEpTuuRhxMwvKDwqdKiGJS9XFKzUsAF"+    ),+    ( "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+      "legal winner thank year wave sausage worth useful legal winner thank yellow",+      "2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe1296106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607",+      "xprv9s21ZrQH143K2gA81bYFHqU68xz1cX2APaSq5tt6MFSLeXnCKV1RVUJt9FWNTbrrryem4ZckN8k4Ls1H6nwdvDTvnV7zEXs2HgPezuVccsq"+    ),+    ( "80808080808080808080808080808080",+      "letter advice cage absurd amount doctor acoustic avoid letter advice cage above",+      "d71de856f81a8acc65e6fc851a38d4d7ec216fd0796d0a6827a3ad6ed5511a30fa280f12eb2e47ed2ac03b5c462a0358d18d69fe4f985ec81778c1b370b652a8",+      "xprv9s21ZrQH143K2shfP28KM3nr5Ap1SXjz8gc2rAqqMEynmjt6o1qboCDpxckqXavCwdnYds6yBHZGKHv7ef2eTXy461PXUjBFQg6PrwY4Gzq"+    ),+    ( "ffffffffffffffffffffffffffffffff",+      "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong",+      "ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a13332572917f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069",+      "xprv9s21ZrQH143K2V4oox4M8Zmhi2Fjx5XK4Lf7GKRvPSgydU3mjZuKGCTg7UPiBUD7ydVPvSLtg9hjp7MQTYsW67rZHAXeccqYqrsx8LcXnyd"+    ),+    ( "000000000000000000000000000000000000000000000000",+      "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",+      "035895f2f481b1b0f01fcf8c289c794660b289981a78f8106447707fdd9666ca06da5a9a565181599b79f53b844d8a71dd9f439c52a3d7b3e8a79c906ac845fa",+      "xprv9s21ZrQH143K3mEDrypcZ2usWqFgzKB6jBBx9B6GfC7fu26X6hPRzVjzkqkPvDqp6g5eypdk6cyhGnBngbjeHTe4LsuLG1cCmKJka5SMkmU"+    ),+    ( "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+      "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will",+      "f2b94508732bcbacbcc020faefecfc89feafa6649a5491b8c952cede496c214a0c7b3c392d168748f2d4a612bada0753b52a1c7ac53c1e93abd5c6320b9e95dd",+      "xprv9s21ZrQH143K3Lv9MZLj16np5GzLe7tDKQfVusBni7toqJGcnKRtHSxUwbKUyUWiwpK55g1DUSsw76TF1T93VT4gz4wt5RM23pkaQLnvBh7"+    ),+    ( "808080808080808080808080808080808080808080808080",+      "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always",+      "107d7c02a5aa6f38c58083ff74f04c607c2d2c0ecc55501dadd72d025b751bc27fe913ffb796f841c49b1d33b610cf0e91d3aa239027f5e99fe4ce9e5088cd65",+      "xprv9s21ZrQH143K3VPCbxbUtpkh9pRG371UCLDz3BjceqP1jz7XZsQ5EnNkYAEkfeZp62cDNj13ZTEVG1TEro9sZ9grfRmcYWLBhCocViKEJae"+    ),+    ( "ffffffffffffffffffffffffffffffffffffffffffffffff",+      "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when",+      "0cd6e5d827bb62eb8fc1e262254223817fd068a74b5b449cc2f667c3f1f985a76379b43348d952e2265b4cd129090758b3e3c2c49103b5051aac2eaeb890a528",+      "xprv9s21ZrQH143K36Ao5jHRVhFGDbLP6FCx8BEEmpru77ef3bmA928BxsqvVM27WnvvyfWywiFN8K6yToqMaGYfzS6Db1EHAXT5TuyCLBXUfdm"+    ),+    ( "0000000000000000000000000000000000000000000000000000000000000000",+      "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",+      "bda85446c68413707090a52022edd26a1c9462295029f2e60cd7c4f2bbd3097170af7a4d73245cafa9c3cca8d561a7c3de6f5d4a10be8ed2a5e608d68f92fcc8",+      "xprv9s21ZrQH143K32qBagUJAMU2LsHg3ka7jqMcV98Y7gVeVyNStwYS3U7yVVoDZ4btbRNf4h6ibWpY22iRmXq35qgLs79f312g2kj5539ebPM"+    ),+    ( "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",+      "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title",+      "bc09fca1804f7e69da93c2f2028eb238c227f2e9dda30cd63699232578480a4021b146ad717fbb7e451ce9eb835f43620bf5c514db0f8add49f5d121449d3e87",+      "xprv9s21ZrQH143K3Y1sd2XVu9wtqxJRvybCfAetjUrMMco6r3v9qZTBeXiBZkS8JxWbcGJZyio8TrZtm6pkbzG8SYt1sxwNLh3Wx7to5pgiVFU"+    ),+    ( "8080808080808080808080808080808080808080808080808080808080808080",+      "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless",+      "c0c519bd0e91a2ed54357d9d1ebef6f5af218a153624cf4f2da911a0ed8f7a09e2ef61af0aca007096df430022f7a2b6fb91661a9589097069720d015e4e982f",+      "xprv9s21ZrQH143K3CSnQNYC3MqAAqHwxeTLhDbhF43A4ss4ciWNmCY9zQGvAKUSqVUf2vPHBTSE1rB2pg4avopqSiLVzXEU8KziNnVPauTqLRo"+    ),+    ( "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",+      "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote",+      "dd48c104698c30cfe2b6142103248622fb7bb0ff692eebb00089b32d22484e1613912f0a5b694407be899ffd31ed3992c456cdf60f5d4564b8ba3f05a69890ad",+      "xprv9s21ZrQH143K2WFF16X85T2QCpndrGwx6GueB72Zf3AHwHJaknRXNF37ZmDrtHrrLSHvbuRejXcnYxoZKvRquTPyp2JiNG3XcjQyzSEgqCB"+    ),+    ( "9e885d952ad362caeb4efe34a8e91bd2",+      "ozone drill grab fiber curtain grace pudding thank cruise elder eight picnic",+      "274ddc525802f7c828d8ef7ddbcdc5304e87ac3535913611fbbfa986d0c9e5476c91689f9c8a54fd55bd38606aa6a8595ad213d4c9c9f9aca3fb217069a41028",+      "xprv9s21ZrQH143K2oZ9stBYpoaZ2ktHj7jLz7iMqpgg1En8kKFTXJHsjxry1JbKH19YrDTicVwKPehFKTbmaxgVEc5TpHdS1aYhB2s9aFJBeJH"+    ),+    ( "6610b25967cdcca9d59875f5cb50b0ea75433311869e930b",+      "gravity machine north sort system female filter attitude volume fold club stay feature office ecology stable narrow fog",+      "628c3827a8823298ee685db84f55caa34b5cc195a778e52d45f59bcf75aba68e4d7590e101dc414bc1bbd5737666fbbef35d1f1903953b66624f910feef245ac",+      "xprv9s21ZrQH143K3uT8eQowUjsxrmsA9YUuQQK1RLqFufzybxD6DH6gPY7NjJ5G3EPHjsWDrs9iivSbmvjc9DQJbJGatfa9pv4MZ3wjr8qWPAK"+    ),+    ( "68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c",+      "hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy gospel tennis maple dilemma loan word shrug inflict delay length",+      "64c87cde7e12ecf6704ab95bb1408bef047c22db4cc7491c4271d170a1b213d20b385bc1588d9c7b38f1b39d415665b8a9030c9ec653d75e65f847d8fc1fc440",+      "xprv9s21ZrQH143K2XTAhys3pMNcGn261Fi5Ta2Pw8PwaVPhg3D8DWkzWQwjTJfskj8ofb81i9NP2cUNKxwjueJHHMQAnxtivTA75uUFqPFeWzk"+    ),+    ( "c0ba5a8e914111210f2bd131f3d5e08d",+      "scheme spot photo card baby mountain device kick cradle pact join borrow",+      "ea725895aaae8d4c1cf682c1bfd2d358d52ed9f0f0591131b559e2724bb234fca05aa9c02c57407e04ee9dc3b454aa63fbff483a8b11de949624b9f1831a9612",+      "xprv9s21ZrQH143K3FperxDp8vFsFycKCRcJGAFmcV7umQmcnMZaLtZRt13QJDsoS5F6oYT6BB4sS6zmTmyQAEkJKxJ7yByDNtRe5asP2jFGhT6"+    ),+    ( "6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3",+      "horn tenant knee talent sponsor spell gate clip pulse soap slush warm silver nephew swap uncle crack brave",+      "fd579828af3da1d32544ce4db5c73d53fc8acc4ddb1e3b251a31179cdb71e853c56d2fcb11aed39898ce6c34b10b5382772db8796e52837b54468aeb312cfc3d",+      "xprv9s21ZrQH143K3R1SfVZZLtVbXEB9ryVxmVtVMsMwmEyEvgXN6Q84LKkLRmf4ST6QrLeBm3jQsb9gx1uo23TS7vo3vAkZGZz71uuLCcywUkt"+    ),+    ( "9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863",+      "panda eyebrow bullet gorilla call smoke muffin taste mesh discover soft ostrich alcohol speed nation flash devote level hobby quick inner drive ghost inside",+      "72be8e052fc4919d2adf28d5306b5474b0069df35b02303de8c1729c9538dbb6fc2d731d5f832193cd9fb6aeecbc469594a70e3dd50811b5067f3b88b28c3e8d",+      "xprv9s21ZrQH143K2WNnKmssvZYM96VAr47iHUQUTUyUXH3sAGNjhJANddnhw3i3y3pBbRAVk5M5qUGFr4rHbEWwXgX4qrvrceifCYQJbbFDems"+    ),+    ( "23db8160a31d3e0dca3688ed941adbf3",+      "cat swing flag economy stadium alone churn speed unique patch report train",+      "deb5f45449e615feff5640f2e49f933ff51895de3b4381832b3139941c57b59205a42480c52175b6efcffaa58a2503887c1e8b363a707256bdd2b587b46541f5",+      "xprv9s21ZrQH143K4G28omGMogEoYgDQuigBo8AFHAGDaJdqQ99QKMQ5J6fYTMfANTJy6xBmhvsNZ1CJzRZ64PWbnTFUn6CDV2FxoMDLXdk95DQ"+    ),+    ( "8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0",+      "light rule cinnamon wrap drastic word pride squirrel upgrade then income fatal apart sustain crack supply proud access",+      "4cbdff1ca2db800fd61cae72a57475fdc6bab03e441fd63f96dabd1f183ef5b782925f00105f318309a7e9c3ea6967c7801e46c8a58082674c860a37b93eda02",+      "xprv9s21ZrQH143K3wtsvY8L2aZyxkiWULZH4vyQE5XkHTXkmx8gHo6RUEfH3Jyr6NwkJhvano7Xb2o6UqFKWHVo5scE31SGDCAUsgVhiUuUDyh"+    ),+    ( "066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad",+      "all hour make first leader extend hole alien behind guard gospel lava path output census museum junior mass reopen famous sing advance salt reform",+      "26e975ec644423f4a4c4f4215ef09b4bd7ef924e85d1d17c4cf3f136c2863cf6df0a475045652c57eb5fb41513ca2a2d67722b77e954b4b3fc11f7590449191d",+      "xprv9s21ZrQH143K3rEfqSM4QZRVmiMuSWY9wugscmaCjYja3SbUD3KPEB1a7QXJoajyR2T1SiXU7rFVRXMV9XdYVSZe7JoUXdP4SRHTxsT1nzm"+    ),+    ( "f30f8c1da665478f49b001d94c5fc452",+      "vessel ladder alter error federal sibling chat ability sun glass valve picture",+      "2aaa9242daafcee6aa9d7269f17d4efe271e1b9a529178d7dc139cd18747090bf9d60295d0ce74309a78852a9caadf0af48aae1c6253839624076224374bc63f",+      "xprv9s21ZrQH143K2QWV9Wn8Vvs6jbqfF1YbTCdURQW9dLFKDovpKaKrqS3SEWsXCu6ZNky9PSAENg6c9AQYHcg4PjopRGGKmdD313ZHszymnps"+    ),+    ( "c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05",+      "scissors invite lock maple supreme raw rapid void congress muscle digital elegant little brisk hair mango congress clump",+      "7b4a10be9d98e6cba265566db7f136718e1398c71cb581e1b2f464cac1ceedf4f3e274dc270003c670ad8d02c4558b2f8e39edea2775c9e232c7cb798b069e88",+      "xprv9s21ZrQH143K4aERa2bq7559eMCCEs2QmmqVjUuzfy5eAeDX4mqZffkYwpzGQRE2YEEeLVRoH4CSHxianrFaVnMN2RYaPUZJhJx8S5j6puX"+    ),+    ( "f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f",+      "void come effort suffer camp survey warrior heavy shoot primary clutch crush open amazing screen patrol group space point ten exist slush involve unfold",+      "01f5bced59dec48e362f2c45b5de68b9fd6c92c6634f44d6d40aab69056506f0e35524a518034ddc1192e1dacd32c1ed3eaa3c3b131c88ed8e7e54c49a5d0998",+      "xprv9s21ZrQH143K39rnQJknpH1WEPFJrzmAqqasiDcVrNuk926oizzJDDQkdiTvNPr2FYDYzWgiMiC63YmfPAa2oPyNB23r2g7d1yiK6WpqaQS"+    )+  ]++binWordsToBS :: (Serialize a) => [a] -> BS.ByteString+binWordsToBS = foldr f BS.empty+  where+    f b a = a `BS.append` encode b++{- Encode mnemonic -}++toMnemonic128 :: (Word64, Word64) -> Bool+toMnemonic128 (a, b) = l == 12+  where+    bs = encode a `BS.append` encode b+    l =+      length+        . T.words+        . fromRight (error "Could not decode mnemonic senttence")+        $ toMnemonic bs++toMnemonic160 :: (Word32, Word64, Word64) -> Bool+toMnemonic160 (a, b, c) = l == 15+  where+    bs = BS.concat [encode a, encode b, encode c]+    l =+      length+        . T.words+        . fromRight (error "Could not decode mnemonic sentence")+        $ toMnemonic bs++toMnemonic256 :: (Word64, Word64, Word64, Word64) -> Bool+toMnemonic256 (a, b, c, d) = l == 24+  where+    bs = BS.concat [encode a, encode b, encode c, encode d]+    l =+      length+        . T.words+        . fromRight (error "Could not decode mnemonic sentence")+        $ toMnemonic bs++toMnemonic512 ::+  ((Word64, Word64, Word64, Word64), (Word64, Word64, Word64, Word64)) -> Bool+toMnemonic512 ((a, b, c, d), (e, f, g, h)) = l == 48+  where+    bs =+      BS.concat+        [ encode a,+          encode b,+          encode c,+          encode d,+          encode e,+          encode f,+          encode g,+          encode h+        ]+    l =+      length+        . T.words+        . fromRight (error "Could not decode mnemonic sentence")+        $ toMnemonic bs++toMnemonicVar :: [Word32] -> Property+toMnemonicVar ls = not (null ls) && length ls <= 8 ==> l == wc+  where+    bs = binWordsToBS ls+    bl = BS.length bs+    cb = bl `div` 4+    wc = (cb + bl * 8) `div` 11+    l =+      length+        . T.words+        . fromRight (error "Could not decode mnemonic sentence")+        $ toMnemonic bs++{- Encode/Decode -}++fromToMnemonic128 :: (Word64, Word64) -> Bool+fromToMnemonic128 (a, b) = bs == bs'+  where+    bs = encode a `BS.append` encode b+    bs' =+      fromRight+        (error "Could not decode mnemonic entropy")+        (fromMnemonic =<< toMnemonic bs)++fromToMnemonic160 :: (Word32, Word64, Word64) -> Bool+fromToMnemonic160 (a, b, c) = bs == bs'+  where+    bs = BS.concat [encode a, encode b, encode c]+    bs' =+      fromRight+        (error "Could not decode mnemonic entropy")+        (fromMnemonic =<< toMnemonic bs)++fromToMnemonic256 :: (Word64, Word64, Word64, Word64) -> Bool+fromToMnemonic256 (a, b, c, d) = bs == bs'+  where+    bs = BS.concat [encode a, encode b, encode c, encode d]+    bs' =+      fromRight+        (error "Could not decode mnemonic entropy")+        (fromMnemonic =<< toMnemonic bs)++fromToMnemonic512 ::+  ((Word64, Word64, Word64, Word64), (Word64, Word64, Word64, Word64)) -> Bool+fromToMnemonic512 ((a, b, c, d), (e, f, g, h)) = bs == bs'+  where+    bs =+      BS.concat+        [ encode a,+          encode b,+          encode c,+          encode d,+          encode e,+          encode f,+          encode g,+          encode h+        ]+    bs' =+      fromRight+        (error "Could not decode mnemonic entropy")+        (fromMnemonic =<< toMnemonic bs)++fromToMnemonicVar :: [Word32] -> Property+fromToMnemonicVar ls = not (null ls) && length ls <= 8 ==> bs == bs'+  where+    bs = binWordsToBS ls+    bs' =+      fromRight+        (error "Could not decode mnemonic entropy")+        (fromMnemonic =<< toMnemonic bs)++{- Mnemonic to seed -}++mnemonicToSeed128 :: (Word64, Word64) -> Bool+mnemonicToSeed128 (a, b) = l == 64+  where+    bs = encode a `BS.append` encode b+    seed =+      fromRight+        (error "Could not decode mnemonic seed")+        (mnemonicToSeed "" =<< toMnemonic bs)+    l = BS.length seed++mnemonicToSeed160 :: (Word32, Word64, Word64) -> Bool+mnemonicToSeed160 (a, b, c) = l == 64+  where+    bs = BS.concat [encode a, encode b, encode c]+    seed =+      fromRight+        (error "Could not decode mnemonic seed")+        (mnemonicToSeed "" =<< toMnemonic bs)+    l = BS.length seed++mnemonicToSeed256 :: (Word64, Word64, Word64, Word64) -> Bool+mnemonicToSeed256 (a, b, c, d) = l == 64+  where+    bs = BS.concat [encode a, encode b, encode c, encode d]+    seed =+      fromRight+        (error "Could not decode mnemonic seed")+        (mnemonicToSeed "" =<< toMnemonic bs)+    l = BS.length seed++mnemonicToSeed512 ::+  ((Word64, Word64, Word64, Word64), (Word64, Word64, Word64, Word64)) -> Bool+mnemonicToSeed512 ((a, b, c, d), (e, f, g, h)) = l == 64+  where+    bs =+      BS.concat+        [ encode a,+          encode b,+          encode c,+          encode d,+          encode e,+          encode f,+          encode g,+          encode h+        ]+    seed =+      fromRight+        (error "Could not decode mnemonic seed")+        (mnemonicToSeed "" =<< toMnemonic bs)+    l = BS.length seed++mnemonicToSeedVar :: [Word32] -> Property+mnemonicToSeedVar ls = not (null ls) && length ls <= 16 ==> l == 64+  where+    bs = binWordsToBS ls+    seed =+      fromRight+        (error "Could not decode mnemonic seed")+        (mnemonicToSeed "" =<< toMnemonic bs)+    l = BS.length seed++{- Get bits from ByteString -}++data ByteCountGen = ByteCountGen BS.ByteString Int deriving (Show)++instance Arbitrary ByteCountGen where+  arbitrary = do+    bs <- arbitraryBS+    i <- choose (0, BS.length bs * 8)+    return $ ByteCountGen bs i++getBitsByteCount :: ByteCountGen -> Bool+getBitsByteCount (ByteCountGen bs i) = BS.length bits == l+  where+    (q, r) = i `quotRem` 8+    bits = getBits i bs+    l = if r == 0 then q else q + 1++getBitsEndBits :: ByteCountGen -> Bool+getBitsEndBits (ByteCountGen bs i) =+  (r == 0) || (BS.last bits .&. (0xff `shiftR` r) == 0x00)+  where+    r = i `mod` 8+    bits = getBits i bs
+ test/Haskoin/Crypto/KeysSpec.hs view
@@ -0,0 +1,276 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}++module Haskoin.Crypto.KeysSpec (spec) where++import Control.Lens+import Control.Monad+import Data.Aeson as A+import Data.Aeson.Lens+import Data.ByteString qualified as B+import Data.ByteString.Char8 qualified as C+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Default (def)+import Data.Maybe+import Data.Serialize qualified as S+import Data.String (fromString)+import Data.String.Conversions (cs)+import Data.Text (Text)+import Haskoin.Address+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Script+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.HUnit+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck++identityTests :: Ctx -> IdentityTests+identityTests ctx =+  def+    { readTests =+        [ ReadBox (arbitrary :: Gen SecKey),+          ReadBox arbitraryPrivateKey,+          ReadBox (arbitraryPublicKey ctx)+        ],+      marshalJsonTests =+        [ MarshalJsonBox $ (,) <$> arbitraryNetwork <*> arbitraryPrivateKey,+          MarshalJsonBox ((,) ctx <$> arbitraryPublicKey ctx)+        ],+      serialTests =+        [ SerialBox arbitraryPrivateKey+        ],+      marshalTests =+        [ MarshalBox ((,) ctx <$> arbitraryPublicKey ctx)+        ]+    }++spec :: Spec+spec = prepareContext $ \ctx -> do+  describe "Key pair property checks" $ do+    testIdentity $ identityTests ctx+    prop "Public key is canonical" $+      forAll (arbitraryKeyPair ctx) (isCanonicalPubKey ctx . snd)+    prop "Key pair key show . read identity" $+      forAll (arbitraryKeyPair ctx) $ \x ->+        (read . show) x == x+    prop "Public key binary serialization" $ do+      forAll (arbitraryKeyPair ctx) $ \(sec, pub) ->+        (unmarshal ctx . marshal ctx) pub == Right pub+    prop "fromWif . toWif identity" $+      forAll arbitraryNetwork $ \net ->+        forAll (arbitraryKeyPair ctx) $ \(key, _) ->+          (fromWif net . toWif net) key == Just key+    prop "WIF binary identity" $+      forAll arbitraryNetwork $ \net ->+        forAll (arbitraryKeyPair ctx) $ \(key, _) ->+          (runGetS deserialize . runPutS . serialize) key == Right key+  describe "Bitcoin core vectors /src/test/key_tests.cpp" $ do+    it "Passes WIF decoding tests" testPrivkey+    it "Passes SecKey compression tests" testPrvKeyCompressed+    it "Passes PubKey compression tests" $ testKeyCompressed ctx+    it "Passes address matching tests" $ testMatchingAddress ctx+    it "Passes signature verification" $ testSigs ctx+    it "Passes deterministic signing tests" $ testDetSigning ctx+  describe "MiniKey vectors" $+    it "Passes MiniKey decoding tests" testMiniKey+  describe "key_io_valid.json vectors" $ do+    vectors <- runIO (readTestFile "key_io_valid.json" :: IO [(Text, Text, A.Value)])+    it "Passes the key_io_valid.json vectors" $+      mapM_ (testKeyIOValidVector ctx) vectors+  describe "key_io_invalid.json vectors" $ do+    vectors <- runIO (readTestFile "key_io_invalid.json" :: IO [[Text]])+    it "Passes the key_io_invalid.json vectors" $+      mapM_ (testKeyIOInvalidVector ctx) vectors++-- github.com/bitcoin/bitcoin/blob/master/src/script.cpp+-- from function IsCanonicalPubKey+isCanonicalPubKey :: Ctx -> PublicKey -> Bool+isCanonicalPubKey ctx p =+  not $+    -- Non-canonical public key: too short+    (B.length bs < 33)+      ||+      -- Non-canonical public key: invalid length for uncompressed key+      (B.index bs 0 == 4 && B.length bs /= 65)+      ||+      -- Non-canonical public key: invalid length for compressed key+      (B.index bs 0 `elem` [2, 3] && B.length bs /= 33)+      ||+      -- Non-canonical public key: compressed nor uncompressed+      (B.index bs 0 `notElem` [2, 3, 4])+  where+    bs = marshal ctx p++testMiniKey :: Assertion+testMiniKey =+  assertEqual "fromMiniKey" (Just res) (go "S6c56bnXQiBjk9mqSYE7ykVQ7NzrRy")+  where+    go = fmap (encodeHex . (.key.get)) . fromMiniKey+    res = "4c7a9640c72dc2099f23715d0c8a0d8a35f8906e3cab61dd3f78b67bf887c9ab"++-- Test vectors from:+-- https://github.com/bitcoin/bitcoin/blob/master/src/test/key_io_tests.cpp++testKeyIOValidVector :: Ctx -> (Text, Text, A.Value) -> Assertion+testKeyIOValidVector ctx (a, payload, obj)+  | disabled = return () -- There are invalid version 1 bech32 addresses+  | isPrv = do+      -- Test from WIF to SecKey+      let isComp = obj ^?! key "isCompressed" . _Bool+          prvKeyM = fromWif net a+          prvKeyHexM = encodeHex . (.key.get) <$> prvKeyM+      assertBool "Valid PrvKey" $ isJust prvKeyM+      assertEqual "Valid compression" (Just isComp) ((.compress) <$> prvKeyM)+      assertEqual "WIF matches payload" (Just payload) prvKeyHexM+      let prvAsPubM :: Maybe ScriptOutput+          prvAsPubM = (eitherToMaybe . unmarshal ctx <=< decodeHex) a+      assertBool "PrvKey is invalid ScriptOutput" $ isNothing prvAsPubM+      -- Test from SecKey to WIF+      let secM = secKey =<< decodeHex payload+          wifM = toWif net . wrapSecKey isComp <$> secM+      assertEqual "Payload matches WIF" (Just a) wifM+  | otherwise = do+      -- Test Addr to Script+      let addrM = textToAddr net a+          scriptM = encodeHex . marshal ctx . addressToOutput <$> addrM+      assertBool ("Valid Address " <> cs a) $ isJust addrM+      assertEqual "Address matches payload" (Just payload) scriptM+      let pubAsWifM = fromWif net a+          pubAsSecM = secKey =<< decodeHex a+      assertBool "Address is invalid Wif" $ isNothing pubAsWifM+      assertBool "Address is invalid PrvKey" $ isNothing pubAsSecM+      -- Test Script to Addr+      let outM = eitherToMaybe . unmarshal ctx =<< decodeHex payload+          resM = addrToText net =<< outputAddress ctx =<< outM+      assertEqual "Payload matches address" (Just a) resM+  where+    isPrv = obj ^?! key "isPrivkey" . _Bool+    disabled = fromMaybe False $ obj ^? key "disabled" . _Bool+    chain = obj ^?! key "chain" . _String+    net =+      case chain of+        "main" -> btc+        "test" -> btcTest+        "regtest" -> btcRegTest+        _ -> error "Invalid chain key in key_io_valid.json"++testKeyIOInvalidVector :: Ctx -> [Text] -> Assertion+testKeyIOInvalidVector ctx [a] = do+  let wifMs = (`fromWif` a) <$> allNets+      secKeyM = (secKey <=< decodeHex) a+      scriptM :: Maybe ScriptOutput+      scriptM = (eitherToMaybe . unmarshal ctx <=< decodeHex) a+  assertBool "Payload is invalid WIF" $ all isNothing wifMs+  assertBool "Payload is invalid SecKey" $ isNothing secKeyM+  assertBool "Payload is invalid Script" $ isNothing scriptM+testKeyIOInvalidVector _ _ = assertFailure "Invalid test vector"++-- Test vectors from:+-- https://github.com/bitcoin/bitcoin/blob/master/src/test/key_tests.cpp++testPrivkey :: Assertion+testPrivkey = do+  assertBool "Key 1" $ isJust $ fromWif btc strSecret1+  assertBool "Key 2" $ isJust $ fromWif btc strSecret2+  assertBool "Key 1C" $ isJust $ fromWif btc strSecret1C+  assertBool "Key 2C" $ isJust $ fromWif btc strSecret2C+  assertBool "Bad key" $ isNothing $ fromWif btc strAddressBad++testPrvKeyCompressed :: Assertion+testPrvKeyCompressed = do+  assertBool "Key 1" $ not sec1.compress+  assertBool "Key 2" $ not sec2.compress+  assertBool "Key 1C" sec1C.compress+  assertBool "Key 2C" sec2C.compress++testKeyCompressed :: Ctx -> Assertion+testKeyCompressed ctx = do+  assertBool "Key 1" $ not (pub1 ctx).compress+  assertBool "Key 2" $ not (pub2 ctx).compress+  assertBool "Key 1C" (pub1C ctx).compress+  assertBool "Key 2C" (pub2C ctx).compress++testMatchingAddress :: Ctx -> Assertion+testMatchingAddress ctx = do+  assertEqual "Key 1" (Just addr1) $ addrToText btc (pubKeyAddr ctx (pub1 ctx))+  assertEqual "Key 2" (Just addr2) $ addrToText btc (pubKeyAddr ctx (pub2 ctx))+  assertEqual "Key 1C" (Just addr1C) $ addrToText btc (pubKeyAddr ctx (pub1C ctx))+  assertEqual "Key 2C" (Just addr2C) $ addrToText btc (pubKeyAddr ctx (pub2C ctx))++testSigs :: Ctx -> Assertion+testSigs ctx = forM_ sigMsg $ testSignature ctx . doubleSHA256++sigMsg :: [B.ByteString]+sigMsg =+  [ mconcat ["Very secret message ", C.pack (show (i :: Int)), ": 11"]+    | i <- [0 .. 15]+  ]++testSignature :: Ctx -> Hash256 -> Assertion+testSignature ctx h = do+  let sign1 = signHash ctx sec1.key h+      sign2 = signHash ctx sec2.key h+      sign1C = signHash ctx sec1C.key h+      sign2C = signHash ctx sec2C.key h+  assertBool "Key 1, Sign1" $ verifyHashSig ctx h sign1 (pub1 ctx).point+  assertBool "Key 1, Sign2" $ not $ verifyHashSig ctx h sign2 (pub1 ctx).point+  assertBool "Key 1, Sign1C" $ verifyHashSig ctx h sign1C (pub1 ctx).point+  assertBool "Key 1, Sign2C" $ not $ verifyHashSig ctx h sign2C (pub1 ctx).point+  assertBool "Key 2, Sign1" $ not $ verifyHashSig ctx h sign1 (pub2 ctx).point+  assertBool "Key 2, Sign2" $ verifyHashSig ctx h sign2 (pub2 ctx).point+  assertBool "Key 2, Sign1C" $ not $ verifyHashSig ctx h sign1C (pub2 ctx).point+  assertBool "Key 2, Sign2C" $ verifyHashSig ctx h sign2C (pub2 ctx).point+  assertBool "Key 1C, Sign1" $ verifyHashSig ctx h sign1 (pub1C ctx).point+  assertBool "Key 1C, Sign2" $ not $ verifyHashSig ctx h sign2 (pub1C ctx).point+  assertBool "Key 1C, Sign1C" $ verifyHashSig ctx h sign1C (pub1C ctx).point+  assertBool "Key 1C, Sign2C" $ not $ verifyHashSig ctx h sign2C (pub1C ctx).point+  assertBool "Key 2C, Sign1" $ not $ verifyHashSig ctx h sign1 (pub2C ctx).point+  assertBool "Key 2C, Sign2" $ verifyHashSig ctx h sign2 (pub2C ctx).point+  assertBool "Key 2C, Sign1C" $ not $ verifyHashSig ctx h sign1C (pub2C ctx).point+  assertBool "Key 2C, Sign2C" $ verifyHashSig ctx h sign2C (pub2C ctx).point++testDetSigning :: Ctx -> Assertion+testDetSigning ctx = do+  let m = doubleSHA256 ("Very deterministic message" :: B.ByteString)+  assertEqual+    "Det sig 1"+    (signHash ctx sec1.key m)+    (signHash ctx sec1C.key m)+  assertEqual+    "Det sig 2"+    (signHash ctx sec2.key m)+    (signHash ctx sec2C.key m)++strSecret1, strSecret2, strSecret1C, strSecret2C :: Text+strSecret1 = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"+strSecret2 = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"+strSecret1C = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"+strSecret2C = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"++sec1, sec2, sec1C, sec2C :: PrivateKey+sec1 = fromJust $ fromWif btc strSecret1+sec2 = fromJust $ fromWif btc strSecret2+sec1C = fromJust $ fromWif btc strSecret1C+sec2C = fromJust $ fromWif btc strSecret2C++addr1, addr2, addr1C, addr2C :: Text+addr1 = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ"+addr2 = "1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ"+addr1C = "1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs"+addr2C = "1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs"++strAddressBad :: Text+strAddressBad = "1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF"++pub1, pub2, pub1C, pub2C :: Ctx -> PublicKey+pub1 ctx = derivePublicKey ctx sec1+pub2 ctx = derivePublicKey ctx sec2+pub1C ctx = derivePublicKey ctx sec1C+pub2C ctx = derivePublicKey ctx sec2C
+ test/Haskoin/Crypto/SignatureSpec.hs view
@@ -0,0 +1,449 @@+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}++module Haskoin.Crypto.SignatureSpec (spec) where++import Control.Monad+import Data.Bits (testBit)+import Data.ByteString (ByteString)+import Data.ByteString qualified as BS+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Maybe+import Data.Serialize as S+import Data.String.Conversions (cs)+import Data.Text (Text)+import Haskoin.Address+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Script+import Haskoin.Transaction+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.HUnit+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck++spec :: Spec+spec = prepareContext $ \ctx -> do+  describe "Signature property checks" $ do+    prop "verifies signature" $+      forAll (arbitrarySignature ctx) $ \(m, key', sig) ->+        verifyHashSig ctx m sig (derivePubKey ctx key')+    prop "s component less than half order" $+      forAll (arbitrarySignature ctx) $+        isCanonicalHalfOrder ctx . lst3+    prop "encoded signature is canonical" $+      forAll (arbitrarySignature ctx) $+        testIsCanonical ctx . lst3+    prop "decodeStrictSig . exportSig identity" $+      forAll (arbitrarySignature ctx) $+        (\s -> decodeStrictSig ctx (exportSig ctx s) == Just s) . lst3+    prop "importSig . exportSig identity" $+      forAll (arbitrarySignature ctx) $+        (\s -> importSig ctx (exportSig ctx s) == Just s) . lst3+    prop "signature JSON identity" $+      forAll (arbitrarySignature ctx) $+        (\s -> (unmarshalJSON ctx . marshalJSON ctx) s == Just s) . lst3+    prop "getSig . putSig identity" $+      forAll (arbitrarySignature ctx) $+        (\s -> unmarshal ctx (marshal ctx s) == Right s) . lst3+  describe "Signature vectors" $ do+    it "passes RFC6979 test vectors" $+      checkDistSig ctx $ \file1 file2 -> do+        vectors <- readTestFile file1 :: IO [(Text, Text, Text)]+        vectorsDER <- readTestFile file2 :: IO [(Text, Text, Text)]+        mapM_ (testRFC6979Vector ctx . toVector) vectors+        mapM_ (testRFC6979DERVector ctx . toVector) vectorsDER+  describe "BIP143 signature vectors" $ do+    it "agrees with BIP143 p2wpkh example" $+      testBip143p2wpkh ctx+    it "agrees with BIP143 p2sh-p2wpkh example" $+      testBip143p2shp2wpkh ctx+    it "builds a p2wsh multisig transaction" $+      testP2WSHMulsig ctx+    it "agrees with BIP143 p2sh-p2wsh multisig example" $+      testBip143p2shp2wpkhMulsig ctx++-- github.com/bitcoin/bitcoin/blob/master/src/script.cpp+-- from function IsCanonicalSignature+testIsCanonical :: Ctx -> Sig -> Bool+testIsCanonical ctx sig =+  not $+    -- Non-canonical signature: too short+    (len < 8)+      ||+      -- Non-canonical signature: too long+      (len > 72)+      ||+      -- Non-canonical signature: wrong type+      (BS.index s 0 /= 0x30)+      ||+      -- Non-canonical signature: wrong length marker+      (BS.index s 1 /= len - 2)+      ||+      -- Non-canonical signature: S length misplaced+      (5 + rlen >= len)+      ||+      -- Non-canonical signature: R+S length mismatch+      (rlen + slen + 6 /= len)+      ||+      -- Non-canonical signature: R value type mismatch+      (BS.index s 2 /= 0x02)+      ||+      -- Non-canonical signature: R length is zero+      (rlen == 0)+      ||+      -- Non-canonical signature: R value negative+      testBit (BS.index s 4) 7+      ||+      -- Non-canonical signature: R value excessively padded+      ( rlen > 1+          && BS.index s 4 == 0+          && not (testBit (BS.index s 5) 7)+      )+      ||+      -- Non-canonical signature: S value type mismatch+      (BS.index s (fromIntegral rlen + 4) /= 0x02)+      ||+      -- Non-canonical signature: S length is zero+      (slen == 0)+      ||+      -- Non-canonical signature: S value negative+      testBit (BS.index s (fromIntegral rlen + 6)) 7+      ||+      -- Non-canonical signature: S value excessively padded+      ( slen > 1+          && BS.index s (fromIntegral rlen + 6) == 0+          && not (testBit (BS.index s (fromIntegral rlen + 7)) 7)+      )+  where+    s = exportSig ctx sig+    len = fromIntegral $ BS.length s+    rlen = BS.index s 3+    slen = BS.index s (fromIntegral rlen + 5)++-- RFC6979 note: Different libraries of libsecp256k1 use different constants+-- to produce a nonce. Thus, their deterministric signatures will be different.+-- We still want to test against fixed signatures so we need a way to switch+-- between implementations. We check the output of signMsg 1 0++data ValidImpl+  = ImplCore+  | ImplCash++implSig :: Ctx -> Text+implSig ctx =+  encodeHex $+    exportSig ctx $+      signMsg+        ctx+        "0000000000000000000000000000000000000000000000000000000000000001"+        "0000000000000000000000000000000000000000000000000000000000000000"++-- We have test vectors for these cases+validImplMap :: Map Text ValidImpl+validImplMap =+  Map.fromList+    [ ( "3045022100a0b37f8fba683cc68f6574cd43b39f0343a50008bf6ccea9d13231\+        \d9e7e2e1e4022011edc8d307254296264aebfc3dc76cd8b668373a072fd64665\+        \b50000e9fcce52",+        ImplCore+      ),+      ( "304402200581361d23e645be9e3efe63a9a2ac2e8dd0c70ba3ac8554c9befe06\+        \0ad0b36202207d8172f1e259395834793d81b17e986f1e6131e4734969d2f4ae\+        \3a9c8bc42965",+        ImplCash+      )+    ]++getImpl :: Ctx -> Maybe ValidImpl+getImpl ctx = implSig ctx `Map.lookup` validImplMap++rfc6979files :: ValidImpl -> (FilePath, FilePath)+rfc6979files ImplCore = ("rfc6979core.json", "rfc6979DERcore.json")+rfc6979files ImplCash = ("rfc6979cash.json", "rfc6979DERcash.json")++checkDistSig :: Ctx -> (FilePath -> FilePath -> Assertion) -> Assertion+checkDistSig ctx go =+  case rfc6979files <$> getImpl ctx of+    Just (file1, file2) -> go file1 file2+    _ -> assertFailure "invalid RFC6979 signature"++{- Trezor RFC 6979 Test Vectors -}+-- github.com/trezor/python-ecdsa/blob/master/ecdsa/test_pyecdsa.py++toVector :: (Text, Text, Text) -> (SecKey, ByteString, Text)+toVector (prv, m, res) = (fromJust $ (secKey <=< decodeHex) prv, cs m, res)++testRFC6979Vector :: Ctx -> (SecKey, ByteString, Text) -> Assertion+testRFC6979Vector ctx (prv, m, res) = do+  assertEqual "RFC 6979 Vector" res (encodeHex (exportCompactSig ctx s).get)+  assertBool "Signature is valid" $ verifyHashSig ctx h s (derivePubKey ctx prv)+  assertBool "Signature is canonical" $ testIsCanonical ctx s+  assertBool "Signature is normalized" $ isCanonicalHalfOrder ctx s+  where+    h = sha256 m+    s = signHash ctx prv h++-- Test vectors from:+-- https://crypto.stackexchange.com/questions/20838/request-for-data-to-test-deterministic-ecdsa-signature-algorithm-for-secp256k1++testRFC6979DERVector :: Ctx -> (SecKey, ByteString, Text) -> Assertion+testRFC6979DERVector ctx (prv, m, res) = do+  assertEqual "RFC 6979 DER Vector" res (encodeHex $ exportSig ctx s)+  assertBool "DER Signature is valid" $ verifyHashSig ctx h s (derivePubKey ctx prv)+  assertBool "DER Signature is canonical" $ testIsCanonical ctx s+  assertBool "DER Signature is normalized" $ isCanonicalHalfOrder ctx s+  where+    h = sha256 m+    s = signHash ctx prv h++-- Reproduce the P2WPKH example from BIP 143+testBip143p2wpkh :: Ctx -> Assertion+testBip143p2wpkh ctx =+  case getImpl ctx of+    Just ImplCore ->+      assertEqual "BIP143 Core p2wpkh" (Right signedTxCore) generatedSignedTx+    Just ImplCash ->+      assertEqual "BIP143 ABC p2wpkh" (Right signedTxCash) generatedSignedTx+    Nothing -> assertFailure "Invalid secp256k1 library"+  where+    signedTxCore =+      "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433\+      \541db4e4ad969f00000000494830450221008b9d1dc26ba6a9cb62127b02742f\+      \a9d754cd3bebf337f7a55d114c8e5cdd30be022040529b194ba3f9281a99f2b1\+      \c0a19c0489bc22ede944ccf4ecbab4cc618ef3ed01eeffffffef51e1b804cc89\+      \d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffff\+      \ffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac\+      \7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0\+      \167faa815988ac000247304402203609e17b84f6a7d30c80bfa610b5b4542f32\+      \a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f33\+      \58f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e\+      \7acafcdb3566bb0ad253f62fc70f07aeee635711000000"+    signedTxCash =+      "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433\+      \541db4e4ad969f000000004847304402200fbc9dad97500334e47c2dca50096a\+      \2117c01952c2870102e320823d21c36229022007cb36c2b141d11c08ef81d948\+      \f148332fc09fe8f6d226aaaf8ba6ae0d8a66ba01eeffffffef51e1b804cc89d1\+      \82d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffff\+      \ff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a\+      \6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f016\+      \7faa815988ac0002473044022011cb891cee521eb1fc7aef681655a881288553\+      \fc024cff9cee5007bae5e6b8c602200b89d60ee2f98aa9a645dad59cd680b4b6\+      \25f343efcd3e7fb70852100ef601890121025476c2e83188368da1ff3e292e7a\+      \cafcdb3566bb0ad253f62fc70f07aeee635711000000"+    unsignedTx =+      "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541d\+      \b4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b\+      \1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb20600000000\+      \1976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d0000\+      \00001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000"+    Just key0 =+      secHexKey+        "bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866"+    pubKey0 = toPubKey ctx key0+    Just key1 =+      secHexKey+        "619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9"+    [op0, op1] = (.outpoint) <$> unsignedTx.inputs+    sigIn0 = SigInput (PayPK pubKey0) 625000000 op0 sigHashAll Nothing+    WitnessPubKeyAddress h = pubKeyWitnessAddr ctx $ toPubKey ctx key1+    sigIn1 = SigInput (PayWitnessPKHash h) 600000000 op1 sigHashAll Nothing+    generatedSignedTx = signTx btc ctx unsignedTx [sigIn0, sigIn1] [key0, key1]++-- Reproduce the P2SH-P2WPKH example from BIP 143+testBip143p2shp2wpkh :: Ctx -> Assertion+testBip143p2shp2wpkh ctx =+  case getImpl ctx of+    Just ImplCore ->+      assertEqual "BIP143 Core p2sh-p2wpkh" (Right signedTxCore) generatedSignedTx+    Just ImplCash ->+      assertEqual "BIP143 Cash p2sh-p2wpkh" (Right signedTxCash) generatedSignedTx+    Nothing -> assertFailure "Invalid secp256k1 library"+  where+    signedTxCore =+      "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092\+      \ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009b\+      \df0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bb\+      \c043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fe\+      \a7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a1\+      \0d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d\+      \877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c7\+      \4d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000"+    signedTxCash =+      "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092\+      \ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009b\+      \df0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bb\+      \c043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fe\+      \a7ad0402e8bd8ad6d77c88ac024730440220091c78fd1e21535f6ddc45515e4c\+      \afca15cdf344765d72c1529fb82d3ada2d1802204a980d5e37d0b04f5e1185a0\+      \f97295c383764e9a4b08d8bd1161b33c6719139a012103ad1d8e89212f0b92c7\+      \4d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000"+    unsignedTx =+      "0100000001db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d\+      \3ceb1a54770100000000feffffff02b8b4eb0b000000001976a914a457b684d7\+      \f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b\+      \1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac92040000"+    Just key0 =+      secHexKey+        "eb696a065ef48a2192da5b28b694f87544b30fae8327c4510137a922f32c6dcf"+    op0 = (head unsignedTx.inputs).outpoint+    WitnessPubKeyAddress h = pubKeyWitnessAddr ctx $ toPubKey ctx key0+    sigIn0 = SigInput (PayWitnessPKHash h) 1000000000 op0 sigHashAll Nothing+    generatedSignedTx = signNestedWitnessTx btc ctx unsignedTx [sigIn0] [key0]++-- P2WSH multisig example (tested against bitcoin-core 0.19.0.1)+testP2WSHMulsig :: Ctx -> Assertion+testP2WSHMulsig ctx =+  case getImpl ctx of+    Just ImplCore ->+      assertEqual "Core p2wsh multisig" (Right signedTxCore) generatedSignedTx+    Just ImplCash ->+      assertEqual "Cash p2wsh multisig" (Right signedTxCash) generatedSignedTx+    Nothing -> assertFailure "Invalid secp256k1 library"+  where+    signedTxCore =+      "01000000000101d2e34df5d7ee565208eddd231548916b9b0e99f4f5071f8961\+      \34a448c5fb07bf0100000000ffffffff01f0b9f505000000001976a9143d5a35\+      \2cab583b12fbcb26d1269b4a2c951a33ad88ac0400483045022100fad4fedd2b\+      \b4c439c64637eb8e9150d9020a7212808b8dc0578d5ff5b4ad65fe0220714640\+      \f261b37eb3106310bf853f4b706e51436fb6b64c2ab00768814eb55b98014730\+      \44022100baff4e4ceea4022b9725a2e6f6d77997a554f858165b91ac8c16c983\+      \3008bee9021f5f70ebc3f8580dc0a5e96451e3697bdf1f1f5883944f0f33ab0c\+      \fb272354040169522102ba46d3bb8db74c77c6cf082db57fc0548058fcdea811\+      \549e186526e3d10caf6721038ac8aef2dd9cea5e7d66e2f6e23f177a6c21f69e\+      \a311fa0c85d81badb6b37ceb2103d96d2bfbbc040faaf93491d69e2bfe9695e2\+      \d8e007a7f26db96c2ee42db15dc953ae00000000"+    signedTxCash =+      "01000000000101d2e34df5d7ee565208eddd231548916b9b0e99f4f5071f8961\+      \34a448c5fb07bf0100000000ffffffff01f0b9f505000000001976a9143d5a35\+      \2cab583b12fbcb26d1269b4a2c951a33ad88ac0400483045022100b79bf3714a\+      \50f8f0e2f946034361ba4f6567b796d55910d89e98720d2e99f98c0220134879\+      \518002df23e80a058475fa8b10bc4182bedfecd5f85e446a00f211ea53014830\+      \45022100ce3c77480d664430a7544c1a962d1ae31151109a528a37e5bccc92ba\+      \2e460ad10220317bc9a71d0c3471058d16d4c3b1ea99616208db6b9b9040fb81\+      \0a7fa27f72b40169522102ba46d3bb8db74c77c6cf082db57fc0548058fcdea8\+      \11549e186526e3d10caf6721038ac8aef2dd9cea5e7d66e2f6e23f177a6c21f6\+      \9ea311fa0c85d81badb6b37ceb2103d96d2bfbbc040faaf93491d69e2bfe9695\+      \e2d8e007a7f26db96c2ee42db15dc953ae00000000"+    unsignedTx =+      "0100000001d2e34df5d7ee565208eddd231548916b9b0e99f4f5071f896134a4\+      \48c5fb07bf0100000000ffffffff01f0b9f505000000001976a9143d5a352cab\+      \583b12fbcb26d1269b4a2c951a33ad88ac00000000"+    op0 = (head unsignedTx.inputs).outpoint+    Just keys =+      traverse+        secHexKey+        [ "3030303030303030303030303030303030303030303030303030303030303031",+          "3030303030303030303030303030303030303030303030303030303030303032",+          "3030303030303030303030303030303030303030303030303030303030303033"+        ]+    rdm = PayMulSig (toPubKey ctx <$> keys) 2+    sigIn =+      SigInput+        (toP2WSH $ encodeOutput ctx rdm)+        100000000+        op0+        sigHashAll+        (Just rdm)+    generatedSignedTx = signTx btc ctx unsignedTx [sigIn] (take 2 keys)++-- Reproduce the P2SH-P2WSH multisig example from BIP 143+testBip143p2shp2wpkhMulsig :: Ctx -> Assertion+testBip143p2shp2wpkhMulsig ctx =+  case getImpl ctx of+    Just ImplCore ->+      assertEqual+        "BIP143 Core p2sh-p2wsh multisig"+        (Right signedTxCore)+        generatedSignedTx+    Just ImplCash ->+      assertEqual+        "BIP143 Core p2sh-p2wsh multisig"+        (Right signedTxCash)+        generatedSignedTx+    Nothing -> assertFailure "Invalid secp256k1 library"+  where+    signedTxCore =+      "0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa10\+      \6c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b8\+      \9af4b1f14a1bb8a09062c35f0dcb54ffffffff0200e9a435000000001976a914\+      \389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976\+      \a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac080047304402206a\+      \c44d672dac41f9b00e28f4df20c52eeb087207e8d758d76d92c6fab3b73e2b02\+      \20367750dbbe19290069cba53d096f44530e4f98acaa594810388cf7409a1870\+      \ce01473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1\+      \e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa26955\+      \78cc6432cdabce271502473044022059ebf56d98010a932cf8ecfec54c48e613\+      \9ed6adb0728c09cbe1e4fa0915302e022007cd986c8fa870ff5d2b3a89139c9f\+      \e7e499259875357e20fcbb15571c76795403483045022100fbefd94bd0a488d5\+      \0b79102b5dad4ab6ced30c4069f1eaa69a4b5a763414067e02203156c6a5c9cf\+      \88f91265f5a942e96213afae16d83321c8b31bb342142a14d163814830450221\+      \00a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a4\+      \07022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e\+      \1a8a08824730440220525406a1482936d5a21888260dc165497a90a15669636d\+      \8edca6b9fe490d309c022032af0c646a34a44d1f4576bf6a4a74b67940f8faa8\+      \4c7df9abe12a01a11e2b4783cf56210307b8ae49ac90a048e9b53357a2354b33\+      \34e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c\+      \3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b97\+      \81957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a\+      \21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba0\+      \4d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b330\+      \2ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000"+    signedTxCash =+      "0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa10\+      \6c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b8\+      \9af4b1f14a1bb8a09062c35f0dcb54ffffffff0200e9a435000000001976a914\+      \389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976\+      \a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac0800483045022100\+      \b70b684ef0d17b51adf71c0dae932beca5d447dd5eec03394328436bdba836e7\+      \0220208ebfd7408d21e41da11d8287655528385429d3fe300bee241f10944339\+      \5b580147304402204b5f9bc06c8f0a252b9842ea44785853beb1638002cec5f2\+      \489d73e5f6f5109302204f3b132b32638835d4b1a651e7d18dc93c10192db553\+      \999932af6a8e3d8a153202483045022100e0ed8d3a245a138c751d74e1359aee\+      \6a52476ddf33a3a9a5f0c2ad30147319650220581318187061ad0f48fc4f5c85\+      \1822e554d59977005b8de4b78bf2ce2fe8399703483045022100a0a40abc581e\+      \4b725775a3aa93bf0f0fd9a02ad3aa0f93483214784a47ba5387022069151c30\+      \f85a7e20c8671107c5af884ee4c5a82bd06398327fa68a993f7cc64b81473044\+      \022016d828460f6fab3cf89ae4b87c8f02c11c798cf739967f3b7406e7367c29\+      \ae8b022079e82b822eb6c37a66efabc3f0b40a2b98c52f848d36463f6623cbdc\+      \fe675812824730440220225a14ba7434858dbb5e6e0a0969ddf3b5455edaabf9\+      \9f5773d1f59e7816b918022047ed1ab87840a74f7e9489f3af051e5fd26b790f\+      \b308c79f4b0ed73c0422795d83cf56210307b8ae49ac90a048e9b53357a2354b\+      \3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac\+      \5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b\+      \9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a\+      \9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94b\+      \a04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3\+      \302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000"+    unsignedTx =+      "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1c\+      \a29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd\+      \9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a3\+      \3f950689af511e6e84c138dbbd3c3ee41588ac00000000"+    op0 = (head unsignedTx.inputs).outpoint+    rawKeys =+      [ "730fff80e1413068a05b57d6a58261f07551163369787f349438ea38ca80fac6",+        "11fa3d25a17cbc22b29c44a484ba552b5a53149d106d3d853e22fdd05a2d8bb3",+        "77bf4141a87d55bdd7f3cd0bdccf6e9e642935fec45f2f30047be7b799120661",+        "14af36970f5025ea3e8b5542c0f8ebe7763e674838d08808896b63c3351ffe49",+        "fe9a95c19eef81dde2b95c1284ef39be497d128e2aa46916fb02d552485e0323",+        "428a7aee9f0c2af0cd19af3cf1c78149951ea528726989b2e83e4778d2c3f890"+      ]+    Just keys = traverse secHexKey rawKeys+    rdm = PayMulSig (toPubKey ctx <$> keys) 6+    sigIn sh = SigInput (toP2WSH $ encodeOutput ctx rdm) 987654321 op0 sh (Just rdm)+    sigHashesA = [sigHashAll, sigHashNone, sigHashSingle]+    sigHashesB = setAnyoneCanPay <$> sigHashesA+    sigIns = sigIn <$> (sigHashesA <> sigHashesB)+    generatedSignedTx = foldM addSig unsignedTx $ zip sigIns keys+    addSig tx (sigIn', key') = signNestedWitnessTx btc ctx tx [sigIn'] [key']++secHexKey :: Text -> Maybe SecKey+secHexKey = decodeHex >=> secKey++toPubKey :: Ctx -> SecKey -> PublicKey+toPubKey ctx = derivePublicKey ctx . wrapSecKey True
+ test/Haskoin/NetworkSpec.hs view
@@ -0,0 +1,190 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}++module Haskoin.NetworkSpec (spec) where++import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Default (def)+import Data.Maybe (fromJust)+import Data.Text (Text)+import Data.Word (Word32)+import Haskoin.Address+import Haskoin.Crypto+import Haskoin.Network+import Haskoin.Network.Constants+import Haskoin.Transaction+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.HUnit (Assertion, assertBool, assertEqual)+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck++identityTests :: IdentityTests+identityTests =+  def+    { serialTests =+        [ SerialBox arbitraryVarInt,+          SerialBox arbitraryVarString,+          SerialBox arbitraryNetworkAddress,+          SerialBox arbitraryInvType,+          SerialBox arbitraryInvVector,+          SerialBox arbitraryInv1,+          SerialBox arbitraryVersion,+          SerialBox arbitraryAddr1,+          SerialBox arbitraryAlert,+          SerialBox arbitraryReject,+          SerialBox arbitraryRejectCode,+          SerialBox arbitraryGetData,+          SerialBox arbitraryNotFound,+          SerialBox arbitraryPing,+          SerialBox arbitraryPong,+          SerialBox arbitraryMessageCommand,+          SerialBox arbitraryMessageHeader,+          SerialBox arbitraryBloomFlags,+          SerialBox arbitraryBloomFilter,+          SerialBox arbitraryFilterLoad,+          SerialBox arbitraryFilterAdd+        ]+    }++spec :: Spec+spec = prepareContext $ \ctx -> do+  testIdentity identityTests+  describe "Custom identity tests" $ do+    prop "Data.Serialize Encoding for type Message" $+      forAll arbitraryNetwork $ \net ->+        forAll (arbitraryMessage net ctx) $+          customCerealID (getMessage net) (putMessage net)+  describe "bloom filters" $ do+    it "bloom filter vector 1" bloomFilter1+    it "bloom filter vector 2" bloomFilter2+    it "bloom filter vector 3" $ bloomFilter3 ctx+  describe "relevant bloom filter update" $ do+    it "Relevant Update" $ relevantOutputUpdated ctx+    it "Irrelevant Update" $ irrelevantOutputNotUpdated ctx++bloomFilter :: Word32 -> Text -> Assertion+bloomFilter n x = do+  assertBool "Bloom filter doesn't contain vector 1" $ bloomContains f1 v1+  assertBool "Bloom filter contains something it should not" $+    not $+      bloomContains f1 v2+  assertBool "Bloom filter doesn't contain vector 3" $ bloomContains f3 v3+  assertBool "Bloom filter doesn't contain vector 4" $ bloomContains f4 v4+  assertBool "Bloom filter serialization is incorrect" $+    runPutS (serialize f4) == bs+  where+    f0 = bloomCreate 3 0.01 n BloomUpdateAll+    f1 = bloomInsert f0 v1+    f3 = bloomInsert f1 v3+    f4 = bloomInsert f3 v4+    v1 = fromJust $ decodeHex "99108ad8ed9bb6274d3980bab5a85c048f0950c8"+    v2 = fromJust $ decodeHex "19108ad8ed9bb6274d3980bab5a85c048f0950c8"+    v3 = fromJust $ decodeHex "b5a2c786d9ef4658287ced5914b37a1b4aa32eee"+    v4 = fromJust $ decodeHex "b9300670b4c5366e95b2699e8b18bc75e5f729c5"+    bs = fromJust $ decodeHex x++bloomFilter1 :: Assertion+bloomFilter1 = bloomFilter 0 "03614e9b050000000000000001"++bloomFilter2 :: Assertion+bloomFilter2 = bloomFilter 2147483649 "03ce4299050000000100008001"++bloomFilter3 :: Ctx -> Assertion+bloomFilter3 ctx =+  assertBool "Bloom filter serialization is incorrect" $+    runPutS (serialize f2) == bs+  where+    f0 = bloomCreate 2 0.001 0 BloomUpdateAll+    f1 = bloomInsert f0 $ marshal ctx p+    f2 = bloomInsert f1 $ runPutS $ serialize (pubKeyAddr ctx p).hash160+    k = fromJust $ fromWif btc "5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C"+    p = derivePublicKey ctx k+    bs = fromJust $ decodeHex "038fc16b080000000000000001"++relevantOutputUpdated :: Ctx -> Assertion+relevantOutputUpdated ctx =+  assertBool "Bloom filter output updated" $+    any (bloomContains bf2) spendTxInput+  where+    bf0 = bloomCreate 10 0.000001 0 BloomUpdateAll+    relevantOutputHash = fromJust $ decodeHex "03f47604ea2736334151081e13265b4fe38e6fa8"+    bf1 = bloomInsert bf0 relevantOutputHash+    bf2 = fromJust $ bloomRelevantUpdate ctx bf1 relevantTx+    spendTxInput = runPutS . serialize . (.outpoint) <$> spendRelevantTx.inputs++irrelevantOutputNotUpdated :: Ctx -> Assertion+irrelevantOutputNotUpdated ctx = assertEqual "Bloom filter not updated" Nothing bf2+  where+    bf0 = bloomCreate 10 0.000001 0 BloomUpdateAll+    relevantOutputHash = fromJust $ decodeHex "03f47604ea2736334151081e13265b4fe38e6fa8"+    bf1 = bloomInsert bf0 relevantOutputHash+    bf2 = bloomRelevantUpdate ctx bf1 unrelatedTx++-- Random transaction (57dc904f32ad4daab7b321dd469e8791ad09df784cdd273a73985150a4f225e9)+relevantTx :: Tx+relevantTx =+  Tx+    { version = 1,+      inputs =+        [ TxIn+            { outpoint = OutPoint "35fe9017b7e3af592920b56fa06ac02faf0c52cdb19dcb416129ac71c95d060e" 1,+              script = fromJust $ decodeHex "473044022032fc8eef299b7e94b9a986a6aa2dcb9733ab804bef80df995e443b9c1f8c604202203335df7a2e2b4789451cdb4b2b05a786a81c51519eb6a567fd6fe8cd7b2d33fe014104272502dc63a512dad1473cb82a71be9baf4f4303abd1ff6028fc8a78e1f3aec1218907119dec14f07354850758ff0948e88a904fa411c4df7d5444414ec64ad6",+              sequence = 4294967295+            }+        ],+      outputs =+        [ TxOut {value = 100000000, script = fromJust $ decodeHex "76a91403f47604ea2736334151081e13265b4fe38e6fa888ac"},+          TxOut {value = 107980000, script = fromJust $ decodeHex "76a91481cc186a2f4a69f633ed4bf10ef4a78be13effdd88ac"}+        ],+      witness = [],+      locktime = 0+    }++-- Transaction that spends above (fd6e3b693b844aa431fad46765c1aa019a6b13aebfa9dae916b3ffa43283a300)+spendRelevantTx :: Tx+spendRelevantTx =+  Tx+    { version = 1,+      inputs =+        [ TxIn+            { outpoint = OutPoint "57dc904f32ad4daab7b321dd469e8791ad09df784cdd273a73985150a4f225e9" 0,+              script = fromJust $ decodeHex "483045022100ecc334821e4e94cc2fdc841d5ad147d5bb942b993ba81460cc446e0410afa811022015fcbc542b734dbb61a05ec06012095096de5839c50808fe56f2b315e877c20d012103fb64e5792fa586172339b776b7017d3d529358cb73be6406a1fc994228d14f88",+              sequence = 4294967295+            },+          TxIn+            { outpoint = OutPoint "cfee6a8d6e68e8fd16df6fff010afffcd19d7e075aa7b707dd1bae6adc420042" 0,+              script = fromJust $ decodeHex "47304402200e6bb95fa606f254d17089d83c4ceeb19c5d1699b4faddcd4f1f1568286e6b650220087fb8439f31e1b30e47710d095422405f601d6151f2f93e125e1a08a6e29ad4012103b49252e8fc6d5b49c8d14ee71fab45591df4a126a6c453c724f3d356e38f0cee",+              sequence = 4294967295+            }+        ],+      outputs =+        [ TxOut {value = 3851100, script = fromJust $ decodeHex "76a914a297cae82a9a3b932bf023ae274fe2585295c9ca88ac"},+          TxOut {value = 111000000, script = fromJust $ decodeHex "76a9148f952c38600a61385974acc30a64f74407f9801488ac"}+        ],+      witness = [],+      locktime = 0+    }++-- This random transaction is unrelated to the others+unrelatedTx :: Tx+unrelatedTx =+  Tx+    { version = 1,+      inputs =+        [ TxIn+            { outpoint = OutPoint "3ec3a71431c68e5d978a5fb4a0a1081d8bee8384d8aa4c06b1fbaf9413e2214f" 20,+              script = fromJust $ decodeHex "483045022100ec9c202c9d3140b973aca9d7f21a82138aa4cfa43fddc5419098ac5e26a6f152022010848fd688f290ae010fb5cb493410caa03145fc12445900ec1ad2bde33aecd9012102c7445e72d723f99a0064526c28269d07f47c8fd81531a94a8d3bf5ebd5e23ef1",+              sequence = 4294967295+            }+        ],+      outputs =+        [ TxOut {value = 12600000, script = fromJust $ decodeHex "76a9148fef3b7051de8cc44e966159e7ea37f4520187e888ac"}+        ],+      witness = [],+      locktime = 0+    }
+ test/Haskoin/ScriptSpec.hs view
@@ -0,0 +1,472 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}++module Haskoin.ScriptSpec (spec) where++import Control.Monad+import Data.Aeson as A+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Default (def)+import Data.Either+import Data.List+import Data.Maybe+import Data.String+import Data.String.Conversions (cs)+import Data.Text (Text)+import Data.Word+import Haskoin.Address+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Script+import Haskoin.Transaction+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.HUnit as HUnit+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck+import Text.Read++identityTests :: Ctx -> IdentityTests+identityTests ctx =+  def+    { readTests =+        [ ReadBox arbitrarySigHash,+          ReadBox arbitrarySigHashFlag,+          ReadBox arbitraryScript,+          ReadBox arbitraryPushDataType,+          ReadBox arbitraryScriptOp,+          ReadBox ((`arbitraryScriptOutput` ctx) =<< arbitraryNetwork),+          ReadBox ((`arbitraryScriptInput` ctx) =<< arbitraryNetwork)+        ],+      jsonTests =+        [ JsonBox arbitraryScript,+          JsonBox arbitraryOutPoint,+          JsonBox arbitrarySigHash+        ],+      marshalJsonTests =+        [ MarshalJsonBox $ do+            n <- arbitraryNetwork+            (_, _, ts) <- arbitraryTxSignature n ctx+            return ((n, ctx), ts),+          MarshalJsonBox $ do+            n <- arbitraryNetwork+            o <- arbitraryScriptOutput n ctx+            return (ctx, o),+          MarshalJsonBox $ do+            n <- arbitraryNetwork+            (i, _) <- arbitrarySigInput n ctx+            return (ctx, i)+        ],+      serialTests =+        [ SerialBox arbitraryScriptOp,+          SerialBox arbitraryScript+        ],+      marshalTests =+        [ MarshalBox $ do+            n <- arbitraryNetwork+            (_, _, ts) <- arbitraryTxSignature n ctx+            return ((n, ctx), ts),+          MarshalBox $ do+            n <- arbitraryNetwork+            o <- arbitraryScriptOutput n ctx+            return (ctx, o),+          MarshalBox $ do+            n <- arbitraryNetwork+            i <- arbitraryScriptInput n ctx+            return ((n, ctx), i)+        ]+    }++spec :: Spec+spec = prepareContext $ \ctx -> do+  testIdentity $ identityTests ctx+  describe "btc scripts" $ props btc ctx+  describe "bch scripts" $ props bch ctx+  describe "multi signatures" $+    zipWithM_ (curry (mapMulSigVector ctx)) mulSigVectors [0 ..]+  describe "signature decoding" $+    zipWithM_ (curry (sigDecodeMap btc ctx)) scriptSigSignatures [0 ..]+  describe "SigHashFlag fromEnum/toEnum" $+    prop "fromEnum/toEnum" $+      forAll arbitrarySigHashFlag $+        \f -> toEnum (fromEnum f) `shouldBe` f+  describe "Script vectors" $+    it "Can encode script vectors" encodeScriptVector++props :: Network -> Ctx -> Spec+props net ctx = do+  standardSpec net ctx+  strictSigSpec net ctx+  scriptSpec net ctx+  txSigHashForkIdSpec net+  forkIdScriptSpec net ctx+  sigHashSpec net ctx+  txSigHashSpec net++standardSpec :: Network -> Ctx -> Spec+standardSpec net ctx = do+  prop "has intToScriptOp . scriptOpToInt identity" $+    forAll arbitraryIntScriptOp $ \i ->+      intToScriptOp <$> scriptOpToInt i `shouldBe` Right i+  prop "has decodeOutput . encodeOutput identity" $+    forAll (arbitraryScriptOutput net ctx) $ \so ->+      decodeOutput ctx (encodeOutput ctx so) `shouldBe` Right so+  prop "has decodeInput . encodeOutput identity" $+    forAll (arbitraryScriptInput net ctx) $ \si ->+      (decodeInput net ctx . encodeInput net ctx) si `shouldBe` Right si+  prop "can sort multisig scripts" $+    forAll (arbitraryMSOutput ctx) $ \out ->+      let keyList = map (marshal ctx) (sortMulSig ctx out).keys+          isSorted xs = xs == sort xs+       in keyList `shouldSatisfy` isSorted+  it "can decode inputs with empty signatures" $ do+    decodeInput net ctx (Script [OP_0])+      `shouldBe` Right (RegularInput (SpendPK TxSignatureEmpty))+    decodeInput net ctx (Script [opPushData ""])+      `shouldBe` Right (RegularInput (SpendPK TxSignatureEmpty))+    let Just sk = secKey (B.replicate 32 1)+        pk = derivePublicKey ctx (wrapSecKey True sk)+    decodeInput net ctx (Script [OP_0, opPushData $ marshal ctx pk])+      `shouldBe` Right (RegularInput (SpendPKHash TxSignatureEmpty pk))+    decodeInput net ctx (Script [OP_0, OP_0])+      `shouldBe` Right (RegularInput (SpendMulSig [TxSignatureEmpty]))+    decodeInput net ctx (Script [OP_0, OP_0, OP_0, OP_0])+      `shouldBe` Right (RegularInput (SpendMulSig $ replicate 3 TxSignatureEmpty))++scriptSpec :: Network -> Ctx -> Spec+scriptSpec net ctx =+  when (net.name == "btc") $+    it "can verify standard scripts from script_tests.json file" $ do+      xs <- readTestFile "script_tests.json" :: IO [A.Value]+      let vectorsA =+            mapMaybe (A.decode . A.encode) xs ::+              [(String, String, String, String, String)]+          vectorsB =+            mapMaybe (A.decode . A.encode) xs ::+              [([Word64], String, String, String, String, String)]+          vectors =+            map (\(a, b, c, d, e) -> ([0], a, b, c, d, e)) vectorsA+              <> vectorsB+      length vectors `shouldBe` 86+      forM_ vectors $ \([val], siStr, soStr, flags, res, desc) ->+        -- We can disable specific tests by adding a DISABLED flag in the data+        unless ("DISABLED" `isInfixOf` flags) $ do+          let _strict =+                any+                  (`isInfixOf` flags)+                  ["DERSIG", "STRICTENC", "NULLDUMMY"]+              scriptSig = parseScript siStr+              scriptPubKey = parseScript soStr+              out = unmarshal ctx scriptPubKey+              tx = spendTx scriptPubKey 0 scriptSig+              sat = val * 100000000+              ver o = verifyStdInput net ctx tx 0 o sat+              valid = either (const False) ver out+          assertBool desc $ if res == "OK" then valid else not valid++forkIdScriptSpec :: Network -> Ctx -> Spec+forkIdScriptSpec net ctx =+  when (isJust net.sigHashForkId) $+    it "can verify scripts from forkid_script_tests.json file" $ do+      xs <- readTestFile "forkid_script_tests.json" :: IO [A.Value]+      let vectors =+            mapMaybe (A.decode . A.encode) xs ::+              [ ( [Word64],+                  String,+                  String,+                  String,+                  String,+                  String+                )+              ]+      length vectors `shouldBe` 3+      forM_ vectors $ \([valBTC], siStr, soStr, _, res, _) -> do+        let val = valBTC * 100000000+            scriptSig = parseScript siStr+            scriptPubKey = parseScript soStr+            out = unmarshal ctx scriptPubKey+            tx = spendTx scriptPubKey val scriptSig+            ver o = verifyStdInput net ctx tx 0 o val+            valid = either (const False) ver out+        case res of+          "OK" -> valid `shouldBe` True+          _ -> valid `shouldBe` False++creditTx :: ByteString -> Word64 -> Tx+creditTx scriptPubKey val =+  Tx 1 [txI] [txO] [] 0+  where+    txO = TxOut {value = val, script = scriptPubKey}+    txI =+      TxIn+        { outpoint = nullOutPoint,+          script = runPutS $ serialize $ Script [OP_0, OP_0],+          sequence = maxBound+        }++spendTx :: ByteString -> Word64 -> ByteString -> Tx+spendTx scriptPubKey val scriptSig =+  Tx 1 [txI] [txO] [] 0+  where+    txO = TxOut {value = val, script = B.empty}+    txI =+      TxIn+        { outpoint = OutPoint (txHash $ creditTx scriptPubKey val) 0,+          script = scriptSig,+          sequence = maxBound+        }++parseScript :: String -> ByteString+parseScript str =+  B.concat $ fromMaybe err $ mapM f $ words str+  where+    f = decodeHex . cs . dropHex . replaceToken+    dropHex ('0' : 'x' : xs) = xs+    dropHex xs = xs+    err = error $ "Could not decode script: " <> str++replaceToken :: String -> String+replaceToken str = case readMaybe $ "OP_" <> str of+  Just opcode -> "0x" <> cs (encodeHex $ runPutS $ serialize (opcode :: ScriptOp))+  _ -> str++strictSigSpec :: Network -> Ctx -> Spec+strictSigSpec net ctx =+  when (net.name == "btc") $ do+    it "can decode strict signatures" $ do+      xs <- readTestFile "sig_strict.json"+      let vectors = mapMaybe decodeHex xs+      length vectors `shouldBe` 3+      forM_ vectors $ \sig ->+        let eitherSig :: Either String TxSignature+            eitherSig = decodeTxSig net ctx sig+         in eitherSig `shouldSatisfy` isRight+    it "can detect non-strict signatures" $ do+      xs <- readTestFile "sig_nonstrict.json"+      let vectors = mapMaybe decodeHex xs+      length vectors `shouldBe` 17+      forM_ vectors $ \sig ->+        let eitherSig = decodeTxSig net ctx sig+         in eitherSig `shouldSatisfy` isLeft++txSigHashSpec :: Network -> Spec+txSigHashSpec net =+  when (net.name == "btc") $+    it "can produce valid sighashes from sighash.json test vectors" $ do+      xs <- readTestFile "sighash.json" :: IO [A.Value]+      let vectors =+            mapMaybe (A.decode . A.encode) xs ::+              [ ( String,+                  String,+                  Int,+                  Integer,+                  String+                )+              ]+      length vectors `shouldBe` 500+      forM_ vectors $ \(txStr, scpStr, i, shI, resStr) -> do+        let tx = fromString txStr+            s =+              fromMaybe (error $ "Could not decode script: " <> cs scpStr) $+                eitherToMaybe . runGetS deserialize =<< decodeHex (cs scpStr)+            sh = fromIntegral shI+            res =+              eitherToMaybe . runGetS deserialize . B.reverse+                =<< decodeHex (cs resStr)+        Just (txSigHash net tx s 0 i sh) `shouldBe` res++txSigHashForkIdSpec :: Network -> Spec+txSigHashForkIdSpec net =+  when (net.name == "btc") $+    it "can produce valid sighashes from forkid_sighash.json test vectors" $ do+      xs <- readTestFile "forkid_sighash.json" :: IO [A.Value]+      let vectors =+            mapMaybe (A.decode . A.encode) xs ::+              [ ( String,+                  String,+                  Int,+                  Word64,+                  Integer,+                  String+                )+              ]+      length vectors `shouldBe` 13+      forM_ vectors $ \(txStr, scpStr, i, val, shI, resStr) -> do+        let tx = fromString txStr+            s =+              fromMaybe (error $ "Could not decode script: " <> cs scpStr) $+                eitherToMaybe . runGetS deserialize =<< decodeHex (cs scpStr)+            sh = fromIntegral shI+            res = eitherToMaybe . runGetS deserialize =<< decodeHex (cs resStr)+        Just (txSigHashForkId net tx s val i sh) `shouldBe` res++sigHashSpec :: Network -> Ctx -> Spec+sigHashSpec net ctx = do+  it "can correctly show" $ do+    show (0x00 :: SigHash) `shouldBe` "SigHash " <> show (0x00 :: Word32)+    show (0x01 :: SigHash) `shouldBe` "SigHash " <> show (0x01 :: Word32)+    show (0xff :: SigHash) `shouldBe` "SigHash " <> show (0xff :: Word32)+    show (0xabac3344 :: SigHash)+      `shouldBe` "SigHash "+        <> show (0xabac3344 :: Word32)+  it "can add a forkid" $ do+    0x00 `sigHashAddForkId` 0x00 `shouldBe` 0x00+    0xff `sigHashAddForkId` 0x00ffffff `shouldBe` 0xffffffff+    0xffff `sigHashAddForkId` 0x00aaaaaa `shouldBe` 0xaaaaaaff+    0xffff `sigHashAddForkId` 0xaaaaaaaa `shouldBe` 0xaaaaaaff+    0xffff `sigHashAddForkId` 0x00004444 `shouldBe` 0x004444ff+    0xff01 `sigHashAddForkId` 0x44440000 `shouldBe` 0x44000001+    0xff03 `sigHashAddForkId` 0x00550000 `shouldBe` 0x55000003+  it "can extract a forkid" $ do+    sigHashGetForkId 0x00000000 `shouldBe` 0x00000000+    sigHashGetForkId 0x80000000 `shouldBe` 0x00800000+    sigHashGetForkId 0xffffffff `shouldBe` 0x00ffffff+    sigHashGetForkId 0xabac3403 `shouldBe` 0x00abac34+  it "can build some vectors" $ do+    sigHashAll `shouldBe` 0x01+    sigHashNone `shouldBe` 0x02+    sigHashSingle `shouldBe` 0x03+    setForkIdFlag sigHashAll `shouldBe` 0x41+    setAnyoneCanPay sigHashAll `shouldBe` 0x81+    setAnyoneCanPay (setForkIdFlag sigHashAll) `shouldBe` 0xc1+  it "can test flags" $ do+    hasForkIdFlag sigHashAll `shouldBe` False+    hasForkIdFlag (setForkIdFlag sigHashAll) `shouldBe` True+    anyoneCanPay sigHashAll `shouldBe` False+    anyoneCanPay (setAnyoneCanPay sigHashAll) `shouldBe` True+    isSigHashAll sigHashNone `shouldBe` False+    isSigHashAll sigHashAll `shouldBe` True+    isSigHashNone sigHashSingle `shouldBe` False+    isSigHashNone sigHashNone `shouldBe` True+    isSigHashSingle sigHashAll `shouldBe` False+    isSigHashSingle sigHashSingle `shouldBe` True+    isSigHashUnknown sigHashAll `shouldBe` False+    isSigHashUnknown sigHashNone `shouldBe` False+    isSigHashUnknown sigHashSingle `shouldBe` False+    isSigHashUnknown 0x00 `shouldBe` True+    isSigHashUnknown 0x04 `shouldBe` True+  it "can decodeTxSig . encode a TxSignature" $+    property $+      forAll (arbitraryTxSignature net ctx) $ \(_, _, ts) ->+        let f = decodeTxSig net ctx . encodeTxSig net ctx+         in f ts `shouldBe` Right ts+  it "can produce the sighash one" $+    property $+      forAll (arbitraryTx net ctx) $+        forAll arbitraryScript . testSigHashOne net++testSigHashOne :: Network -> Tx -> Script -> Word64 -> Bool -> Property+testSigHashOne net tx s val acp =+  not (null tx.inputs) ==>+    if length tx.inputs > length tx.outputs+      then res `shouldBe` one+      else res `shouldNotBe` one+  where+    res = txSigHash net tx s val (length tx.inputs - 1) (f sigHashSingle)+    one = "0100000000000000000000000000000000000000000000000000000000000000"+    f =+      if acp+        then setAnyoneCanPay+        else id++{- Parse tests from bitcoin-qt repository -}++mapMulSigVector :: Ctx -> ((Text, Text), Int) -> Spec+mapMulSigVector ctx (v, i) =+  it name $ runMulSigVector ctx v+  where+    name = "check multisig vector " <> show i++runMulSigVector :: Ctx -> (Text, Text) -> Assertion+runMulSigVector ctx (a, ops) = assertBool "multisig vector" $ Just a == b+  where+    s = do+      s' <- decodeHex ops+      eitherToMaybe $ runGetS deserialize s'+    b = do+      o <- s+      d <- eitherToMaybe $ decodeOutput ctx o+      addrToText btc $ payToScriptAddress ctx d++sigDecodeMap :: Network -> Ctx -> (Text, Int) -> Spec+sigDecodeMap net ctx (_, i) =+  it ("check signature " ++ show i) func+  where+    func = testSigDecode net ctx $ scriptSigSignatures !! i++testSigDecode :: Network -> Ctx -> Text -> Assertion+testSigDecode net ctx str =+  let bs = fromJust $ decodeHex str+      eitherSig = decodeTxSig net ctx bs+   in assertBool+        ( unwords+            [ "Decode failed:",+              fromLeft (error "Decode did not fail") eitherSig+            ]+        )+        $ isRight eitherSig++mulSigVectors :: [(Text, Text)]+mulSigVectors =+  [ ( "3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC",+      "52410491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb\+      \129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8\+      \334f864104865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253\+      \e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccf\+      \fef4ec687441048d2455d2403e08708fc1f556002f1b6cd83f992d085097f997\+      \4ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f\+      \322a1863d4621353ae"+    )+  ]++scriptSigSignatures :: [Text]+scriptSigSignatures =+  -- Signature in input of txid+  -- 1983a69265920c24f89aac81942b1a59f7eb30821a8b3fb258f88882b6336053+  [ "304402205ca6249f43538908151fe67b26d020306c0e59fa206cf9f3ccf641f333\+    \57119d02206c82f244d04ac0a48024fb9cc246b66e58598acf206139bdb7b75a29\+    \41a2b1e401"+    -- Signature in input of txid+    -- fb0a1d8d34fa5537e461ac384bac761125e1bfa7fec286fa72511240fa66864d.+    -- Strange DER sizes, but in Blockchain. Now invalid as Haskoin can only+    -- decode strict signatures.+    -- "3048022200002b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378d\+    -- \b4b14e736602220000334a96676e58b1bb01784cb7c556dd8ce1c220171904da22\+    -- \e18fe1e7d1510db501"+  ]++encodeScriptVector :: Assertion+encodeScriptVector =+  assertEqual "Encode script" res (encodeHex $ runPutS $ serialize s)+  where+    res =+      "514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58b\+      \bfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d\+      \348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2f\+      \cfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b\+      \25c15342af52ae"+    s =+      Script+        [ OP_1,+          opPushData $+            d+              "04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef5\+              \8bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d\+              \11fcdd0d348ac4",+          opPushData $+            d+              "0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcf\+              \deb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39\+              \f58b25c15342af",+          OP_2,+          OP_CHECKMULTISIG+        ]+    d = fromJust . decodeHex
+ test/Haskoin/Transaction/PartialSpec.hs view
@@ -0,0 +1,615 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TypeApplications #-}++module Haskoin.Transaction.PartialSpec (spec) where++import Control.Monad ((<=<))+import Data.Aeson (FromJSON, parseJSON, withObject, (.:))+import Data.Bifunctor (first)+import Data.ByteString (ByteString)+import Data.ByteString.Base64 (decodeBase64Untyped)+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Either (fromRight, isLeft, isRight)+import Data.HashMap.Strict (fromList, singleton)+import Data.Maybe (fromJust, isJust)+import Data.Serialize+import Data.Text (Text)+import Data.Text qualified as Text+import Data.Text.Encoding (encodeUtf8)+import Haskoin.Address+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Script+import Haskoin.Transaction+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.HUnit (Assertion, assertBool, assertEqual)+import Test.Hspec+import Test.QuickCheck++spec :: Spec+spec = prepareContext $ \ctx ->+  describe "PSBT unit tests" $ do+    it "encodes trivial PSBT" $+      (encodeHex . runPut . putPSBT ctx) trivialPSBT == trivialPSBTHex+    it "decodes trivial PSBT" $+      decodeHexPSBT ctx trivialPSBTHex == Right trivialPSBT+    it "encodes and decodes non-empty transactions" $+      (runGet (getPSBT ctx) . runPut . putPSBT ctx) nonEmptyTransactionPSBT == Right nonEmptyTransactionPSBT+    it "does not decode invalid BIP test vectors" $+      mapM_ (invalidVecTest ctx) invalidVec+    it "encodes valid BIP test vectors" $+      mapM_ (uncurry (encodeVecTest ctx)) validEncodeVec+    it "decodes valid BIP test vectors" $+      mapM_ (uncurry (decodeVecTest ctx)) $+        zip [1 ..] validVec+    it "decodes vector 2" $ vec2Test ctx+    it "decodes vector 3" $ vec3Test ctx+    it "decodes vector 4" $ vec4Test ctx+    it "decodes vector 5" $ vec5Test ctx+    it "decodes vector 6" $ vec6Test ctx+    it "signed and finalized P2PKH PSBTs verify" $+      property $+        forAll (arbitraryKeyPair ctx) $+          verifyNonWitnessPSBT btc ctx . unfinalizedPkhPSBT btc ctx+    it "signed and finalized multisig PSBTs verify" $+      property $+        forAll (arbitraryMultiSig ctx) $+          verifyNonWitnessPSBT btc ctx . unfinalizedMsPSBT btc ctx+    it "encodes and decodes PSBT with final witness script" $+      (fmap (encodeHex . runPut . putPSBT ctx) . decodeHexPSBT ctx) validVec7Hex == Right validVec7Hex+    it "handles complex PSBTs correctly" $ complexPsbtTest ctx+    it "calculates keys properly" $ psbtSignerTest ctx++vec2Test :: Ctx -> Assertion+vec2Test ctx = do+  psbt <- decodeHexPSBTM ctx "Cannot parse validVec2" validVec2Hex+  assertEqual "2 inputs" 2 (length psbt.inputs)+  assertEqual "2 outputs" 2 (length psbt.outputs)+  assertBool "final script sig" $ isJust (head psbt.inputs).finalScriptSig++  let rdmScript = fromJust (psbt.inputs !! 1).inputRedeemScript+  assertBool "p2wpkh" $ (isPayWitnessPKHash <$> decodeOutput ctx rdmScript) == Right True++  let scrptPubKey = witnessScriptPubKey ctx (psbt.inputs !! 1)+      rdmScriptP2SH = toP2SH rdmScript+  assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey+  assertEqual "expected redeem script" (expectedOut ctx) rdmScriptP2SH++  mapM_ (assertEqual "outputs are empty" emptyOutput) psbt.outputs++vec3Test :: Ctx -> Assertion+vec3Test ctx = do+  psbt <- decodeHexPSBTM ctx "Cannot parse validVec3" validVec3Hex+  assertEqual "1 input" 1 (length psbt.inputs)+  assertEqual "2 outputs" 2 (length psbt.outputs)+  let (txInput : _) = psbt.unsignedTransaction.inputs+      (firstInput : _) = psbt.inputs+      Just utx = firstInput.nonWitnessUtxo+      OutPoint prevHash prevVOut = txInput.outpoint+  assertEqual "txids of inputs match" prevHash (txHash utx)+  let prevOutputKey =+        fromRight (error "Could not decode key")+          . unmarshal ctx+          . (.script)+          $ utx.outputs !! fromIntegral prevVOut+  assertBool "p2pkh" $ isPayPKHash prevOutputKey+  assertEqual "sighash type" sigHashAll (fromJust firstInput.sigHashType)++vec4Test :: Ctx -> Assertion+vec4Test ctx = do+  psbt <- decodeHexPSBTM ctx "Cannot parse validVec4" validVec4Hex+  assertEqual "2 inputs" 2 (length psbt.inputs)+  assertEqual "2 outputs" 2 (length psbt.outputs)+  let (firstInput : _) = psbt.inputs+      (_ : secondInput : _) = psbt.inputs+  assertEqual "first input not final script sig" Nothing firstInput.finalScriptSig+  assertEqual "second input not final script sig" Nothing secondInput.finalScriptSig++  let Just rdmScript = secondInput.inputRedeemScript+  assertBool "p2wpkh" $ (isPayWitnessPKHash <$> decodeOutput ctx rdmScript) == Right True++  let scrptPubKey = witnessScriptPubKey ctx secondInput+      rdmScriptP2SH = toP2SH rdmScript+  assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey+  assertEqual "expected redeem script" (expectedOut ctx) rdmScriptP2SH++  assertBool "all non-empty outputs" $ emptyOutput `notElem` psbt.outputs++vec5Test :: Ctx -> Assertion+vec5Test ctx = do+  psbt <- decodeHexPSBTM ctx "Cannot parse validVec5" validVec5Hex+  assertEqual "Correctly decode PSBT" expectedPsbt psbt+  let (input : _) = psbt.inputs++  let Just rdmScript = input.inputRedeemScript+  assertBool "p2wsh" $ (isPayWitnessScriptHash <$> decodeOutput ctx rdmScript) == Right True++  let scrptPubKey = witnessScriptPubKey ctx input+      rdmScriptP2SH = toP2SH rdmScript+  assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey+  assertEqual "expected redeem script" expectedOut2 rdmScriptP2SH+  where+    expectedOut2 =+      fromRight (error "could not decode expected output")+        . unmarshal ctx+        . fromJust+        $ decodeHex "a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87"+    -- From the bitcoind decodepsbt rpc call+    expectedPsbt =+      PSBT+        { unsignedTransaction =+            Tx+              { version = 2,+                inputs =+                  [ TxIn+                      { outpoint =+                          OutPoint+                            { hash = "39bc5c3b33d66ce3d7852a7942331e3ec10f8ba50f225fc41fb5dfa523239a27",+                              index = 0+                            },+                        script = "",+                        sequence = 4294967295+                      }+                  ],+                outputs =+                  [ TxOut+                      { value = 199908000,+                        script = (fromJust . decodeHex) "76a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac"+                      }+                  ],+                witness = mempty,+                locktime = 0+              },+          globalUnknown = mempty,+          inputs =+            [ Input+                { nonWitnessUtxo = Nothing,+                  witnessUtxo =+                    Just+                      ( TxOut+                          { value = 199909013,+                            script = (fromJust . decodeHex) "a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87"+                          }+                      ),+                  partialSigs =+                    fromList+                      [ ( PublicKey+                            { point =+                                fromJust $+                                  importPubKey ctx+                                    =<< decodeHex+                                      "03b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46",+                              compress = True+                            },+                          (fromJust . decodeHex) "304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a01"+                        )+                      ],+                  sigHashType = Nothing,+                  inputRedeemScript =+                    Just+                      . fromRight (error "vec5Test: Could not decode redeem script")+                      . decode+                      . fromJust+                      $ decodeHex "0020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681",+                  inputWitnessScript =+                    Just+                      . fromRight (error "vec5Test: Could not decode witness script")+                      . decode+                      . fromJust+                      $ decodeHex "522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae",+                  inputHDKeypaths =+                    fromList+                      [ ( PublicKey+                            { point =+                                fromJust $+                                  importPubKey ctx+                                    =<< decodeHex+                                      "03b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46",+                              compress = True+                            },+                          ("b4a6ba67", [hardIndex 0, hardIndex 0, hardIndex 4])+                        ),+                        ( PublicKey+                            { point =+                                fromJust $+                                  importPubKey ctx+                                    =<< decodeHex+                                      "03de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd",+                              compress = True+                            },+                          ("b4a6ba67", [hardIndex 0, hardIndex 0, hardIndex 5])+                        )+                      ],+                  finalScriptSig = Nothing,+                  finalScriptWitness = Nothing,+                  inputUnknown = mempty+                }+            ],+          outputs =+            [ Output+                { outputRedeemScript = Nothing,+                  outputWitnessScript = Nothing,+                  outputHDKeypaths = mempty,+                  outputUnknown = mempty+                }+            ]+        }+    hardIndex = (+ 2 ^ 31)++vec6Test :: Ctx -> Assertion+vec6Test ctx = do+  psbt <- decodeHexPSBTM ctx "Cannot parse validVec6" validVec6Hex+  assertEqual "1 input" 1 (length psbt.inputs)+  assertEqual "1 output" 1 (length psbt.outputs)++  let tx = psbt.unsignedTransaction+  assertEqual "correct transaction" "75c5c9665a570569ad77dd1279e6fd4628a093c4dcbf8d41532614044c14c115" (txHash tx)++  assertEqual "correct unknowns" expectedUnknowns (head psbt.inputs).inputUnknown+  where+    expectedUnknowns =+      UnknownMap $+        singleton+          (Key 0x0f (fromJust $ decodeHex "010203040506070809"))+          (fromJust $ decodeHex "0102030405060708090a0b0c0d0e0f")++complexPsbtTest :: Ctx -> Assertion+complexPsbtTest ctx = do+  complex <- readTestFileParser (parseComplexJSON ctx) "complex_psbt.json"++  let computedCombinedPsbt = mergeMany $ complexSignedPsbts complex+      expectedCombinedPsbt = stripRedundantUtxos $ complexCombinedPsbt complex+  assertEqual "combined psbt" computedCombinedPsbt (Just expectedCombinedPsbt)++  let computedCompletePsbt = complete ctx $ complexCombinedPsbt complex+      expectedCompletePsbt = complexCompletePsbt complex+  assertEqual "complete psbt" computedCompletePsbt expectedCompletePsbt++  let computedFinalTx = finalTransaction $ complexCompletePsbt complex+  assertEqual "final tx" computedFinalTx (complexFinalTx complex)+  where+    stripRedundantUtxos PSBT {..} = PSBT {inputs = stripRedundantUtxo <$> inputs, ..}+    stripRedundantUtxo input+      | Just {} <- input.witnessUtxo = input {nonWitnessUtxo = Nothing}+      | otherwise = input++psbtSignerTest :: Ctx -> Assertion+psbtSignerTest ctx = do+  assertEqual "recover explicit secret key" (Just theSecKey) (getSignerKey signer thePubKey Nothing)+  assertEqual+    "recover key for origin path"+    (Just originPathSecKey)+    (getSignerKey signer originPathPubKey (Just (rootFP, originKeyPath)))+  assertEqual+    "recover key for direct path"+    (Just directPathSecKey)+    (getSignerKey signer directPathPubKey (Just (keyFP, directPath)))+  where+    signer = secKeySigner ctx theSecKey <> xPrvSigner ctx xprv (Just origin)++    Just theSecKey = secKey "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"+    thePubKey = PublicKey {point = derivePubKey ctx theSecKey, compress = True}++    rootXPrv = makeXPrvKey "PSBTSignerTest"+    rootFP = xPubFP ctx $ deriveXPubKey ctx rootXPrv+    xprv = derivePath ctx keyPath rootXPrv+    keyFP = xPubFP ctx $ deriveXPubKey ctx xprv+    keyPath = Deriv :| 444+    origin = (rootFP, keyPath)++    originKeyPath = Deriv :| 444 :/ 0+    originPathSecKey = (derivePath ctx originKeyPath rootXPrv).key+    originPathPubKey = PublicKey {point = derivePubKey ctx originPathSecKey, compress = True}++    directPath = Deriv :/ 1+    directPathSecKey = (derivePath ctx directPath xprv).key+    directPathPubKey = PublicKey {point = derivePubKey ctx directPathSecKey, compress = True}++expectedOut :: Ctx -> ScriptOutput+expectedOut ctx =+  fromRight (error "could not decode expected output")+    . unmarshal ctx+    . fromJust+    $ decodeHex "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"++witnessScriptPubKey :: Ctx -> Input -> ScriptOutput+witnessScriptPubKey ctx =+  fromRight (error "could not decode witness utxo")+    . unmarshal ctx+    . (.script)+    . fromJust+    . (.witnessUtxo)++decodeHexPSBT :: Ctx -> Text -> Either String PSBT+decodeHexPSBT ctx = runGet (getPSBT ctx) . fromJust . decodeHex++decodeHexPSBTM :: (Monad m, MonadFail m) => Ctx -> String -> Text -> m PSBT+decodeHexPSBTM ctx errMsg = either (fail . (errMsg <>) . (": " <>)) return . decodeHexPSBT ctx++hexScript :: Text -> ByteString+hexScript =+  either (error "Could not decode script") encodeScript+    . runGetS deserialize+    . fromJust+    . decodeHex+  where+    encodeScript :: Script -> ByteString+    encodeScript = runPutS . serialize++invalidVecTest :: Ctx -> Text -> Assertion+invalidVecTest ctx = assertBool "invalid psbt" . isLeft . decodeHexPSBT ctx++decodeVecTest :: Ctx -> Int -> Text -> Assertion+decodeVecTest ctx i = assertBool (show i <> " decodes correctly") . isRight . decodeHexPSBT ctx++encodeVecTest :: Ctx -> PSBT -> Text -> Assertion+encodeVecTest ctx psbt hex =+  assertEqual+    "encodes correctly"+    ((runPut . putPSBT ctx) psbt)+    ((fromJust . decodeHex) hex)++trivialPSBT :: PSBT+trivialPSBT =+  PSBT+    { unsignedTransaction = Tx {version = 2, inputs = [], outputs = [], witness = [], locktime = 0},+      globalUnknown = UnknownMap mempty,+      inputs = [],+      outputs = []+    }++trivialPSBTHex :: Text+trivialPSBTHex = "70736274ff01000a0200000000000000000000"++nonEmptyTransactionPSBT :: PSBT+nonEmptyTransactionPSBT = emptyPSBT testTx1++verifyNonWitnessPSBT :: Network -> Ctx -> PSBT -> Bool+verifyNonWitnessPSBT net ctx psbt =+  verifyStdTx net ctx (finalTransaction (complete ctx psbt)) sigData+  where+    sigData = inputSigData =<< zip psbt.inputs psbt.unsignedTransaction.inputs+    decodeOutScript = fromRight (error "Could not parse output script") . unmarshal ctx+    inputSigData (input@Input {}, txInput@TxIn {}) =+      map+        (\(TxOut val script) -> (decodeOutScript script, val, txInput.outpoint))+        (fromJust input.nonWitnessUtxo).outputs++unfinalizedPkhPSBT :: Network -> Ctx -> (PrivateKey, PublicKey) -> PSBT+unfinalizedPkhPSBT net ctx (prvKey, pubKey) =+  let PSBT {..} = emptyPSBT currTx+   in PSBT {inputs = [input], ..}+  where+    input =+      emptyInput+        { nonWitnessUtxo = Just prevTx,+          partialSigs = singleton pubKey sig+        }+    currTx =+      unfinalizedTx (txHash prevTx)+    prevTx =+      testUtxo [prevOut]+    prevOutScript =+      addressToScript ctx (pubKeyAddr ctx pubKey)+    prevOut =+      TxOut+        { value = 200000000,+          script = runPutS (serialize prevOutScript)+        }+    h = txSigHash net currTx prevOutScript prevOut.value 0 sigHashAll+    sig =+      encodeTxSig net ctx $+        TxSignature (signHash ctx prvKey.key h) sigHashAll++arbitraryMultiSig :: Ctx -> Gen ([(PrivateKey, PublicKey)], Int)+arbitraryMultiSig ctx = do+  (m, n) <- arbitraryMSParam+  keys <- vectorOf n (arbitraryKeyPair ctx)+  return (keys, m)++unfinalizedMsPSBT :: Network -> Ctx -> ([(PrivateKey, PublicKey)], Int) -> PSBT+unfinalizedMsPSBT net ctx (keys, m) =+  let PSBT {..} = emptyPSBT currTx+   in PSBT {inputs = [input], ..}+  where+    input =+      emptyInput+        { nonWitnessUtxo = Just prevTx,+          partialSigs = sigs,+          inputRedeemScript = Just prevOutScript+        }+    currTx = unfinalizedTx (txHash prevTx)+    prevTx = testUtxo [prevOut]+    prevOutScript = encodeOutput ctx $ PayMulSig (map snd keys) m+    prevOut =+      TxOut+        { value = 200000000,+          script = marshal ctx (toP2SH prevOutScript)+        }+    h = txSigHash net currTx prevOutScript prevOut.value 0 sigHashAll+    sigs = fromList $ map sig keys+    sig (prvKey@PrivateKey {key}, pubKey) =+      let sh = signHash ctx key h+          ts = TxSignature sh sigHashAll+       in (pubKey, encodeTxSig net ctx ts)++unfinalizedTx :: TxHash -> Tx+unfinalizedTx prevHash =+  Tx+    { version = 2,+      inputs =+        [ TxIn+            { outpoint = OutPoint prevHash 0,+              script = "",+              sequence = 4294967294+            }+        ],+      outputs =+        [ TxOut {value = 99999699, script = hexScript "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac"},+          TxOut {value = 100000000, script = hexScript "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"}+        ],+      witness = [],+      locktime = 1257139+    }++invalidVec :: [Text]+invalidVec =+  [ "0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300",+    "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000",+    "70736274ff0100fd0a010200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be4000000006a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa88292feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000",+    "70736274ff000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000",+    "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000000",+    "70736274ff020001550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+    "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac000000000002010020955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+    "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87210203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+    "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a01020400220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+    "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d568102050047522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+    "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae210603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd10b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+    "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f0000000000020000bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",+    "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000020700da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",+    "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903020800da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",+    "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00210203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58710d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",+    "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0203000100000000010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00",+    "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0002000016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00",+    "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c00010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a6521010025512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00"+  ]++validEncodeVec :: [(PSBT, Text)]+validEncodeVec = [(validVec1, validVec1Hex)]++testTx1 :: Tx+testTx1 =+  Tx+    { version = 2,+      inputs =+        [ TxIn+            { outpoint = OutPoint "f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126" 0,+              script = "",+              sequence = 4294967294+            }+        ],+      outputs =+        [ TxOut {value = 99999699, script = hexScript "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac"},+          TxOut {value = 100000000, script = hexScript "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"}+        ],+      witness = [],+      locktime = 1257139+    }++testUtxo :: [TxOut] -> Tx+testUtxo prevOuts =+  Tx+    { version = 1,+      inputs =+        [ TxIn+            { outpoint = OutPoint "e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389" 1,+              script = hexScript "160014be18d152a9b012039daf3da7de4f53349eecb985",+              sequence = 4294967295+            },+          TxIn+            { outpoint = OutPoint "b490486aec3ae671012dddb2bb08466bef37720a533a894814ff1da743aaf886" 1,+              script = hexScript "160014fe3e9ef1a745e974d902c4355943abcb34bd5353",+              sequence = 4294967295+            }+        ],+      outputs = prevOuts,+      witness =+        [ [ fromJust $ decodeHex "304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c01",+            fromJust $ decodeHex "03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105"+          ],+          [ fromJust $ decodeHex "3045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01",+            fromJust $ decodeHex "0223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab3"+          ]+        ],+      locktime = 0+    }++testUtxo1 :: Tx+testUtxo1 =+  testUtxo+    [ TxOut {value = 200000000, script = hexScript "76a91485cff1097fd9e008bb34af709c62197b38978a4888ac"},+      TxOut {value = 190303501938, script = hexScript "a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587"}+    ]++validVec1 :: PSBT+validVec1 =+  let PSBT {..} = emptyPSBT testTx1+   in PSBT {inputs = [emptyInput {nonWitnessUtxo = Just testUtxo1}], ..}++validVec :: [Text]+validVec = [validVec1Hex, validVec2Hex, validVec3Hex, validVec4Hex, validVec5Hex, validVec6Hex]++validVec1Hex :: Text+validVec1Hex = "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab300000000000000"++validVec2Hex :: Text+validVec2Hex = "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac000000000001076a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa882920001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000"++validVec3Hex :: Text+validVec3Hex = "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001030401000000000000"++validVec4Hex :: Text+validVec4Hex = "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000100df0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e13000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb8230800220202ead596687ca806043edc3de116cdf29d5e9257c196cd055cf698c8d02bf24e9910b4a6ba670000008000000080020000800022020394f62be9df19952c5587768aeb7698061ad2c4a25c894f47d8c162b4d7213d0510b4a6ba6700000080010000800200008000"++validVec5Hex :: Text+validVec5Hex = "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"++validVec6Hex :: Text+validVec6Hex = "70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f0000"++-- Example of a PSBT with a `finalWitnessScript`+validVec7Hex :: Text+validVec7Hex = "70736274ff0100520200000001815dd29e16fd2f567a040ce24f5337fb9cfd0c05bacd8890714a33edc7cbbc920000000000ffffffff0192f1052a01000000160014ef9ade26f63015d57f4ecdb268d1a9b8d6cd8872000000000001008402000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03510101ffffffff0200f2052a010000001600145f4ffa19dbbe464561c50fc4d8d8ac0b41009dd20000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90000000001011f00f2052a010000001600145f4ffa19dbbe464561c50fc4d8d8ac0b41009dd201086b02473044022026a9f7afdb0128970bb3577e536ec3d3dc10c1e82650d11c9da1df9003b31d0202202258b11f962f12e0897c642cd6f38a0181db17197f3693a530c9431eb44dde7d0121033dc786e9628bb6c41c08fceb9b37458ad7a95e7e6b04e0bde45b6879398c3ac100220203a6affb58dda998a4ffdce652feb91038fdfc78c748ae687372e11292af8d312d101c4c5bfc00000080000000800100008000"++data ComplexPSBT = ComplexPSBT+  { complexSignedPsbts :: [PSBT],+    complexCombinedPsbt :: PSBT,+    complexCompletePsbt :: PSBT,+    complexFinalTx :: Tx+  }+  deriving (Eq, Show)++parseComplexJSON ctx = withObject "ComplexPSBT" $ \obj -> do+  ComplexPSBT+    <$> sequence+      [ psbtField "miner_psbt" obj,+        psbtField "p2pkh_psbt" obj,+        psbtField "p2sh_ms_1_psbt" obj,+        psbtField "p2sh_ms_2_psbt" obj,+        psbtField "p2sh_pk_psbt" obj,+        psbtField "p2sh_wsh_pk_psbt" obj,+        psbtField "p2sh_wsh_ms_1_psbt" obj,+        psbtField "p2sh_wsh_ms_2_psbt" obj,+        psbtField "p2wpkh_psbt" obj,+        psbtField "p2wsh_pk_psbt" obj,+        psbtField "p2wsh_ms_1_psbt" obj,+        psbtField "p2wsh_ms_2_psbt" obj+      ]+    <*> psbtField "combined_psbt" obj+    <*> psbtField "complete_psbt" obj+    <*> (obj .: "final_tx" >>= parseTx)+  where+    parseTx =+      either fail pure+        . ( decode+              <=< maybe (Left "hex") Right+                . decodeHex+          )+    parsePsbt =+      either fail pure+        . ( runGet (getPSBT ctx)+              <=< first Text.unpack . decodeBase64Untyped+          )+        . encodeUtf8+    psbtField fieldName obj =+      obj .: fieldName >>= parsePsbt
+ test/Haskoin/Transaction/TaprootSpec.hs view
@@ -0,0 +1,181 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}++module Haskoin.Transaction.TaprootSpec (spec) where++import Control.Applicative ((<|>))+import Control.Monad (zipWithM, (<=<))+import Data.Aeson+import Data.Aeson.Types+import Data.ByteArray qualified as BA+import Data.ByteString (ByteString)+import Data.ByteString qualified as BS+import Data.Bytes.Get (runGetS)+import Data.Bytes.Put (runPutS)+import Data.Bytes.Serial (deserialize, serialize)+import Data.Text (Text)+import Data.Word (Word8)+import Haskoin+import Test.HUnit (assertBool, (@?=))+import Test.Hspec (Spec, describe, it, runIO)++spec :: Spec+spec = prepareContext $ \ctx -> do+  TestVector {testScriptPubKey} <-+    runIO $+      readTestFileParser (testVectorParseJSON ctx) "bip341.json"+  describe "Taproot" $ do+    it "should calculate the correct hashes" $+      mapM_ testHashes testScriptPubKey+    it "should build the correct output key" $+      mapM_ (testOutputKey ctx) testScriptPubKey+    it "should build the correct script output" $+      mapM_ (testScriptOutput ctx) testScriptPubKey+    it "should calculate the correct control blocks" $+      mapM_ (testControlBlocks ctx) testScriptPubKey+    it "should arrive at the correct address" $+      mapM_ (testAddress ctx) testScriptPubKey++testHashes :: TestScriptPubKey -> IO ()+testHashes testData =+  mapM_ checkMASTDetails $ ((.mast) . tspkGiven) testData+  where+    checkMASTDetails theMAST = do+      -- Leaf hashes+      (Just . getLeafHashes) theMAST @?= (spkiLeafHashes . tspkIntermediary) testData+      -- Merkle root+      (Just . BA.convert . mastCommitment) theMAST @?= (spkiMerkleRoot . tspkIntermediary) testData++    getLeafHashes = \case+      MASTBranch branchL branchR -> getLeafHashes branchL <> getLeafHashes branchR+      leaf@MASTLeaf {} -> [BA.convert $ mastCommitment leaf]+      MASTCommitment {} -> mempty -- The test vectors have complete trees++testOutputKey :: Ctx -> TestScriptPubKey -> IO ()+testOutputKey ctx testData = do+  XOnlyPubKey (taprootOutputKey ctx theOutput) @?= theOutputKey+  where+    theOutput = tspkGiven testData+    theOutputKey = XOnlyPubKey . spkiTweakedPubKey $ tspkIntermediary testData++testScriptOutput :: Ctx -> TestScriptPubKey -> IO ()+testScriptOutput ctx testData =+  taprootScriptOutput ctx (tspkGiven testData) @?= (spkeScriptPubKey . tspkExpected) testData++testControlBlocks :: Ctx -> TestScriptPubKey -> IO ()+testControlBlocks ctx testData = do+  mapM_ onExamples exampleControlBlocks+  mapM_ checkVerification scriptPathSpends+  where+    theOutput = tspkGiven testData+    theOutputKey = taprootOutputKey ctx theOutput+    exampleControlBlocks = spkeControlBlocks $ tspkExpected testData+    calculatedControlBlocks =+      (!! 1) . encodeTaprootWitness ctx . ScriptPathSpend <$> scriptPathSpends+    scriptPathSpends =+      mkScriptPathSpend <$> maybe mempty getMerkleProofs theOutput.mast+    mkScriptPathSpend (leafVersion, script, proof) =+      ScriptPathData+        { annex = Nothing,+          stack = mempty,+          script,+          extIsOdd = odd $ keyParity ctx theOutputKey,+          leafVersion,+          internalKey = theOutput.internalKey,+          control = BA.convert <$> proof+        }+    onExamples = zipWithM (@?=) calculatedControlBlocks+    checkVerification = assertBool "Script verifies" . verifyScriptPathData ctx theOutputKey++keyParity :: Ctx -> PubKey -> Word8+keyParity ctx key =+  case BS.unpack . marshal ctx $ PublicKey key True of+    0x02 : _ -> 0x00+    _ -> 0x01++testAddress :: Ctx -> TestScriptPubKey -> IO ()+testAddress ctx testData =+  computedAddress @?= (Just . spkeAddress . tspkExpected) testData+  where+    computedAddress =+      (addrToText btc <=< outputAddress ctx)+        . taprootScriptOutput ctx+        $ tspkGiven testData++newtype SpkGiven = SpkGiven {unSpkGiven :: TaprootOutput}++spkGivenParseJSON :: Ctx -> Value -> Parser SpkGiven+spkGivenParseJSON ctx = withObject "SpkGiven" $ \obj -> do+  pxopk@XOnlyPubKey {} <- unmarshalValue ctx =<< obj .: "internalPubkey"+  tree <- traverse parseScriptTree =<< obj .:? "scriptTree"+  return $ SpkGiven $ TaprootOutput pxopk.point tree+  where+    parseScriptTree v =+      parseScriptLeaf v+        <|> parseScriptBranch v+        <|> fail "Unable to parse scriptTree"+    parseScriptLeaf = withObject "ScriptTree leaf" $ \obj ->+      MASTLeaf+        <$> obj .: "leafVersion"+        <*> (obj .: "script" >>= hexScript)+    parseScriptBranch v =+      parseJSON v >>= \case+        [v1, v2] -> MASTBranch <$> parseScriptTree v1 <*> parseScriptTree v2+        _ -> fail "ScriptTree branch"+    hexScript = either fail pure . runGetS deserialize <=< jsonHex++data SpkIntermediary = SpkIntermediary+  { spkiLeafHashes :: Maybe [ByteString],+    spkiMerkleRoot :: Maybe ByteString,+    spkiTweakedPubKey :: PubKey+  }++spkIntermediaryParseJSON :: Ctx -> Value -> Parser SpkIntermediary+spkIntermediaryParseJSON ctx = withObject "SpkIntermediary" $ \obj ->+  SpkIntermediary+    <$> (obj .:? "leafHashes" >>= (traverse . traverse) jsonHex)+    <*> (obj .: "merkleRoot" >>= traverse jsonHex)+    <*> fmap+      (\(XOnlyPubKey k) -> k)+      (unmarshalValue ctx =<< obj .: "tweakedPubkey")++data SpkExpected = SpkExpected+  { spkeScriptPubKey :: ScriptOutput,+    spkeControlBlocks :: Maybe [ByteString],+    spkeAddress :: Text+  }++spkExpectedParseJSON :: Ctx -> Value -> Parser SpkExpected+spkExpectedParseJSON ctx = withObject "SpkExpected" $ \obj ->+  SpkExpected+    <$> (unmarshalValue ctx =<< obj .: "scriptPubKey")+    <*> ((traverse . traverse) jsonHex =<< obj .:? "scriptPathControlBlocks")+    <*> obj .: "bip350Address"++data TestScriptPubKey = TestScriptPubKey+  { tspkGiven :: TaprootOutput,+    tspkIntermediary :: SpkIntermediary,+    tspkExpected :: SpkExpected+  }++testScriptPubKeyParseJSON :: Ctx -> Value -> Parser TestScriptPubKey+testScriptPubKeyParseJSON ctx = withObject "TestScriptPubKey" $ \obj -> do+  given <- unSpkGiven <$> (spkGivenParseJSON ctx =<< obj .: "given")+  inter <- spkIntermediaryParseJSON ctx =<< obj .: "intermediary"+  expect <- spkExpectedParseJSON ctx =<< obj .: "expected"+  return $ TestScriptPubKey given inter expect++newtype TestVector = TestVector+  { testScriptPubKey :: [TestScriptPubKey]+  }++testVectorParseJSON :: Ctx -> Value -> Parser TestVector+testVectorParseJSON ctx = withObject "TestVector" $ \obj ->+  TestVector <$> (mapM (testScriptPubKeyParseJSON ctx) =<< obj .: "scriptPubKey")++jsonHex :: Text -> Parser ByteString+jsonHex = maybe (fail "Unable to decode hex") pure . decodeHex
+ test/Haskoin/TransactionSpec.hs view
@@ -0,0 +1,347 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Haskoin.TransactionSpec (spec) where++import Control.Monad (unless)+import Data.ByteString qualified as B+import Data.Bytes.Get+import Data.Bytes.Put+import Data.Bytes.Serial+import Data.Default (def)+import Data.Either+import Data.Maybe+import Data.String (fromString)+import Data.String.Conversions+import Data.Text (Text)+import Data.Word (Word32, Word64)+import Haskoin.Address+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Network.Data+import Haskoin.Script+import Haskoin.Transaction+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.HUnit+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck++identityTests :: Ctx -> IdentityTests+identityTests ctx =+  def+    { readTests =+        [ ReadBox arbitraryTxHash,+          ReadBox $ flip arbitraryTx ctx =<< arbitraryNetwork,+          ReadBox $ flip arbitraryTxIn ctx =<< arbitraryNetwork,+          ReadBox $ flip arbitraryTxOut ctx =<< arbitraryNetwork,+          ReadBox arbitraryOutPoint+        ],+      jsonTests =+        [ JsonBox arbitraryTxHash,+          JsonBox $ flip arbitraryTx ctx =<< arbitraryNetwork,+          JsonBox $ flip arbitraryWitnessTx ctx =<< arbitraryNetwork,+          JsonBox $ flip arbitraryLegacyTx ctx =<< arbitraryNetwork,+          JsonBox $ flip arbitraryTxIn ctx =<< arbitraryNetwork,+          JsonBox $ flip arbitraryTxOut ctx =<< arbitraryNetwork,+          JsonBox arbitraryOutPoint+        ],+      serialTests =+        [ SerialBox $ flip arbitraryTx ctx =<< arbitraryNetwork,+          SerialBox $ flip arbitraryWitnessTx ctx =<< arbitraryNetwork,+          SerialBox $ flip arbitraryLegacyTx ctx =<< arbitraryNetwork,+          SerialBox $ flip arbitraryTxIn ctx =<< arbitraryNetwork,+          SerialBox $ flip arbitraryTxOut ctx =<< arbitraryNetwork,+          SerialBox arbitraryOutPoint+        ]+    }++spec :: Spec+spec = prepareContext $ \ctx -> do+  testIdentity $ identityTests ctx+  describe "Transaction properties" $ do+    prop "decode and encode txid" $+      forAll arbitraryTxHash $+        \h -> hexToTxHash (txHashToHex h) == Just h+    prop "from string transaction id" $+      forAll arbitraryTxHash $+        \h -> fromString (cs $ txHashToHex h) == h+    prop "building address tx" $+      forAll arbitraryNetwork $ \net ->+        forAll arbitraryAddress $+          forAll (arbitrarySatoshi net) . testBuildAddrTx net ctx+    prop "guess transaction size" $+      forAll arbitraryNetwork $ \net ->+        forAll (arbitraryAddrOnlyTxFull net ctx) (testGuessSize net ctx)+    prop "choose coins" $+      forAll arbitraryNetwork $ \net ->+        forAll (listOf (arbitrarySatoshi net)) testChooseCoins+    prop "choose multisig coins" $+      forAll arbitraryNetwork $ \net ->+        forAll arbitraryMSParam $+          forAll (listOf (arbitrarySatoshi net)) . testChooseMSCoins+    prop "sign and validate transaction" $+      forAll arbitraryNetwork $ \net ->+        forAll (arbitrarySigningData net ctx) (testDetSignTx net ctx)+    prop "sign and validate (nested) transaction" $+      forAll arbitraryNetwork $ \net ->+        forAll (arbitrarySigningData net ctx) (testDetSignNestedTx net ctx)+    prop "merge partially signed transactions" $+      forAll arbitraryNetwork $ \net ->+        property $ forAll (arbitraryPartialTxs net ctx) (testMergeTx net ctx)+  describe "Transaction vectors" $ do+    it "compute txid from tx" $ mapM_ testTxidVector txidVectors+    it "build pkhash transaction (generated from bitcoind)" $+      mapM_ (testPKHashVector ctx) pkHashVectors++-- Txid Vectors++testTxidVector :: (Text, Text) -> Assertion+testTxidVector (tid, tx) =+  assertEqual "txid" (Just tid) (txHashToHex . txHash <$> txM)+  where+    txM = eitherToMaybe . runGetS deserialize =<< decodeHex tx++txidVectors :: [(Text, Text)]+txidVectors =+  [ ( "23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63",+      "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa4\+      \3ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75\+      \418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb\+      \60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f0000000000\+      \1976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000"+    ),+    ( "c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73",+      "01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3\+      \bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a\+      \04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7\+      \d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68\+      \350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b\+      \2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d0000\+      \0000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac000000\+      \00"+    ),+    ( "f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb",+      "01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2\+      \f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332ad\+      \fca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c\+      \70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f\+      \09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919a\+      \d1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737\+      \864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a473044\+      \0220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa\+      \2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f8\+      \1e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc8\+      \0a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd32273276\+      \94c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df3\+      \2949d4646dfa10a92458cfaa88ac00000000"+    ),+    ( "afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae",+      "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c5\+      \9da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678\+      \fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab\+      \486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b\+      \52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbe\+      \a097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045\+      \022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf9506\+      \50802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f\+      \68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e3\+      \77c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e1\+      \8217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a378\+      \8dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972\+      \d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2\+      \e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffff\+      \ff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9c\+      \fdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675\+      \d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa\+      \34b26f62db4a88ac00000000"+    )+  ]++-- Build address transactions vectors generated from bitcoin-core raw tx API++testPKHashVector :: Ctx -> ([(Text, Word32)], [(Text, Word64)], Text) -> Assertion+testPKHashVector ctx (is, os, res) =+  assertEqual+    "Build PKHash Tx"+    (Right res)+    (encodeHex . runPutS . serialize <$> txE)+  where+    txE = buildAddrTx btc ctx (map f is) os+    f (tid, ix) = OutPoint (fromJust $ hexToTxHash tid) ix++pkHashVectors :: [([(Text, Word32)], [(Text, Word64)], Text)]+pkHashVectors =+  [ ( [ ( "eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",+          14+        )+      ],+      [("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb", 90000000)],+      "0100000001db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654\+      \a1eb29eb0e00000000ffffffff01804a5d05000000001976a91424aa604689cc58\+      \2292b97668bedd91dd5bf9374c88ac00000000"+    ),+    ( [ ( "eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",+          0+        ),+        ( "0001000000000000000000000000000000000000000000000000000000000000",+          2147483647+        )+      ],+      [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb", 1),+        ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n", 2100000000000000)+      ],+      "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654\+      \a1eb29eb0000000000ffffffff0000000000000000000000000000000000000000\+      \000000000000000000000100ffffff7f00ffffffff0201000000000000001976a9\+      \1424aa604689cc582292b97668bedd91dd5bf9374c88ac0040075af07507001976\+      \a9145d16672f53981ff21c5f42b40d1954993cbca54f88ac00000000"+    ),+    ( [ ( "eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",+          0+        ),+        ( "0001000000000000000000000000000000000000000000000000000000000000",+          2147483647+        )+      ],+      [],+      "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a\+      \1eb29eb0000000000ffffffff000000000000000000000000000000000000000000\+      \0000000000000000000100ffffff7f00ffffffff0000000000"+    ),+    ( [],+      [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb", 1),+        ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n", 2100000000000000)+      ],+      "01000000000201000000000000001976a91424aa604689cc582292b97668bedd91d\+      \d5bf9374c88ac0040075af07507001976a9145d16672f53981ff21c5f42b40d1954\+      \993cbca54f88ac00000000"+    )+  ]++-- Transaction Properties --++testBuildAddrTx :: Network -> Ctx -> Address -> TestCoin -> Bool+testBuildAddrTx net ctx a (TestCoin v)+  | isPubKeyAddress a = PayPKHash a.hash160 == out+  | isScriptAddress a = PayScriptHash a.hash160 == out+  | otherwise = undefined+  where+    out = either error id $ do+      tx <- buildAddrTx net ctx [] [(fromJust (addrToText net a), v)]+      unmarshal ctx (head tx.outputs).script++-- We compute an upper bound but it should be close enough to the real size+-- We give 2 bytes of slack on every signature (1 on r and 1 on s)+testGuessSize :: Network -> Ctx -> Tx -> Bool+testGuessSize net ctx tx =+  guess >= len && guess <= len + 2 * delta+  where+    delta = pki + sum (map fst msi)+    guess = guessTxSize pki msi pkout msout+    len = B.length $ runPutS $ serialize tx+    ins = map f tx.inputs+    f i = either error id $ unmarshal (net, ctx) i.script+    pki = length $ filter isSpendPKHash ins+    msi = concatMap shData ins+    shData (ScriptHashInput _ (PayMulSig keys r)) = [(r, length keys)]+    shData _ = []+    out = map (either error id . unmarshal ctx . (.script)) tx.outputs+    pkout = length $ filter isPayPKHash out+    msout = length $ filter isPayScriptHash out++testChooseCoins :: [TestCoin] -> Word64 -> Word64 -> Int -> Property+testChooseCoins coins target byteFee nOut =+  nOut >= 0 ==>+    case chooseCoins target byteFee nOut True coins of+      Right (chosen, change) ->+        let outSum = sum $ map coinValue chosen+            fee = guessTxFee byteFee nOut (length chosen)+         in outSum == target + change + fee+      Left _ ->+        let fee = guessTxFee byteFee nOut (length coins)+         in target == 0 || s < target + fee+  where+    s = sum $ map coinValue coins++testChooseMSCoins ::+  (Int, Int) ->+  [TestCoin] ->+  Word64 ->+  Word64 ->+  Int ->+  Property+testChooseMSCoins (m, n) coins target byteFee nOut =+  nOut >= 0 ==>+    case chooseMSCoins target byteFee (m, n) nOut True coins of+      Right (chosen, change) ->+        let outSum = sum $ map coinValue chosen+            fee = guessMSTxFee byteFee (m, n) nOut (length chosen)+         in outSum == target + change + fee+      Left _ ->+        let fee = guessMSTxFee byteFee (m, n) nOut (length coins)+         in target == 0 || s < target + fee+  where+    s = sum $ map coinValue coins++{- Signing Transactions -}++testDetSignTx :: Network -> Ctx -> (Tx, [SigInput], [PrivateKey]) -> Bool+testDetSignTx net ctx (tx, sigis, prv) =+  not verify1 && not verify2 && verify3+  where+    verify1 = verifyStdTx net ctx tx verData+    verify2 = verifyStdTx net ctx txSigP verData+    verify3 = verifyStdTx net ctx txSigC verData+    txSigP = either error id $ signTx net ctx tx sigis (map (.key) (tail prv))+    txSigC = either error id $ signTx net ctx txSigP sigis [(head prv).key]+    sigData SigInput {..} = (script, value, outpoint)+    verData = map sigData sigis++testDetSignNestedTx :: Network -> Ctx -> (Tx, [SigInput], [PrivateKey]) -> Bool+testDetSignNestedTx net ctx (tx, sigis, prv) =+  not verify1 && not verify2 && verify3+  where+    verify1 = verifyStdTx net ctx tx verData+    verify2 = verifyStdTx net ctx txSigP verData+    verify3 = verifyStdTx net ctx txSigC verData+    txSigP =+      either error id $+        signNestedWitnessTx net ctx tx sigis ((.key) <$> tail prv)+    txSigC =+      either error id $+        signNestedWitnessTx net ctx txSigP sigis [(head prv).key]+    verData = handleSegwit <$> sigis+    handleSegwit (SigInput s v o _ _)+      | isSegwit s = (toP2SH (encodeOutput ctx s), v, o)+      | otherwise = (s, v, o)++testMergeTx :: Network -> Ctx -> ([Tx], [(ScriptOutput, Word64, OutPoint, Int, Int)]) -> Bool+testMergeTx net ctx (txs, os) =+  and+    [ isRight mergeRes,+      length mergedTx.inputs == length os,+      if enoughSigs+        then isValid+        else not isValid,+      -- Signature count == min (length txs) (sum required signatures)+      sum (map snd sigMap) == min (length txs) (sum (map fst sigMap))+    ]+  where+    outs = map (\(so, val, op, _, _) -> (so, val, op)) os+    mergeRes = mergeTxs net ctx txs outs+    mergedTx = fromRight (error "Could not merge") mergeRes+    isValid = verifyStdTx net ctx mergedTx outs+    enoughSigs = all (\(m, c) -> c >= m) sigMap+    sigFun (_, _, _, m, _) inp = (m, sigCnt inp)+    sigMap = zipWith sigFun os mergedTx.inputs+    sigCnt inp =+      case unmarshal (net, ctx) inp.script of+        Right (RegularInput (SpendMulSig sigs)) -> length sigs+        Right (ScriptHashInput (SpendMulSig sigs) _) -> length sigs+        _ -> error "Invalid input script type"
+ test/Haskoin/UtilSpec.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE ImportQualifiedPost #-}++module Haskoin.UtilSpec (spec) where++import Data.Aeson+import Data.Aeson.Encoding (encodingToLazyByteString)+import Data.Aeson.Types (Parser, parse)+import Data.ByteString (ByteString)+import Data.ByteString qualified as B+import Data.Default (def)+import Data.Either (fromLeft, fromRight, isLeft, isRight)+import Data.Foldable (toList)+import Data.List (permutations)+import Data.Map.Strict (singleton)+import Data.Maybe+import Data.Sequence qualified as Seq+import Data.Serialize as S+import Haskoin.Crypto+import Haskoin.Util+import Haskoin.Util.Arbitrary+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck (forAll)++identityTests :: IdentityTests+identityTests =+  def+    { readTests =+        [ ReadBox arbitraryNetwork+        ]+    }++spec :: Spec+spec =+  describe "utility functions" $ do+    testIdentity identityTests+    prop "bsToInteger . integerToBS" getPutInteger+    prop "decodeHex . encodeHex" $ forAll arbitraryBS fromToHex+    prop "compare updateIndex with Data.Sequence" testUpdateIndex+    prop "matchTemplate" testMatchTemplate+    prop "testing matchTemplate with two lists" testMatchTemplateLen+    prop "test eitherToMaybe" testEitherToMaybe+    prop "test maybeToEither" testMaybeToEither++{- Various utilities -}++getPutInteger :: Integer -> Bool+getPutInteger i = bsToInteger (integerToBS $ abs i) == abs i++fromToHex :: ByteString -> Bool+fromToHex bs = decodeHex (encodeHex bs) == Just bs++testUpdateIndex :: [Int] -> Int -> Int -> Bool+testUpdateIndex xs v i =+  updateIndex i xs (const v) == toList (Seq.update i v $ Seq.fromList xs)++testMatchTemplate :: [Int] -> Int -> Bool+testMatchTemplate as i = catMaybes res == bs+  where+    res = matchTemplate as bs (==)+    idx =+      if null as+        then 0+        else i `mod` length as+    bs = permutations as !! idx++testMatchTemplateLen :: [Int] -> [Int] -> Bool+testMatchTemplateLen as bs = length bs == length res+  where+    res = matchTemplate as bs (==)++testEitherToMaybe :: Either String Int -> Bool+testEitherToMaybe (Right v) = eitherToMaybe (Right v) == Just v+testEitherToMaybe e = isNothing (eitherToMaybe e)++testMaybeToEither :: Maybe Int -> String -> Bool+testMaybeToEither (Just v) str = maybeToEither str (Just v) == Right v+testMaybeToEither m str = maybeToEither str m == Left str
− test/Network/Haskoin/Address/Bech32Spec.hs
@@ -1,155 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.Address.Bech32Spec-    ( spec-    ) where--import           Control.Monad-import           Data.Bits                      (xor)-import           Data.ByteString                (ByteString)-import qualified Data.ByteString                as B-import           Data.Char                      (chr, ord, toLower)-import           Data.Maybe-import           Data.String.Conversions-import           Data.Text                      (Text, append, pack, snoc,-                                                 uncons)-import qualified Data.Text                      as T-import           Data.Word                      (Word8)-import           Network.Haskoin.Address-import           Network.Haskoin.Address.Bech32-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.HUnit--spec = do-    describe "bech32 checksum" $ do-        it "should have valid checksum" $ forM_ validChecksums testValidChecksum-        it "should have invalid checksum" $-            forM_ invalidChecksums testInvalidChecksum-        it "should be a valid address" $ forM_ validAddresses testValidAddress-        it "should be an invalid address" $-            forM_ invalidAddresses testInvalidAddress-        it "should be same for the input in different case" $-            all (== Just "test12hrzfj") . map (flip bech32Encode []) $ hrpCaseVariants-    describe "more encoding/decoding cases" $ do-        it "length > 90" $-            assert $-            isNothing $-            bech32Encode "bc" (replicate 82 (word5 (1 :: Word8)))-        it "segwit version bounds" $-            assert $ isNothing $ segwitEncode "bc" 17 []-        it "segwit prog len version 0" $-            assert $ isNothing $ segwitEncode "bc" 0 (replicate 30 1)-        it "segwit prog len version != 0" $-            assert $ isJust $ segwitEncode "bc" 1 (replicate 30 1)-        it "segwit prog len version != 0" $-            assert $ isNothing $ segwitEncode "bc" 1 (replicate 41 1)-        it "empty HRP encode" $ assert $ isNothing $ bech32Encode "" []-        it "empty HRP encode" $-            assert $ isNothing $ bech32Decode "10a06t8"-        it "hrp lowercased" $-            bech32Encode "HRP" [] `shouldBe` bech32Encode "hrp" []---testValidChecksum :: Bech32 -> Assertion-testValidChecksum checksum = case bech32Decode checksum of-    Nothing                      -> assertFailure (show checksum)-    Just (resultHRP, resultData) -> do-        -- test that a corrupted checksum fails decoding.-        let (hrp, rest)         = T.breakOnEnd "1" checksum-            Just (first, rest') = uncons rest-            checksumCorrupted   = (hrp `snoc` chr (ord first `xor` 1)) `append` rest'-        assertBool (show checksum ++ " corrupted")-            $ isNothing (bech32Decode checksumCorrupted)-        -- test that re-encoding the decoded checksum results in the same checksum.-        let checksumEncoded  = bech32Encode resultHRP resultData-            expectedChecksum = Just $ T.toLower checksum-        assertEqual (show checksum ++ " re-encode")-                    expectedChecksum-                    checksumEncoded--testInvalidChecksum :: Bech32 -> Assertion-testInvalidChecksum checksum =-    assertBool (show checksum) (isNothing $ bech32Decode checksum)--testValidAddress :: (Text, Text) -> Assertion-testValidAddress (address, hexscript) = do-    let address' = T.toLower address-        hrp      = T.take 2 address'-    case segwitDecode hrp address of-        Nothing                -> assertFailure "decode failed"-        Just (witver, witprog) -> do-            assertEqual (show address)-                        (decodeHex hexscript)-                        (Just $ segwitScriptPubkey witver witprog)-            assertEqual (show address)-                        (Just address')-                        (segwitEncode hrp witver witprog)--testInvalidAddress :: Text -> Assertion-testInvalidAddress address = do-    assertBool (show address) (isNothing $ segwitDecode "bc" address)-    assertBool (show address) (isNothing $ segwitDecode "tb" address)--segwitScriptPubkey :: Word8 -> [Word8] -> ByteString-segwitScriptPubkey witver witprog =-    B.pack $ witver' : fromIntegral (length witprog) : witprog-    where witver' = if witver == 0 then 0 else witver + 0x50--validChecksums :: [Text]-validChecksums =-    [ "A12UEL5L"-    , "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs"-    , "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"-    , "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j"-    , "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w"-    ]--invalidChecksums :: [Text]-invalidChecksums =-    [ " 1nwldj5"-    , "\DEL1axkwrx"-    , "an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx"-    , "pzry9x0s0muk"-    , "1pzry9x0s0muk"-    , "x1b4n0q5v"-    , "li1dgmt3"-    , "de1lg7wt\xFF"-    ]--validAddresses :: [(Text, Text)]-validAddresses =-    [ ("BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", "0014751e76e8199196d454941c45d1b3a323f1433bd6")-    , ("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7"-      ,"00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262")-    , ("bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx"-      ,"5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6")-    , ("BC1SW50QA3JX3S", "6002751e")-    , ("bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", "5210751e76e8199196d454941c45d1b3a323")-    , ("tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy"-      ,"0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433")-    ]--invalidAddresses :: [Text]-invalidAddresses =-    [ "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty"-    , "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"-    , "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2"-    , "bc1rw5uspcuh"-    , "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90"-    , "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P"-    , "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7"-    , "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du"-    , "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv"-    , "bc1gmk9yu"-    ]--hrpCaseVariants:: [Text]-hrpCaseVariants = map T.pack hrpTestPermutations--hrpTestPermutations :: [String]-hrpTestPermutations = do-    a <- ['t', 'T']-    b <- ['e', 'E']-    c <- ['s', 'S']-    d <- ['t', 'T']-    return [a, b, c, d]
− test/Network/Haskoin/Address/CashAddrSpec.hs
@@ -1,280 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.Address.CashAddrSpec (spec) where--import           Control.Monad-import qualified Data.ByteString.Char8            as C-import           Data.Maybe-import           Data.String.Conversions-import           Data.Text                        (Text)-import           Network.Haskoin.Address-import           Network.Haskoin.Address.CashAddr-import           Network.Haskoin.Constants-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.HUnit--spec :: Spec-spec = do-    describe "cashaddr checksum test vectors" $ do-        it "prefix:x64nx6hz" $ do-            let mpb = cash32decode "prefix:x64nx6hz"-            mpb `shouldBe` Just ("prefix", "")-        it "p:gpf8m4h7" $ do-            let mpb = cash32decode "p:gpf8m4h7"-            mpb `shouldBe` Just ("p", "")-        it "bitcoincash:qpzry9x8gf2tvdw0s3jn54khce6mua7lcw20ayyn" $ do-            let mpb =-                    cash32decode-                        "bitcoincash:qpzry9x8gf2tvdw0s3jn54khce6mua7lcw20ayyn"-            mpb `shouldBe`-                Just-                    ( "bitcoincash"-                    , "\NULD2\DC4\199BT\182\&5\207\132e:V\215\198u\190w\223")-        it "bchtest:testnetaddress4d6njnut" $ do-            let mpb = cash32decode "bchtest:testnetaddress4d6njnut"-            mpb `shouldBe` Just ("bchtest", "^`\185\229}kG\152")-        it "bchreg:555555555555555555555555555555555555555555555udxmlmrz" $ do-            let mpb =-                    cash32decode-                        "bchreg:555555555555555555555555555555555555555555555udxmlmrz"-            mpb `shouldBe`-                Just-                    ( "bchreg"-                    , "\165)JR\148\165)JR\148\165)JR\148\165)JR\148\165)JR\148\165)J")-    describe "cashaddr to base58 translation test vectors" $ do-        it "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu" $ do-            let addr =-                    addrToString bch =<<-                    stringToAddr btc "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu"-            addr `shouldBe`-                Just "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"-        it "1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR" $ do-            let addr =-                    addrToString bch =<<-                    stringToAddr btc "1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR"-            addr `shouldBe`-                Just "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy"-        it "16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb" $ do-            let addr =-                    addrToString bch =<<-                    stringToAddr btc "16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb"-            addr `shouldBe`-                Just "bitcoincash:qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r"-        it "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC" $ do-            let addr =-                    addrToString bch =<<-                    stringToAddr btc "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC"-            addr `shouldBe`-                Just "bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq"-        it "3LDsS579y7sruadqu11beEJoTjdFiFCdX4" $ do-            let addr =-                    addrToString bch =<<-                    stringToAddr btc "3LDsS579y7sruadqu11beEJoTjdFiFCdX4"-            addr `shouldBe`-                Just "bitcoincash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e"-        it "31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw" $ do-            let addr =-                    addrToString bch =<<-                    stringToAddr btc "31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw"-            addr `shouldBe`-                Just "bitcoincash:pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37"-    describe "base58 to cashaddr translation test vectors" $ do-        it "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a" $ do-            let addr =-                    addrToString btc =<<-                    stringToAddr-                        bch-                        "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"-            addr `shouldBe` Just "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu"-        it "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy" $ do-            let addr =-                    addrToString btc =<<-                    stringToAddr-                        bch-                        "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy"-            addr `shouldBe` Just "1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR"-        it "bitcoincash:qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r" $ do-            let addr =-                    addrToString btc =<<-                    stringToAddr-                        bch-                        "bitcoincash:qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r"-            addr `shouldBe` Just "16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb"-        it "bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq" $ do-            let addr =-                    addrToString btc =<<-                    stringToAddr-                        bch-                        "bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq"-            addr `shouldBe` Just "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC"-        it "bitcoincash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e" $ do-            let addr =-                    addrToString btc =<<-                    stringToAddr-                        bch-                        "bitcoincash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e"-            addr `shouldBe` Just "3LDsS579y7sruadqu11beEJoTjdFiFCdX4"-        it "bitcoincash:pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37" $ do-            let addr =-                    addrToString btc =<<-                    stringToAddr-                        bch-                        "bitcoincash:pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37"-            addr `shouldBe` Just "31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw"-    describe "cashaddr larger test vectors" $-        forM_ (zip [0 ..] vectors) $ \(i, vec) ->-            it ("cashaddr test vector " <> show (i :: Int)) $ testCashAddr vec--{- Various utilities -}--testCashAddr :: (Int, CashVersion, Cash32, Text) -> Assertion-testCashAddr (len, typ, addr, hex) = do-    let mbs = decodeHex hex-    assertBool "Could not decode hex payload from test vector" (isJust mbs)-    let mlow = cash32decode addr-    assertBool "Could not decode low level address" (isJust mlow)-    let Just (_, lbs) = mlow-    assertEqual "Low-level payload size incorrect" len (C.length lbs - 1)-    assertEqual "Low-level payload doesn't match" bs (C.tail lbs)-    let mdec = cash32decodeType addr-    assertBool ("Could not decode test address: " <> cs addr) (isJust mdec)-    assertEqual "Length doesn't match" len (C.length pay)-    assertEqual "Version doesn't match" typ ver-    assertEqual "Payload doesn't match" bs pay-  where-    Just bs = decodeHex hex-    Just (_, ver, pay) = cash32decodeType addr---- | All vectors starting with @pref@ had the wrong version in the spec--- document.-vectors :: [(Int, CashVersion, Text, Text)]-vectors =-    [ ( 20-      , 0-      , "bitcoincash:qr6m7j9njldwwzlg9v7v53unlr4jkmx6eylep8ekg2"-      , "F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9")-    , ( 20-      , 1-      , "bchtest:pr6m7j9njldwwzlg9v7v53unlr4jkmx6eyvwc0uz5t"-      , "F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9")-    , ( 20-      , 1-      , "pref:pr6m7j9njldwwzlg9v7v53unlr4jkmx6ey65nvtks5"-      , "F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9")-    , ( 20-      , 15-      , "prefix:0r6m7j9njldwwzlg9v7v53unlr4jkmx6ey3qnjwsrf"-      , "F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9")-    , ( 24-      , 0-      , "bitcoincash:q9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2ws4mr9g0"-      , "7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA")-    , ( 24-      , 1-      , "bchtest:p9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2u94tsynr"-      , "7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA")-    , ( 24-      , 1-      , "pref:p9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2khlwwk5v"-      , "7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA")-    , ( 24-      , 15-      , "prefix:09adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2p29kc2lp"-      , "7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA")-    , ( 28-      , 0-      , "bitcoincash:qgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcw59jxxuz"-      , "3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B")-    , ( 28-      , 1-      , "bchtest:pgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcvs7md7wt"-      , "3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B")-    , ( 28-      , 1-      , "pref:pgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcrsr6gzkn"-      , "3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B")-    , ( 28-      , 15-      , "prefix:0gagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkc5djw8s9g"-      , "3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B")-    , ( 32-      , 0-      , "bitcoincash:qvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq5nlegake"-      , "3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060")-    , ( 32-      , 1-      , "bchtest:pvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq7fqng6m6"-      , "3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060")-    , ( 32-      , 1-      , "pref:pvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq4k9m7qf9"-      , "3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060")-    , ( 32-      , 15-      , "prefix:0vch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxqsh6jgp6w"-      , "3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060")-    , ( 40-      , 0-      , "bitcoincash:qnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklv39gr3uvz"-      , "C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB")-    , ( 40-      , 1-      , "bchtest:pnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklvmgm6ynej"-      , "C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB")-    , ( 40-      , 1-      , "pref:pnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklv0vx5z0w3"-      , "C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB")-    , ( 40-      , 15-      , "prefix:0nq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklvwsvctzqy"-      , "C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB")-    , ( 48-      , 0-      , "bitcoincash:qh3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqex2w82sl"-      , "E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C")-    , ( 48-      , 1-      , "bchtest:ph3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqnzf7mt6x"-      , "E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C")-    , ( 48-      , 1-      , "pref:ph3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqjntdfcwg"-      , "E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C")-    , ( 48-      , 15-      , "prefix:0h3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqakcssnmn"-      , "E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C")-    , ( 56-      , 0-      , "bitcoincash:qmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqscw8jd03f"-      , "D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041")-    , ( 56-      , 1-      , "bchtest:pmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqs6kgdsg2g"-      , "D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041")-    , ( 56-      , 1-      , "pref:pmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqsammyqffl"-      , "D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041")-    , ( 56-      , 15-      , "prefix:0mvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqsgjrqpnw8"-      , "D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041")-    , ( 64-      , 0-      , "bitcoincash:qlg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96mtky5sv5w"-      , "D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B")-    , ( 64-      , 1-      , "bchtest:plg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96mc773cwez"-      , "D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B")-    , ( 64-      , 1-      , "pref:plg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96mg7pj3lh8"-      , "D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B")-    , ( 64-      , 15-      , "prefix:0lg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96ms92w6845"-      , "D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B")-    ]
− test/Network/Haskoin/AddressSpec.hs
@@ -1,91 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.AddressSpec (spec) where--import           Data.Aeson-import           Data.Aeson.Types-import           Data.ByteString                (ByteString)-import qualified Data.ByteString                as BS (append, empty, pack)-import           Data.Either                    (isRight)-import           Data.Maybe                     (isJust, fromJust)-import qualified Data.Serialize                 as S-import           Data.Text                      (Text)-import           Network.Haskoin.Address-import           Network.Haskoin.Address.Base58-import           Network.Haskoin.Constants-import           Network.Haskoin.Keys           (derivePubKeyI)-import           Network.Haskoin.Test-import           Test.Hspec-import           Test.HUnit                     (Assertion, assertBool,-                                                 assertEqual)-import           Test.QuickCheck--spec :: Spec-spec = do-    let net = btc-    describe "btc address" . it "base58 encodings" $ mapM_ runVector vectors-    describe "btc-test address" $ props btcTest-    describe "bch address" $ props bch-    describe "bch-test address" $ props bchTest-    describe "bch-regtest address" $ props bchRegTest-    describe "json serialization" . it "encodes and decodes address" $-        forAll arbitraryAddress (testCustom (addrFromJSON net) (addrToJSON net))-    describe "witness address vectors" . it "p2sh(pwpkh)" $-        mapM_ testCompatWitness compatWitnessVectors--props :: Network -> Spec-props net = do-    it "encodes and decodes base58 bytestring" $-        property $-        forAll arbitraryBS $ \bs -> decodeBase58 (encodeBase58 bs) == Just bs-    it "encodes and decodes base58 bytestring with checksum" $-        property $-        forAll arbitraryBS $ \bs ->-            decodeBase58Check (encodeBase58Check bs) == Just bs-    it "encodes and decodes address" $-        property $-        forAll arbitraryAddress $ \a ->-            (stringToAddr net =<< addrToString net a) == Just a-    it "shows and reads address" $-        property $ forAll arbitraryAddress $ \a -> read (show a) == a--runVector :: (ByteString, Text, Text) -> Assertion-runVector (bs, e, chk) = do-    assertEqual "encodeBase58" e b58-    assertEqual "encodeBase58Check" chk b58Chk-    assertEqual "decodeBase58" (Just bs) (decodeBase58 b58)-    assertEqual "decodeBase58Check" (Just bs) (decodeBase58Check b58Chk)-  where-    b58    = encodeBase58 bs-    b58Chk = encodeBase58Check bs--vectors :: [(ByteString, Text, Text)]-vectors =-    [ ( BS.empty, "", "3QJmnh" )-    , ( BS.pack [0], "1", "1Wh4bh" )-    , ( BS.pack [0,0,0,0], "1111", "11114bdQda" )-    , ( BS.pack [0,0,1,0,0], "11LUw", "113CUwsFVuo" )-    , ( BS.pack [255], "5Q", "VrZDWwe" )-    , ( BS.pack [0,0,0,0] `BS.append` BS.pack [1..255]-      , "1111cWB5HCBdLjAuqGGReWE3R3CguuwSjw6RHn39s2yuDRTS5NsBgNiFpWgAnEx6VQi8csexkgYw3mdYrMHr8x9i7aEwP8kZ7vccXWqKDvGv3u1GxFKPuAkn8JCPPGDMf3vMMnbzm6Nh9zh1gcNsMvH3ZNLmP5fSG6DGbbi2tuwMWPthr4boWwCxf7ewSgNQeacyozhKDDQQ1qL5fQFUW52QKUZDZ5fw3KXNQJMcNTcaB723LchjeKun7MuGW5qyCBZYzA1KjofN1gYBV3NqyhQJ3Ns746GNuf9N2pQPmHz4xpnSrrfCvy6TVVz5d4PdrjeshsWQwpZsZGzvbdAdN8MKV5QsBDY"-      , "111151KWPPBRzdWPr1ASeu172gVgLf1YfUp6VJyk6K9t4cLqYtFHcMa2iX8S3NJEprUcW7W5LvaPRpz7UG7puBj5STE3nKhCGt5eckYq7mMn5nT7oTTic2BAX6zDdqrmGCnkszQkzkz8e5QLGDjf7KeQgtEDm4UER6DMSdBjFQVa6cHrrJn9myVyyhUrsVnfUk2WmNFZvkWv3Tnvzo2cJ1xW62XDfUgYz1pd97eUGGPuXvDFfLsBVd1dfdUhPwxW7pMPgdWHTmg5uqKGFF6vE4xXpAqZTbTxRZjCDdTn68c2wrcxApm8hq3JX65Hix7VtcD13FF8b7BzBtwjXq1ze6NMjKgUcqpJTN9vt"-      )-    ]--testCustom :: Eq a => (Value -> Parser a) -> (a -> Value) -> a -> Bool-testCustom f g x = parseMaybe f (g x) == Just x--compatWitnessVectors :: [(Network, Text, Text)]-compatWitnessVectors =-    [ ( btcTest-      , "cNUnpYpMsJXYCERYBciJnsWBpcYEFjdcbq6dxj4SskGhs7uHuJ7Q"-      , "2N6PDTueBHvXzW61B4oe5SW1D3v2Z3Vpbvw")-    ]--testCompatWitness :: (Network, Text, Text) -> Assertion-testCompatWitness (net, seckey, addr) = do-    let seckeyM = fromWif net seckey-    assertBool "decode seckey" (isJust seckeyM)-    let pubkey = derivePubKeyI (fromJust seckeyM)-    let addrM = addrToString btcTest (pubKeyCompatWitnessAddr pubkey)-    assertBool "address can be encoded" (isJust addrM)-    assertEqual "witness address matches" addr (fromJust addrM)
− test/Network/Haskoin/BlockSpec.hs
@@ -1,359 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.BlockSpec-    ( spec-    ) where--import           Control.Monad.State.Strict-import           Data.Aeson                    as A-import           Data.Either                   (fromRight)-import           Data.Map.Strict               (singleton)-import           Data.Maybe                    (fromJust)-import           Data.Serialize                as S-import           Data.String                   (fromString)-import           Data.String.Conversions       (cs)-import           Data.Text                     (Text)-import           Network.Haskoin.Block-import           Network.Haskoin.Block.Headers-import           Network.Haskoin.Block.Merkle-import           Network.Haskoin.Constants-import           Network.Haskoin.Test-import           Network.Haskoin.Transaction-import           Test.Hspec-import           Test.HUnit                    hiding (State)-import           Test.QuickCheck--myTime :: Timestamp-myTime = 1499083075--withChain :: Network -> State HeaderMemory a -> a-withChain net f = evalState f (initialChain net)--chain :: BlockHeaders m => Network -> BlockHeader -> Int -> m ()-chain net bh i = do-    bnsE <- connectBlocks net myTime bhs-    either error (const $ return ()) bnsE-  where-    bhs = appendBlocks net 6 bh i--spec :: Spec-spec = do-    let net = bchRegTest-    describe "blockchain headers" $ do-        it "gets best block" $-            let bb =-                    withChain net $ do-                        chain net (getGenesisHeader net) 100-                        getBestBlockHeader-             in nodeHeight bb `shouldBe` 100-        it "builds a block locator" $-            let loc =-                    withChain net $ do-                        chain net (getGenesisHeader net) 100-                        bb <- getBestBlockHeader-                        blockLocatorNodes bb-                heights = map nodeHeight loc-             in heights `shouldBe` [100,99 .. 90] <> [88, 84, 76, 60, 28, 0]-        it "follows split chains" $-            let bb = withChain net $ splitChain net >> getBestBlockHeader-             in nodeHeight bb `shouldBe` 4035-    describe "block hash" $ do-        it "encodes and decodes block hash" $-            property $-            forAll arbitraryBlockHash $ \h ->-                hexToBlockHash (blockHashToHex h) == Just h-        it "from string block hash" $-            property $-            forAll arbitraryBlockHash $ \h ->-                fromString (cs $ blockHashToHex h) == h-        it "show and read block hash" $-            property $ forAll arbitraryBlockHash $ \h -> read (show h) == h-        it "json block hash" $ property $ forAll arbitraryBlockHash testID-    describe "merkle trees" $ do-        let net' = btc-        it "builds tree of right width at height 1" $ property testTreeWidth-        it "builds tree of right width at height 0" $ property testBaseWidth-        it "builds and extracts partial merkle tree" $-            property $-            forAll-                (listOf1 ((,) <$> arbitraryTxHash <*> arbitrary))-                (buildExtractTree net')-        it "merkle root test vectors" $-            mapM_ runMerkleVector merkleVectors-    describe "compact number" $ do-        it "compact number local vectors" testCompact-        it "compact number imported vectors" testCompactBitcoinCore-    describe "block serialization" $ do-        it "encodes and decodes block" $-            property $ forAll (arbitraryBlock net) cerealID-        it "encodes and decodes block JSON" $-            property $ forAll (arbitraryBlock net) testID-        it "encodes and decodes block header" $-            property $ forAll arbitraryBlockHeader cerealID-        it "encodes and decodes block header JSON" $-            property $ forAll arbitraryBlockHeader testID-        it "encodes and decodes getblocks" $-            property $ forAll arbitraryGetBlocks cerealID-        it "encodes and decodes getheaders" $-            property $ forAll arbitraryGetHeaders cerealID-        it "encodes and decdoes headers" $-            property $ forAll arbitraryHeaders cerealID-        it "encodes and decodes merkle block" $-            property $ forAll arbitraryMerkleBlock cerealID-    describe "helper functions" $ do-        it "computes bitcoin block subsidy correctly" (testSubsidy btc)-        it "computes regtest block subsidy correctly" (testSubsidy btcRegTest)---- 0 → → 2015 → → → → → → → 4031---       ↓---       → → 2035 → → → → → → 4035*---           ↓---           → → 2185-splitChain :: Network -> State HeaderMemory ()-splitChain net = do-    start <- go 1 (getGenesisHeader net) 2015-    e 2015 (head start)-    tail1 <- go 2 (nodeHeader $ head start) 2016-    e 4031 (head tail1)-    tail2 <- go 3 (nodeHeader $ head start) 20-    e 2035 (head tail2)-    tail3 <- go 4 (nodeHeader $ head tail2) 2000-    e 4035 (head tail3)-    tail4 <- go 5 (nodeHeader $ head tail2) 150-    e 2185 (head tail4)-    sp1 <- splitPoint (head tail1) (head tail3)-    unless (sp1 == head start) $-        error $-        "Split point wrong between blocks 4031 and 4035: " ++-        show (nodeHeight sp1)-    sp2 <- splitPoint (head tail4) (head tail3)-    unless (sp2 == head tail2) $-        error $-        "Split point wrong between blocks 2185 and 4035: " ++-        show (nodeHeight sp2)-  where-    e n bn =-        unless (nodeHeight bn == n) $-        error $-        "Node height " ++-        show (nodeHeight bn) ++ " of first chunk should be " ++ show n-    go seed start n = do-        let bhs = appendBlocks net seed start n-        bnE <- connectBlocks net myTime bhs-        case bnE of-            Right bn -> return bn-            Left ex  -> error ex--{- Merkle Trees -}--testTreeWidth :: Int -> Property-testTreeWidth i = i /= 0 ==> calcTreeWidth (abs i) (calcTreeHeight $ abs i) == 1--testBaseWidth :: Int -> Property-testBaseWidth i = i /= 0 ==> calcTreeWidth (abs i) 0 == abs i--buildExtractTree :: Network -> [(TxHash, Bool)] -> Bool-buildExtractTree net txs =-    r == buildMerkleRoot (map fst txs) && m == map fst (filter snd txs)-  where-    (f, h) = buildPartialMerkle txs-    (r, m) =-        fromRight (error "Could not extract matches from Merkle tree") $-        extractMatches net f h (length txs)--testCompact :: Assertion-testCompact = do-    assertEqual "vector 1" 0x05123456 (encodeCompact 0x1234560000)-    assertEqual "vector 2" (0x1234560000, False) (decodeCompact 0x05123456)-    assertEqual "vector 3" 0x0600c0de (encodeCompact 0xc0de000000)-    assertEqual "vector 4" (0xc0de000000, False) (decodeCompact 0x0600c0de)-    assertEqual "vector 5" 0x05c0de00 (encodeCompact (-0x40de000000))-    assertEqual "vector 6" (-0x40de000000, False) (decodeCompact 0x05c0de00)--testCompactBitcoinCore :: Assertion-testCompactBitcoinCore = do-    assertEqual "zero" (0, False) (decodeCompact 0x00000000)-    assertEqual-        "zero (encode · decode)"-        0x00000000-        (encodeCompact . fst $ decodeCompact 0x00000000)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x00123456)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x01003456)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x02000056)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x03000000)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x04000000)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x00923456)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x01803456)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x02800056)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x03800000)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x04800000)-    assertEqual "vector 1 (decode)" (0x12, False) (decodeCompact 0x01123456)-    assertEqual-        "vector 1 (encode · decode)"-        0x01120000-        (encodeCompact . fst $ decodeCompact 0x01123456)-    assertEqual "0x80 bit set" 0x02008000 (encodeCompact 0x80)-    assertEqual-        "vector 2 (negative) (decode)"-        (-0x7e, False)-        (decodeCompact 0x01fedcba)-    assertEqual-        "vector 2 (negative) (encode · decode)"-        0x01fe0000-        (encodeCompact . fst $ decodeCompact 0x01fedcba)-    assertEqual "vector 3 (decode)" (0x1234, False) (decodeCompact 0x02123456)-    assertEqual-        "vector 3 (encode · decode)"-        0x02123400-        (encodeCompact . fst $ decodeCompact 0x02123456)-    assertEqual "vector 4 (decode)" (0x123456, False) (decodeCompact 0x03123456)-    assertEqual-        "vector 4 (encode · decode)"-        0x03123456-        (encodeCompact . fst $ decodeCompact 0x03123456)-    assertEqual-        "vector 5 (decode)"-        (0x12345600, False)-        (decodeCompact 0x04123456)-    assertEqual-        "vector 5 (encode · decode)"-        0x04123456-        (encodeCompact . fst $ decodeCompact 0x04123456)-    assertEqual-        "vector 6 (decode)"-        (-0x12345600, False)-        (decodeCompact 0x04923456)-    assertEqual-        "vector 6 (encode · decode)"-        0x04923456-        (encodeCompact . fst $ decodeCompact 0x04923456)-    assertEqual-        "vector 7 (decode)"-        (0x92340000, False)-        (decodeCompact 0x05009234)-    assertEqual-        "vector 7 (encode · decode)"-        0x05009234-        (encodeCompact . fst $ decodeCompact 0x05009234)-    assertEqual-        "vector 8 (decode)"-        ( 0x1234560000000000000000000000000000000000000000000000000000000000-        , False)-        (decodeCompact 0x20123456)-    assertEqual-        "vector 8 (encode · decode)"-        0x20123456-        (encodeCompact . fst $ decodeCompact 0x20123456)-    assertBool "vector 9 (decode) (overflow)" (snd $ decodeCompact 0xff123456)-    assertBool-        "vector 9 (decode) (positive)"-        ((> 0) . fst $ decodeCompact 0xff123456)--runMerkleVector :: (Text, [Text]) -> Assertion-runMerkleVector (r, hs) =-    assertBool "merkle vector" $-        buildMerkleRoot (map f hs) == getTxHash (f r)-  where-    f = fromJust . hexToTxHash--merkleVectors :: [(Text, [Text])]-merkleVectors =-      -- Block 000000000000cd7e8cf6510303dde76121a1a791c15dba0be4be7022b07cf9e1-    [ ( "fb6698ac95b754256c5e71b4fbe07638cb6ca83ee67f44e181b91727f09f4b1f"-      , [ "dd96fdcfaec994bf583af650ff6022980ee0ba1686d84d0a3a2d24eabf34bc52"-        , "1bc216f786a564378710ae589916fc8e092ddfb9f24fe6c47b733550d476d5d9"-        , "a1db0b0194426064b067899ff2d975fb277fd52dbb1a38370800c76dd6503d41"-        , "d69f7fb0e668fbd437d1bf5211cc34d7eb8746f50cfddf705fe10bc2f8f7035f"-        , "5b4057cd80be7df5ed2ac42b776897ed3c26e3a01e4072075b8129c587094ef6"-        , "ed6dabcfba0ef43c50d89a8a0e4b236b1bc6585d4c3bbf49728b55f44312d6bc"-        , "056aaa9a3c635909c794e9b0acc7dccb0456c59a84c6b08417335bee4515e3d3"-        , "05bae5f1d1c874171692e1fc06f664e63eb143d3f096601ef938e4a9012eee66"-        , "b5e48e94e3f2fba197b3f591e01f47e185d7834d669529d44078e41c671aab0f"-        , "3b56aeadfc0c5484fd507bc89f13f2e5f61c42e0a4ae9062eda9a9aeef7db6a4"-        , "2affa187e1ebb94a2a86578b9f64951e854ff3d346fef259acfb6d0f5212e0d3"-        ]-      )-      -- Block 00000000000007cc4b6f07bfed72bccc1ed8dd031a93969a4c22211f784457d4-    , ( "886fea311d2dc64c315519f2d647e43998d780d2170f77e53dc0d85bf2ee680c"-      , [ "c9c9e5211512629fd111cc071d745b8c79bf486b4ea95489eb5de08b5d786b8e"-        , "20beb0ee30dfd323ade790ce9a46ae7a174f9ea44ce22a17c4d4eb23b7016f51"-        , "d4cb7dd741e78a8f57e12f6c8ddb0361ff2a5bf9365bd7d7df761060847daf9a"-        , "ddbfa6fdd29d4b47aeaadf82a4bf0a93d58cd7d8401fabf860a1ae8eeb51f42e"-        , "9d82bafe44abee248b968c86f165051c8413482c232659795335c52922dab471"-        , "86035372d31b53efd848cea7231aa9738c209aff64d3c59b1619341afb5b6ba3"-        , "11e7a7393d9658813dfaebc04fa6d4b73bac8d641bffa7067da879523d43d030"-        , "2f676b9aa5bc0ebf3395032c84c466e40cac29f80434cd1138e31c2d0fcc5c13"-        , "37567d559fbfae07fda9a90de0ce30b202128bc8ebdfef5ad2b53e865a3478c2"-        , "0b8e6c1200c454361e94e261738429e9c9b8dcffd85ec8511bbf5dc7e2e0ada8"-        ]-      )-      -- Block 00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048-    , ( "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098"-      , [ "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098" ]-      )-      -- Block 000000000004d160ac1f7b775d7c1823345aeadd5fcb29ca2ad2403bb7babd4c-    , ( "aae018650f513fc42d55b2210ec3ceeeb194fb1261d37989de07451fc0cbac5c"-      , [ "a4454f22831acd7904a9902c5070a3ee4bf4c2b13bc6b2dc66735dd3c4414028"-        , "45297f334278885108dd38a0b689ed95a4373dd3f7e4413e6aebdc2654fb771b"-        ]-      )-      -- Block 000000000001d1b13a7e86ddb20da178f20d6da5cd037a29c2a15b8b84cc774e-    , ( "ca3580505feb87544760ac14a5859659e23be05f765bbed9f86a3c9aad1a5d0c"-      , [ "60702384c6e9d34ff03c2b3e726bdc649befe603216815bd0a2974921d0d9549"-        , "11f40f58941d2a81a1616a3b84b7dd8b9d07e68750827de488c11a18f54220bb"-        , "d78e82527aa8cf16e375010bc666362c0258d3c0da1885a1871121706da8b633"-        ]-      )-      -- Block 0000000000000630a4e2266a31776e952a19b7c99a6387917d9de9032f608021-    , ( "dcce8be0a9a41e7bb726c5b49d957d90b5308e3dc5dce070ccbc8996e265a6c2"-      , [ "c0f58ff12cd1023b05f8f7035cc62bf50958ddb216a4e0eb5471deb7ef25fe81"-        , "24e5bbf9008641b8fcf3d076fef66c28c695362ba9f6a6042f8275a98414ee92"-        , "e8e1f72abad5e34dabc0f6de46a484b17a9af857d1c41de19482fadf6f7f4b27"-        , "540e4d34d9fd9e5ec02853054be7ad9260379bc23388489049cca1b0f7cf518a"-        , "324444835c5fe0545f98c4240011b75e6ea1bb76f41829e4cfbe7f75b6cee924"-        , "e7d31437ac21bceb0c222a82b2723e2b8a7654147e33397679f041537022a4b2"-        , "a8b5768d8b33525ee89d546a6a6897f8e42ba9d56a2c5e871a5d2ab40258dc95"-        , "7ba712b31bae8d45810a5cda3838c7e7fb9abd6e88bb4b3ee79be9ea2f714bb4"-        , "2ae1c4d927b06edaa626b230976ad8062bbae24da9378d1de2409da5ab08a26d"-        , "3c417dc8087d6878003624b74431e17fec9ca761389034b1b1e0f32cbfb11f4f"-        , "de6de7beae8d8c98c7d46b4409d5460e58e3204d8b4caed256c7471998595909"-        , "c7c3c211402b7c4379f7b01fadc67260ee58d11e8d0bcce3d68cb45f3467e99d"-        , "77aa2717e727a096d81074bd46ae59462692d20a1acc1a01b2535518ae5aeb53"-        , "4859a710bb673aca46208bbd59d1000ae990dafff5f70b56f0853aeeaea3948b"-        , "38deca6991988e461b83aa0d49ffef0f304c4b760371682d152eeb8c56a48174"-        , "648f4f50dada3574e2dfe2dc68956b01dd97d543859a3540bbe1ef5418d0e494"-        , "9cd7be42c2f0cd8bf38738c162cd05108e213ec7958bf2571cb627872963f5c4"-        , "6740e0dd8b97e23864af41839fc197238d2f0dbefce9a82c657556be65c465fa"-        , "f75c2e4b70db4b0aabc44b77af1ae75d305340fcf6e7b5f806ddcba4aa42b55d"-        , "e125c488636749da68e6696b97525a77146c0777c7946927e37afd513d74a4e6"-        , "c20526f119aea10880af631eba7f0b60385a22e0b0c402fe8508d41952e58be9"-        , "6456c023c7e245f5c57a168633a23f57f4fadb651115f807694a6bed14ae3b55"-        , "98b26e364e2888c9f264e4b5e13103c89608609774eb07ce933d8a2a45d19776"-        , "2efaa4f167bb65ba5684f8076cd9279fd67fd9c67388c8862809bab5542e637d"-        , "ec44eeb84d8d976d77079a822710b4dfdb11a2d9a03d8cc00bab0ae424e84666"-        , "410730d9f807d81ac48b8eafac6f1d36642c1c370241b367a35f0bac6ac7c05f"-        , "e95a7d0d477fd3db22756a3fd390a50c7bc48dc9e946fea9d24bd0866b3bb0e9"-        , "a72fec99d14939216628aaf7a0afc4c017113bcae964e777e6b508864eeaacc4"-        , "8548433310fcf75dbbc042121e8318c678e0a017534786dd322a91cebe8d213f"-        ]-      )-    ]--testID :: (FromJSON a, ToJSON a, Eq a) => a -> Bool-testID x =-    (A.decode . A.encode) (singleton ("object" :: String) x) ==-    Just (singleton ("object" :: String) x)--cerealID :: (Serialize a, Eq a) => a -> Bool-cerealID x = S.decode (S.encode x) == Right x--testSubsidy :: Network -> Assertion-testSubsidy net = go (2 * 50 * 100 * 1000 * 1000) 0-  where-    go previous_subsidy halvings = do-        let height = halvings * getHalvingInterval net-            subsidy = computeSubsidy net height-        if halvings >= 64-            then subsidy `shouldBe` 0-            else do-                subsidy `shouldBe` (previous_subsidy `div` 2)-                go subsidy (halvings + 1)
− test/Network/Haskoin/Crypto/HashSpec.hs
@@ -1,1269 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.Crypto.HashSpec (spec) where--import           Data.ByteString         (ByteString)-import           Data.Map.Strict         (singleton)-import           Data.Maybe              (fromJust)-import           Data.Serialize          as S-import           Data.String             (fromString)-import           Data.String.Conversions-import           Network.Haskoin.Block-import           Network.Haskoin.Crypto-import           Network.Haskoin.Test-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.HUnit              (Assertion, assertBool)-import           Test.QuickCheck---- Test vectors from NIST--- http://csrc.nist.gov/groups/STM/cavp/documents/drbg/drbgtestvectors.zip--- About 1/3 of HMAC DRBG SHA-256 test vectors are tested here--spec :: Spec-spec =-    describe "hash" $ do-        it "join512( split512(h) ) == h" $-            property $-            forAll arbitraryHash256 $ forAll arbitraryHash256 . joinSplit512-        it "decodeCompact . encodeCompact i == i" $ property decEncCompact-        it "from string 64-byte hash" $-            property $-            forAll arbitraryHash512 $ \h ->-                fromString (cs $ encodeHex $ encode h) == h-        it "from string 32-byte hash" $-            property $-            forAll arbitraryHash256 $ \h ->-                fromString (cs $ encodeHex $ encode h) == h-        it "from string 20-byte hash" $-            property $-            forAll arbitraryHash160 $ \h ->-                fromString (cs $ encodeHex $ encode h) == h-        it "encodes and decodes bytestring" $-            property $ forAll arbitraryBS cerealID-        it "encodes and decodes hash160" $-            property $ forAll arbitraryHash160 cerealID-        it "encodes and decodes hash256" $-            property $ forAll arbitraryHash256 cerealID-        it "encodes and decodes hash512" $-            property $ forAll arbitraryHash512 cerealID--type TestVector = [ByteString]--{--    [SHA-256]-    [PredictionResistance = False]-    [EntropyInputLen = 256]-    [NonceLen = 128]-    [PersonalizationStringLen = 0]-    [AdditionalInputLen = 0]-    [ReturnedBitsLen = 1024]--}--joinSplit512 :: Hash256 -> Hash256 -> Bool-joinSplit512 a b = split512 (join512 (a, b)) == (a, b)---- After encoding and decoding, we may loose precision so the new result is >=--- to the old one.-decEncCompact :: Integer -> Bool-decEncCompact i-    -- Integer completely fits inside the mantisse-    | abs i <= 0x007fffff = decodeCompact (encodeCompact i) == (i, False)-    -- Otherwise precision will be lost and the decoded result will-    -- be smaller than the original number-    | i >= 0              = fst (decodeCompact (encodeCompact i)) < i-    | otherwise           = fst (decodeCompact (encodeCompact i)) > i--cerealID :: (Serialize a, Eq a) => a -> Bool-cerealID x = decode (encode x) == Right x--t1 :: [TestVector]-t1 =-    [-    -- COUNT = 0-    [ "ca851911349384bffe89de1cbdc46e6831e44d34a4fb935ee285dd14b71a7488"-    , "659ba96c601dc69fc902940805ec0ca8"-    , ""-    , ""-    , ""-    , "e528e9abf2dece54d47c7e75e5fe302149f817ea9fb4bee6f4199697d04d5b89d54fbb978a15b5c443c9ec21036d2460b6f73ebad0dc2aba6e624abf07745bc107694bb7547bb0995f70de25d6b29e2d3011bb19d27676c07162c8b5ccde0668961df86803482cb37ed6d5c0bb8d50cf1f50d476aa0458bdaba806f48be9dcb8"-    ],-    -- COUNT = 1-    [ "79737479ba4e7642a221fcfd1b820b134e9e3540a35bb48ffae29c20f5418ea3"-    , "3593259c092bef4129bc2c6c9e19f343"-    , ""-    , ""-    , ""-    , "cf5ad5984f9e43917aa9087380dac46e410ddc8a7731859c84e9d0f31bd43655b924159413e2293b17610f211e09f770f172b8fb693a35b85d3b9e5e63b1dc252ac0e115002e9bedfb4b5b6fd43f33b8e0eafb2d072e1a6fee1f159df9b51e6c8da737e60d5032dd30544ec51558c6f080bdbdab1de8a939e961e06b5f1aca37"-    ],-    -- COUNT = 2-    [ "b340907445b97a8b589264de4a17c0bea11bb53ad72f9f33297f05d2879d898d"-    , "65cb27735d83c0708f72684ea58f7ee5"-    , ""-    , ""-    , ""-    , "75183aaaf3574bc68003352ad655d0e9ce9dd17552723b47fab0e84ef903694a32987eeddbdc48efd24195dbdac8a46ba2d972f5808f23a869e71343140361f58b243e62722088fe10a98e43372d252b144e00c89c215a76a121734bdc485486f65c0b16b8963524a3a70e6f38f169c12f6cbdd169dd48fe4421a235847a23ff"-    ],-    -- COUNT = 3-    [ "8e159f60060a7d6a7e6fe7c9f769c30b98acb1240b25e7ee33f1da834c0858e7"-    , "c39d35052201bdcce4e127a04f04d644"-    , ""-    , ""-    , ""-    , "62910a77213967ea93d6457e255af51fc79d49629af2fccd81840cdfbb4910991f50a477cbd29edd8a47c4fec9d141f50dfde7c4d8fcab473eff3cc2ee9e7cc90871f180777a97841597b0dd7e779eff9784b9cc33689fd7d48c0dcd341515ac8fecf5c55a6327aea8d58f97220b7462373e84e3b7417a57e80ce946d6120db5"-    ],-    -- COUNT = 4-    [ "74755f196305f7fb6689b2fe6835dc1d81484fc481a6b8087f649a1952f4df6a"-    , "c36387a544a5f2b78007651a7b74b749"-    , ""-    , ""-    , ""-    , "b2896f3af4375dab67e8062d82c1a005ef4ed119d13a9f18371b1b873774418684805fd659bfd69964f83a5cfe08667ddad672cafd16befffa9faed49865214f703951b443e6dca22edb636f3308380144b9333de4bcb0735710e4d9266786342fc53babe7bdbe3c01a3addb7f23c63ce2834729fabbd419b47beceb4a460236"-    ],-    -- COUNT = 5-    [ "4b222718f56a3260b3c2625a4cf80950b7d6c1250f170bd5c28b118abdf23b2f"-    , "7aed52d0016fcaef0b6492bc40bbe0e9"-    , ""-    , ""-    , ""-    , "a6da029b3665cd39fd50a54c553f99fed3626f4902ffe322dc51f0670dfe8742ed48415cf04bbad5ed3b23b18b7892d170a7dcf3ef8052d5717cb0c1a8b3010d9a9ea5de70ae5356249c0e098946030c46d9d3d209864539444374d8fbcae068e1d6548fa59e6562e6b2d1acbda8da0318c23752ebc9be0c1c1c5b3cf66dd967"-    ],-    -- COUNT = 6-    [ "b512633f27fb182a076917e39888ba3ff35d23c3742eb8f3c635a044163768e0"-    , "e2c39b84629a3de5c301db5643af1c21"-    , ""-    , ""-    , ""-    , "fb931d0d0194a97b48d5d4c231fdad5c61aedf1c3a55ac24983ecbf38487b1c93396c6b86ff3920cfa8c77e0146de835ea5809676e702dee6a78100da9aa43d8ec0bf5720befa71f82193205ac2ea403e8d7e0e6270b366dc4200be26afd9f63b7e79286a35c688c57cbff55ac747d4c28bb80a2b2097b3b62ea439950d75dff"-    ],-    -- COUNT = 7-    [ "aae3ffc8605a975befefcea0a7a286642bc3b95fb37bd0eb0585a4cabf8b3d1e"-    , "9504c3c0c4310c1c0746a036c91d9034"-    , ""-    , ""-    , ""-    , "2819bd3b0d216dad59ddd6c354c4518153a2b04374b07c49e64a8e4d055575dfbc9a8fcde68bd257ff1ba5c6000564b46d6dd7ecd9c5d684fd757df62d85211575d3562d7814008ab5c8bc00e7b5a649eae2318665b55d762de36eba00c2906c0e0ec8706edb493e51ca5eb4b9f015dc932f262f52a86b11c41e9a6d5b3bd431"-    ],-    -- COUNT = 8-    [ "b9475210b79b87180e746df704b3cbc7bf8424750e416a7fbb5ce3ef25a82cc6"-    , "24baf03599c10df6ef44065d715a93f7"-    , ""-    , ""-    , ""-    , "ae12d784f796183c50db5a1a283aa35ed9a2b685dacea97c596ff8c294906d1b1305ba1f80254eb062b874a8dfffa3378c809ab2869aa51a4e6a489692284a25038908a347342175c38401193b8afc498077e10522bec5c70882b7f760ea5946870bd9fc72961eedbe8bff4fd58c7cc1589bb4f369ed0d3bf26c5bbc62e0b2b2"-    ],-    -- COUNT = 9-    [ "27838eb44ceccb4e36210703ebf38f659bc39dd3277cd76b7a9bcd6bc964b628"-    , "39cfe0210db2e7b0eb52a387476e7ea1"-    , ""-    , ""-    , ""-    , "e5e72a53605d2aaa67832f97536445ab774dd9bff7f13a0d11fd27bf6593bfb52309f2d4f09d147192199ea584503181de87002f4ee085c7dc18bf32ce5315647a3708e6f404d6588c92b2dda599c131aa350d18c747b33dc8eda15cf40e95263d1231e1b4b68f8d829f86054d49cfdb1b8d96ab0465110569c8583a424a099a"-    ],-    -- COUNT = 10-    [ "d7129e4f47008ad60c9b5d081ff4ca8eb821a6e4deb91608bf4e2647835373a5"-    , "a72882773f78c2fc4878295840a53012"-    , ""-    , ""-    , ""-    , "0cbf48585c5de9183b7ff76557f8fc9ebcfdfde07e588a8641156f61b7952725bbee954f87e9b937513b16bba0f2e523d095114658e00f0f3772175acfcb3240a01de631c19c5a834c94cc58d04a6837f0d2782fa53d2f9f65178ee9c837222494c799e64c60406069bd319549b889fa00a0032dd7ba5b1cc9edbf58de82bfcd"-    ],-    -- COUNT = 11-    [ "67fe5e300c513371976c80de4b20d4473889c9f1214bce718bc32d1da3ab7532"-    , "e256d88497738a33923aa003a8d7845c"-    , ""-    , ""-    , ""-    , "b44660d64ef7bcebc7a1ab71f8407a02285c7592d755ae6766059e894f694373ed9c776c0cfc8594413eefb400ed427e158d687e28da3ecc205e0f7370fb089676bbb0fa591ec8d916c3d5f18a3eb4a417120705f3e2198154cd60648dbfcfc901242e15711cacd501b2c2826abe870ba32da785ed6f1fdc68f203d1ab43a64f"-    ],-    -- COUNT = 12-    [ "de8142541255c46d66efc6173b0fe3ffaf5936c897a3ce2e9d5835616aafa2cb"-    , "d01f9002c407127bc3297a561d89b81d"-    , ""-    , ""-    , ""-    , "64d1020929d74716446d8a4e17205d0756b5264867811aa24d0d0da8644db25d5cde474143c57d12482f6bf0f31d10af9d1da4eb6d701bdd605a8db74fb4e77f79aaa9e450afda50b18d19fae68f03db1d7b5f1738d2fdce9ad3ee9461b58ee242daf7a1d72c45c9213eca34e14810a9fca5208d5c56d8066bab1586f1513de7"-    ],-    -- COUNT = 13-    [ "4a8e0bd90bdb12f7748ad5f147b115d7385bb1b06aee7d8b76136a25d779bcb7"-    , "7f3cce4af8c8ce3c45bdf23c6b181a00"-    , ""-    , ""-    , ""-    , "320c7ca4bbeb7af977bc054f604b5086a3f237aa5501658112f3e7a33d2231f5536d2c85c1dad9d9b0bf7f619c81be4854661626839c8c10ae7fdc0c0b571be34b58d66da553676167b00e7d8e49f416aacb2926c6eb2c66ec98bffae20864cf92496db15e3b09e530b7b9648be8d3916b3c20a3a779bec7d66da63396849aaf"-    ],-    -- COUNT = 14-    [ "451ed024bc4b95f1025b14ec3616f5e42e80824541dc795a2f07500f92adc665"-    , "2f28e6ee8de5879db1eccd58c994e5f0"-    , ""-    , ""-    , ""-    , "3fb637085ab75f4e95655faae95885166a5fbb423bb03dbf0543be063bcd48799c4f05d4e522634d9275fe02e1edd920e26d9accd43709cb0d8f6e50aa54a5f3bdd618be23cf73ef736ed0ef7524b0d14d5bef8c8aec1cf1ed3e1c38a808b35e61a44078127c7cb3a8fd7addfa50fcf3ff3bc6d6bc355d5436fe9b71eb44f7fd"-    ]-    ]--{--    [SHA-256]-    [PredictionResistance = False]-    [EntropyInputLen = 256]-    [NonceLen = 128]-    [PersonalizationStringLen = 0]-    [AdditionalInputLen = 256]-    [ReturnedBitsLen = 1024]--}--t2 :: [TestVector]-t2 =-    [-    -- COUNT = 0-    [ "d3cc4d1acf3dde0c4bd2290d262337042dc632948223d3a2eaab87da44295fbd"-    , "0109b0e729f457328aa18569a9224921"-    , ""-    , "3c311848183c9a212a26f27f8c6647e40375e466a0857cc39c4e47575d53f1f6"-    , "fcb9abd19ccfbccef88c9c39bfb3dd7b1c12266c9808992e305bc3cff566e4e4"-    , "9c7b758b212cd0fcecd5daa489821712e3cdea4467b560ef5ddc24ab47749a1f1ffdbbb118f4e62fcfca3371b8fbfc5b0646b83e06bfbbab5fac30ea09ea2bc76f1ea568c9be0444b2cc90517b20ca825f2d0eccd88e7175538b85d90ab390183ca6395535d34473af6b5a5b88f5a59ee7561573337ea819da0dcc3573a22974"-    ],-    -- COUNT = 1-    [ "f97a3cfd91faa046b9e61b9493d436c4931f604b22f1081521b3419151e8ff06"-    , "11f3a7d43595357d58120bd1e2dd8aed"-    , ""-    , "517289afe444a0fe5ed1a41dbbb5eb17150079bdd31e29cf2ff30034d8268e3b"-    , "88028d29ef80b4e6f0fe12f91d7449fe75062682e89c571440c0c9b52c42a6e0"-    , "c6871cff0824fe55ea7689a52229886730450e5d362da5bf590dcf9acd67fed4cb32107df5d03969a66b1f6494fdf5d63d5b4d0d34ea7399a07d0116126d0d518c7c55ba46e12f62efc8fe28a51c9d428e6d371d7397ab319fc73ded4722e5b4f30004032a6128df5e7497ecf82ca7b0a50e867ef6728a4f509a8c859087039c"-    ],-    -- COUNT = 2-    [ "0f2f23d64f481cabec7abb01db3aabf125c3173a044b9bf26844300b69dcac8b"-    , "9a5ae13232b43aa19cfe8d7958b4b590"-    , ""-    , "ec4c7a62acab73385f567da10e892ff395a0929f959231a5628188ce0c26e818"-    , "6b97b8c6b6bb8935e676c410c17caa8042aa3145f856d0a32b641e4ae5298648"-    , "7480a361058bd9afa3db82c9d7586e42269102013f6ec5c269b6d05f17987847748684766b44918fd4b65e1648622fc0e0954178b0279dfc9fa99b66c6f53e51c4860131e9e0644287a4afe4ca8e480417e070db68008a97c3397e4b320b5d1a1d7e1d18a95cfedd7d1e74997052bf649d132deb9ec53aae7dafdab55e6dae93"-    ],-    -- COUNT = 3-    [ "53c56660c78481be9c63284e005fcc14fbc7fb27732c9bf1366d01a426765a31"-    , "dc7a14d0eb5b0b3534e717a0b3c64614"-    , ""-    , "3aa848706ecb877f5bedf4ffc332d57c22e08747a47e75cff6f0fd1316861c95"-    , "9a401afa739b8f752fddacd291e0b854f5eff4a55b515e20cb319852189d3722"-    , "5c0eb420e0bf41ce9323e815310e4e8303cd677a8a8b023f31f0d79f0ca15aeb636099a369fd074d69889865eac1b72ab3cbfebdb8cf460b00072802e2ec648b1349a5303be4ccaadd729f1a9ea17482fd026aaeb93f1602bc1404b9853adde40d6c34b844cf148bc088941ecfc1642c8c0b9778e45f3b07e06e21ee2c9e0300"-    ],-    -- COUNT = 4-    [ "f63c804404902db334c54bb298fc271a21d7acd9f770278e089775710bf4fdd7"-    , "3e45009ea9cb2a36ba1aa4bf39178200"-    , ""-    , "d165a13dc8cc43f3f0952c3f5d3de4136954d983683d4a3e6d2dc4c89bf23423"-    , "75106bc86d0336df85097f6af8e80e2da59046a03fa65b06706b8bbc7ffc6785"-    , "6363139bba32c22a0f5cd23ca6d437b5669b7d432f786b8af445471bee0b2d24c9d5f2f93717cbe00d1f010cc3b9c515fc9f7336d53d4d26ba5c0d76a90186663c8582eb739c7b6578a3328bf68dc2cec2cd89b3a90201f6993adcc854df0f5c6974d0f5570765a15fe03dbce28942dd2fd16ba2027e68abac83926969349af8"-    ],-    -- COUNT = 5-    [ "2aaca9147da66c176615726b69e3e851cc3537f5f279fe7344233d8e44cfc99d"-    , "4e171f080af9a6081bee9f183ac9e340"-    , ""-    , "d75a2a6eb66c3833e50f5ec3d2e434cf791448d618026d0c360806d120ded669"-    , "b643b74c15b37612e6577ed7ca2a4c67a78d560af9eb50a4108fca742e87b8d6"-    , "501dcdc977f4ba856f24eaa4968b374bebb3166b280334cb510232c31ebffde10fa47b7840ef3fe3b77725c2272d3a1d4219baf23e0290c622271edcced58838cf428f0517425d2e19e0d8c89377eecfc378245f283236fafa466c914b99672ceafab369e8889a0c866d8bd639db9fb797254262c6fd44cfa9045ad6340a60ef"-    ],-    -- COUNT = 6-    [ "a2e4cd48a5cf918d6f55942d95fcb4e8465cdc4f77b7c52b6fae5b16a25ca306"-    , "bef036716440db6e6d333d9d760b7ca8"-    , ""-    , "bfa591c7287f3f931168f95e38869441d1f9a11035ad8ea625bb61b9ea17591c"-    , "c00c735463bca215adc372cb892b05e939bf669583341c06d4e31d0e5b363a37"-    , "e7d136af69926a5421d4266ee0420fd729f2a4f7c295d3c966bdfa05268180b508b8a2852d1b3a06fd2ab3e13c54005123ef319f42d0c6d3a575e6e7e1496cb28aacadbcf83740fba8f35fcee04bb2ed8a51db3d3362b01094a62fb57e33c99a432f29fce6676cffbbcc05107e794e75e44a02d5e6d9d748c5fbff00a0178d65"-    ],-    -- COUNT = 7-    [ "95a67771cba69011a79776e713145d309edae56fad5fd6d41d83eaff89df6e5e"-    , "be5b5164e31ecc51ba6f7c3c5199eb33"-    , ""-    , "065f693b229a7c4fd373cd15b3807552dd9bf98c5485cef361949d4e7d774b53"-    , "9afb62406f0e812c4f156d58b19a656c904813c1b4a45a0029ae7f50731f8014"-    , "f61b61a6e79a41183e8ed6647899d2dc85cdaf5c3abf5c7f3bf37685946dc28f4923dc842f2d4326bd6ce0d50a84cb3ba869d72a36e246910eba6512ba36cd7ed3a5437c9245b00a344308c792b668b458d3c3e16dee2fbec41867da31084d46d8ec168de2148ef64fc5b72069abf5a6ada1ead2b7146bb793ff1c9c3690fa56"-    ],-    -- COUNT = 8-    [ "a459e1815cbca4514ec8094d5ab2414a557ba6fe10e613c345338d0521e4bf90"-    , "62221392e2552e76cd0d36df6e6068eb"-    , ""-    , "0a3642b02b23b3ef62c701a63401124022f5b896de86dab6e6c7451497aa1dcc"-    , "c80514865901371c45ba92d9f95d50bb7c9dd1768cb3dfbc45b968da94965c6e"-    , "464e6977b8adaef307c9623e41c357013249c9ffd77f405f3925cebb69f151ce8fbb6a277164002aee7858fc224f6499042aa1e6322deee9a5d133c31d640e12a7487c731ba03ad866a24675badb1d79220c40be689f79c2a0be93cb4dada3e0eac4ab140cb91998b6f11953e68f2319b050c40f71c34de9905ae41b2de1c2f6"-    ],-    -- COUNT = 9-    [ "252c2cad613e002478162861880979ee4e323025eebb6fb2e0aa9f200e28e0a1"-    , "d001bc9a8f2c8c242e4369df0c191989"-    , ""-    , "9bcfc61cb2bc000034bb3db980eb47c76fb5ecdd40553eff113368d639b947fd"-    , "8b0565c767c2610ee0014582e9fbecb96e173005b60e9581503a6dca5637a26e"-    , "e96c15fe8a60692b0a7d67171e0195ff6e1c87aab844221e71700d1bbee75feea695f6a740c9760bbe0e812ecf4061d8f0955bc0195e18c4fd1516ebca50ba6a6db86881737dbab8321707675479b87611db6af2c97ea361a5484555ead454defb1a64335de964fc803d40f3a6f057893d2afc25725754f4f00abc51920743dc"-    ],-    -- COUNT = 10-    [ "8be0ca6adc8b3870c9d69d6021bc1f1d8eb9e649073d35ee6c5aa0b7e56ad8a5"-    , "9d1265f7d51fdb65377f1e6edd6ae0e4"-    , ""-    , "da86167ac997c406bb7979f423986a84ec6614d6caa7afc10aff0699a9b2cf7f"-    , "e4baa3c555950b53e2bfdba480cb4c94b59381bac1e33947e0c22e838a9534cf"-    , "64384ecc4ea6b458efc227ca697eac5510092265520c0a0d8a0ccf9ed3ca9d58074671188c6a7ad16d0b050cdc072c125d7298d3a31d9f044a9ee40da0089a84fea28cc7f05f1716db952fad29a0e779635cb7a912a959be67be2f0a4170aace2981802e2ff6467e5b46f0ffbff3b42ba5935fd553c82482ac266acf1cd247d7"-    ],-    -- COUNT = 11-    [ "d43a75b6adf26d60322284cb12ac38327792442aa8f040f60a2f331b33ac4a8f"-    , "0682f8b091f811afacaacaec9b04d279"-    , ""-    , "7fd3b8f512940da7de5d80199d9a7b42670c04a945775a3dba869546cbb9bc65"-    , "2575db20bc7aafc2a90a5dabab760db851d754777bc9f05616af1858b24ff3da"-    , "0da7a8dc73c163014bf0841913d3067806456bbca6d5de92b85534c6545467313648d71ef17c923d090dc92cff8d4d1a9a2bb63e001dc2e8ab1a597999be3d6cf70ff63fee9985801395fbd4f4990430c4259fcae4fa1fcd73dc3187ccc102d04af7c07532885e5a226fc42809c48f22eecf4f6ab996ae4fcb144786957d9f41"-    ],-    -- COUNT = 12-    [ "64352f236af5d32067a529a8fd05ba00a338c9de306371a0b00c36e610a48d18"-    , "df99ed2c7608c870624b962a5dc68acd"-    , ""-    , "da416335e7aaf60cf3d06fb438735ce796aad09034f8969c8f8c3f81e32fef24"-    , "a28c07c21a2297311adf172c19e83ca0a87731bdffb80548978d2d1cd82cf8a3"-    , "132b9f25868729e3853d3c51f99a3b5fae6d4204bea70890daf62e042b776a526c8fb831b80a6d5d3f153237df1fd39b6fd9137963f5516d9cdd4e3f9195c46e9972c15d3edc6606e3368bde1594977fb88d0ca6e6f5f3d057ccadc7d7dab77dfc42658a1e972aa446b20d418286386a52dfc1c714d2ac548713268b0b709729"-    ],-    -- COUNT = 13-    [ "282f4d2e05a2cd30e9087f5633089389449f04bac11df718c90bb351cd3653a5"-    , "90a7daf3c0de9ea286081efc4a684dfb"-    , ""-    , "2630b4ccc7271cc379cb580b0aaede3d3aa8c1c7ba002cf791f0752c3d739007"-    , "c31d69de499f1017be44e3d4fa77ecebc6a9b9934749fcf136f267b29115d2cc"-    , "c899094520e0197c37b91dd50778e20a5b950decfb308d39f1db709447ae48f6101d9abe63a783fbb830eec1d359a5f61a2013728966d349213ee96382614aa4135058a967627183810c6622a2158cababe3b8ab99169c89e362108bf5955b4ffc47440f87e4bad0d36bc738e737e072e64d8842e7619f1be0af1141f05afe2d"-    ],-    -- COUNT = 14-    [ "13c752b9e745ce77bbc7c0dbda982313d3fe66f903e83ebd8dbe4ff0c11380e9"-    , "f1a533095d6174164bd7c82532464ae7"-    , ""-    , "4f53db89b9ba7fc00767bc751fb8f3c103fe0f76acd6d5c7891ab15b2b7cf67c"-    , "582c2a7d34679088cca6bd28723c99aac07db46c332dc0153d1673256903b446"-    , "6311f4c0c4cd1f86bd48349abb9eb930d4f63df5e5f7217d1d1b91a71d8a6938b0ad2b3e897bd7e3d8703db125fab30e03464fad41e5ddf5bf9aeeb5161b244468cfb26a9d956931a5412c97d64188b0da1bd907819c686f39af82e91cfeef0cbffb5d1e229e383bed26d06412988640706815a6e820796876f416653e464961"-    ]-    ]--{--    [SHA-256]-    [PredictionResistance = False]-    [EntropyInputLen = 256]-    [NonceLen = 128]-    [PersonalizationStringLen = 256]-    [AdditionalInputLen = 0]-    [ReturnedBitsLen = 1024]--}--t3 :: [TestVector]-t3 =-    [-    -- COUNT = 0-    [ "5cacc68165a2e2ee20812f35ec73a79dbf30fd475476ac0c44fc6174cdac2b55"-    , "6f885496c1e63af620becd9e71ecb824"-    , "e72dd8590d4ed5295515c35ed6199e9d211b8f069b3058caa6670b96ef1208d0"-    , ""-    , ""-    , "f1012cf543f94533df27fedfbf58e5b79a3dc517a9c402bdbfc9a0c0f721f9d53faf4aafdc4b8f7a1b580fcaa52338d4bd95f58966a243cdcd3f446ed4bc546d9f607b190dd69954450d16cd0e2d6437067d8b44d19a6af7a7cfa8794e5fbd728e8fb2f2e8db5dd4ff1aa275f35886098e80ff844886060da8b1e7137846b23b"-    ],-    -- COUNT = 1-    [ "8df013b4d103523073917ddf6a869793059e9943fc8654549e7ab22f7c29f122"-    , "da2625af2ddd4abcce3cf4fa4659d84e"-    , "b571e66d7c338bc07b76ad3757bb2f9452bf7e07437ae8581ce7bc7c3ac651a9"-    , ""-    , ""-    , "b91cba4cc84fa25df8610b81b641402768a2097234932e37d590b1154cbd23f97452e310e291c45146147f0da2d81761fe90fba64f94419c0f662b28c1ed94da487bb7e73eec798fbcf981b791d1be4f177a8907aa3c401643a5b62b87b89d66b3a60e40d4a8e4e9d82af6d2700e6f535cdb51f75c321729103741030ccc3a56"-    ],-    -- COUNT = 2-    [ "565b2b77937ba46536b0f693b3d5e4a8a24563f9ef1f676e8b5b2ef17823832f"-    , "4ef3064ec29f5b7f9686d75a23d170e3"-    , "3b722433226c9dba745087270ab3af2c909425ba6d39f5ce46f07256068319d9"-    , ""-    , ""-    , "d144ee7f8363d128872f82c15663fe658413cd42651098e0a7c51a970de75287ec943f9061e902280a5a9e183a7817a44222d198fbfab184881431b4adf35d3d1019da5a90b3696b2349c8fba15a56d0f9d010a88e3f9eeedb67a69bcaa71281b41afa11af576b765e66858f0eb2e4ec4081609ec81da81df0a0eb06787340ea"-    ],-    -- COUNT = 3-    [ "fc3832a91b1dcdcaa944f2d93cbceb85c267c491b7b59d017cde4add79a836b6"-    , "d5e76ce9eabafed06e33a913e395c5e0"-    , "ffc5f6eefd51da64a0f67b5f0cf60d7ab43fc7836bca650022a0cee57a43c148"-    , ""-    , ""-    , "0e713c6cc9a4dbd4249201d12b7bf5c69c3e18eb504bf3252db2f43675e17d99b6a908400cea304011c2e54166dae1f20260008efe4e06a87e0ce525ca482bca223a902a14adcf2374a739a5dfeaf14cadd72efa4d55d15154c974d9521535bcb70658c5b6c944020afb04a87b223b4b8e5d89821704a9985bb010405ba8f3d4"-    ],-    -- COUNT = 4-    [ "8009eb2cb49fdf16403bcdfd4a9f952191062acb9cc111eca019f957fb9f4451"-    , "355598866952394b1eddd85d59f81c9d"-    , "09ff1d4b97d83b223d002e05f754be480d13ba968e5aac306d71cc9fc49cc2dd"-    , ""-    , ""-    , "9550903c2f02cf77c8f9c9a37041d0040ee1e3ef65ba1a1fbbcf44fb7a2172bd6b3aaabe850281c3a1778277bacd09614dfefececac64338ae24a1bf150cbf9d9541173a82ecba08aa19b75abb779eb10efa4257d5252e8afcac414bc3bb5d3006b6f36fb9daea4c8c359ef6cdbeff27c1068571dd3c89dc87eda9190086888d"-    ],-    -- COUNT = 5-    [ "a6e4c9a8bd6da23b9c2b10a7748fd08c4f782fadbac7ea501c17efdc6f6087bd"-    , "acdc47edf1d3b21d0aec7631abb6d7d5"-    , "c16ee0908a5886dccf332fbc61de9ec7b7972d2c4c83c477409ce8a15c623294"-    , ""-    , ""-    , "a52f93ccb363e2bdf0903622c3caedb7cffd04b726052b8d455744c71b76dee1b71db9880dc3c21850489cb29e412d7d80849cfa9151a151dcbf32a32b4a54cac01d3200200ed66a3a5e5c131a49655ffbf1a8824ff7f265690dffb4054df46a707b9213924c631c5bce379944c856c4f7846e281ac89c64fad3a49909dfb92b"-    ],-    -- COUNT = 6-    [ "59d6307460a9bdd392dfc0904973991d585696010a71e52d590a5039b4849fa4"-    , "34a0aafb95917cbf8c38fc5548373c05"-    , "0407b7c57bc11361747c3d67526c36e228028a5d0b145d66ab9a2fe4b07507a0"-    , ""-    , ""-    , "299aba0661315211b09d2861855d0b4b125ab24649461341af6abd903ed6f025223b3299f2126fcad44c675166d800619cf49540946b12138989417904324b0ddad121327211a297f11259c9c34ce4c70c322a653675f78d385e4e2443f8058d141195e17e0bd1b9d44bf3e48c376e6eb44ef020b11cf03eb141c46ecb43cf3d"-    ],-    -- COUNT = 7-    [ "9ae3506aadbc8358696ba1ba17e876e1157b7048235921503d36d9211b430342"-    , "9abf7d66afee5d2b811cba358bbc527d"-    , "0d645f6238e9ceb038e4af9772426ca110c5be052f8673b8b5a65c4e53d2f519"-    , ""-    , ""-    , "5f032c7fec6320fe423b6f38085cbad59d826085afe915247b3d546c4c6b174554dd4877c0d671de9554b505393a44e71f209b70f991ac8aa6e08f983fff2a4c817b0cd26c12b2c929378506489a75b2025b358cb5d0400821e7e252ac6376cd94a40c911a7ed8b6087e3de5fa39fa6b314c3ba1c593b864ce4ff281a97c325b"-    ],-    -- COUNT = 8-    [ "96ae3b8775b36da2a29b889ad878941f43c7d51295d47440cd0e3c4999193109"-    , "1fe022a6fc0237b055d4d6a7036b18d5"-    , "1e40e97362d0a823d3964c26b81ab53825c56446c5261689011886f19b08e5c2"-    , ""-    , ""-    , "e707cd14b06ce1e6dbcceaedbf08d88891b03f44ad6a797bd12fdeb557d0151df9346a028dec004844ca46adec3051dafb345895fa9f4604d8a13c8ff66ae093fa63c4d9c0816d55a0066d31e8404c841e87b6b2c7b5ae9d7afb6840c2f7b441bf2d3d8bd3f40349c1c014347c1979213c76103e0bece26ad7720601eff42275"-    ],-    -- COUNT = 9-    [ "33f5120396336e51ee3b0b619b5f873db05ca57cda86aeae2964f51480d14992"-    , "6f1f6e9807ba5393edcf3cb4e4bb6113"-    , "3709605af44d90196867c927512aa8ba31837063337b4879408d91a05c8efa9f"-    , ""-    , ""-    , "8b8291126ded9acef12516025c99ccce225d844308b584b872c903c7bc6467599a1cead003dc4c70f6d519f5b51ce0da57f53da90dbe8f666a1a1dde297727fee2d44cebd1301fc1ca75956a3fcae0d374e0df6009b668fd21638d2b733e6902d22d5bfb4af1b455975e08eef0ebe4dc87705801e7776583c8de11672729f723"-    ],-    -- COUNT = 10-    [ "ad300b799005f290fee7f930eebce158b98fb6cb449987fe433f955456b35300"-    , "06aa2514e4bd114edf7ac105cfef2772"-    , "87ada711465e4169da2a74c931afb9b5a5b190d07b7af342aa99570401c3ee8a"-    , ""-    , ""-    , "80d7c606ff49415a3a92ba1f2943235c01339c8f9cd0b0511fbfdf3ef23c42ffff008524193faaa4b7f2f2eb0cfa221d9df89bd373fe4e158ec06fad3ecf1eb48b8239b0bb826ee69d773883a3e8edac66254610ff70b6609836860e39ea1f3bfa04596fee1f2baca6cebb244774c6c3eb4af1f02899eba8f4188f91776de16f"-    ],-    -- COUNT = 11-    [ "130b044e2c15ab89375e54b72e7baae6d4cad734b013a090f4df057e634f6ff0"-    , "65fd6ac602cd44107d705dbc066e52b6"-    , "f374aba16f34d54aae5e494505b67d3818ef1c08ea24967a76876d4361379aec"-    , ""-    , ""-    , "5d179534fb0dba3526993ed8e27ec9f915183d967336bb24352c67f4ab5d7935d3168e57008da851515efbaecb69904b6d899d3bfa6e9805659aef2942c4903875b8fcbc0d1d24d1c075f0ff667c1fc240d8b410dff582fa71fa30878955ce2ed786ef32ef852706e62439b69921f26e84e0f54f62b938f04905f05fcd7c2204"-    ],-    -- COUNT = 12-    [ "716430e999964b35459c17921fe5f60e09bd9ab234cb8f4ba4932bec4a60a1d5"-    , "9533b711e061b07d505da707cafbca03"-    , "372ae616d1a1fc45c5aecad0939c49b9e01c93bfb40c835eebd837af747f079d"-    , ""-    , ""-    , "a80d6a1b2d0ce01fe0d26e70fb73da20d45841cf01bfbd50b90d2751a46114c0e758cb787d281a0a9cf62f5c8ce2ee7ca74fefff330efe74926acca6d6f0646e4e3c1a1e52fce1d57b88beda4a5815896f25f38a652cc240deb582921c8b1d03a1da966dd04c2e7eee274df2cd1837096b9f7a0d89a82434076bc30173229a60"-    ],-    -- COUNT = 13-    [ "7679f154296e6d580854826539003a82d1c54e2e062c619d00da6c6ac820789b"-    , "55d12941b0896462e7d888e5322a99a3"-    , "ba4d1ed696f58ef64596c76cee87cc1ca83069a79e7982b9a06f9d62f4209faf"-    , ""-    , ""-    , "10dc7cd2bb68c2c28f76d1b04ae2aa287071e04c3b688e1986b05cc1209f691daa55868ebb05b633c75a40a32b49663185fe5bb8f906008347ef51590530948b87613920014802e5864e0758f012e1eae31f0c4c031ef823aecfb2f8a73aaa946fc507037f9050b277bdeaa023123f9d22da1606e82cb7e56de34bf009eccb46"-    ],-    -- COUNT = 14-    [ "8ca4a964e1ff68753db86753d09222e09b888b500be46f2a3830afa9172a1d6d"-    , "a59394e0af764e2f21cf751f623ffa6c"-    , "eb8164b3bf6c1750a8de8528af16cffdf400856d82260acd5958894a98afeed5"-    , ""-    , ""-    , "fc5701b508f0264f4fdb88414768e1afb0a5b445400dcfdeddd0eba67b4fea8c056d79a69fd050759fb3d626b29adb8438326fd583f1ba0475ce7707bd294ab01743d077605866425b1cbd0f6c7bba972b30fbe9fce0a719b044fcc1394354895a9f8304a2b5101909808ddfdf66df6237142b6566588e4e1e8949b90c27fc1f"-    ]-    ]--{--    [SHA-256]-    [PredictionResistance = False]-    [EntropyInputLen = 256]-    [NonceLen = 128]-    [PersonalizationStringLen = 256]-    [AdditionalInputLen = 256]-    [ReturnedBitsLen = 1024]--}--t4 :: [TestVector]-t4 =-    [-    -- COUNT = 0-    [ "5d3286bc53a258a53ba781e2c4dcd79a790e43bbe0e89fb3eed39086be34174b"-    , "c5422294b7318952ace7055ab7570abf"-    , "2dba094d008e150d51c4135bb2f03dcde9cbf3468a12908a1b025c120c985b9d"-    , "793a7ef8f6f0482beac542bb785c10f8b7b406a4de92667ab168ecc2cf7573c6"-    , "2238cdb4e23d629fe0c2a83dd8d5144ce1a6229ef41dabe2a99ff722e510b530"-    , "d04678198ae7e1aeb435b45291458ffde0891560748b43330eaf866b5a6385e74c6fa5a5a44bdb284d436e98d244018d6acedcdfa2e9f499d8089e4db86ae89a6ab2d19cb705e2f048f97fb597f04106a1fa6a1416ad3d859118e079a0c319eb95686f4cbcce3b5101c7a0b010ef029c4ef6d06cdfac97efb9773891688c37cf"-    ],-    -- COUNT = 1-    [ "c2a566a9a1817b15c5c3b778177ac87c24e797be0a845f11c2fe399dd37732f2"-    , "cb1894eb2b97b3c56e628329516f86ec"-    , "13ce4d8dd2db9796f94156c8e8f0769b0aa1c82c1323b61536603bca37c9ee29"-    , "413dd83fe56835abd478cb9693d67635901c40239a266462d3133b83e49c820b"-    , "d5c4a71f9d6d95a1bedf0bd2247c277d1f84a4e57a4a8825b82a2d097de63ef1"-    , "b3a3698d777699a0dd9fa3f0a9fa57832d3cefac5df24437c6d73a0fe41040f1729038aef1e926352ea59de120bfb7b073183a34106efed6278ff8ad844ba0448115dfddf3319a82de6bb11d80bd871a9acd35c73645e1270fb9fe4fa88ec0e465409ea0cba809fe2f45e04943a2e396bbb7dd2f4e0795303524cc9cc5ea54a1"-    ],-    -- COUNT = 2-    [ "a33288a96f41dd54b945e060c8bd0c094f1e28267cc1dcbba52063c1a9d54c4d"-    , "36918c977e1a7276a2bb475591c367b7"-    , "6aa528c940962638dc2201738850fd1fe6f5d0eb9f687ff1af39d9c7b36830d9"-    , "37ee633a635e43af59abdb1762c7ea45bfe060ec1d9077ecd2a43a658673f3c7"-    , "2eb96f2e28fa9f674bb03ade703b8f791ee5356e2ee85c7ed5bda96325256c61"-    , "db2f91932767eb846961ce5321c7003431870508e8c6f8d432ca1f9cee5cdc1aed6e0f133d317eb6990c4b3b0a360cdfb5b43a6e712bd46bca04c414868fab22c6a49c4b89c812697c3a7fbfc8ddf10c8aa5ebf13a09fd114eb2a02a07f69786f3ce7fd30231f22779bc8db103b13fa546dbc45a89a86275281172761683d384"-    ],-    -- COUNT = 3-    [ "5f37b6e47e1776e735adc03d4b999879477ff4a206231924033d94c0114f911b"-    , "7d12d62c79c9f6234ae0314156947459"-    , "92d4d9fab5f8bf5119f2663a9df7334f50dcde74fb9d7732f7eba56501e60d54"-    , "c9aef0d7a9ba7345d08b6d5b5ce5645c7495b8685e6b93846ffcf470f5abd40d"-    , "50d9d1f5074f7d9f1a24a9c63aa47b94da5ba78db1b0f18e4d4fe45c6875813c"-    , "20d942bbd7d98700faa37e94d53bf74f2d6bd1d8c95c0b88d842c4857797d59e7c8788aeeac29740122f208f703bf35dc32b0035db0648384feb6aa17a3274bc09b2d2b746c5a06fd82f4469fb86131a49482cb7be7d9b4b95042394cfb18b13f333ec0fe5c227bf1d8f33ecb2e42e358b6c3e034cb585331bd1d27f638029b9"-    ],-    -- COUNT = 4-    [ "2311c5afd64c584484b2729e84db80c0b4063fe9ca7edc83350488d7e67264a0"-    , "6a6dfd975a0dc7b72df1f107c4b3b3a6"-    , "2abd870ec5fe26ed14dfa57a3309f920131b70580c3639af2645cd1af93db1b1"-    , "c6e532a3b25653b6002aed5269cc2118749306e736bde039d4d569d4f967773f"-    , "5e7d26c4da769c373092b2b4f72b109fe34bdb7d169ea38f78ebae5df4a15759"-    , "cacaeb1b4ac2305d8714eb50cbe1c67c5a2c0bbc7938fdfdcafef7c85fc40becbf777a4cfb6f14c6eee320943a493d2b0a744a6eb3c256ee9a3763037437df9adce3e2260f0c35e958af0edb5a81debd8bdaf2b8bb2b98b9186e5a222a21609ff58df4cbe1d4898d10d6e7c46f31f5cb1041bfd83a5fb27d5c56c961e91403fc"-    ],-    -- COUNT = 5-    [ "362ece9d330e1172a8f9e50258476d0c79c3ee50346524ba12d970ee3a6ef8c5"-    , "cf11bcb4d9d51311ceacfca8705e833f"-    , "abb5a8edde02e526449284ecc31bc713383df3ed085f752e3b6a32f305861eed"-    , "746302ab1f4a86b17546bea762e929360f2e95c7788a63545a264ef997c8c65e"-    , "b907c5b2a8833a48e56e819228ce9a050b41b3309f5ca37bed720311d92b33af"-    , "73c7131a558350590053580873ef956ff952f2aa6ff1bea452e013d1bc2afddea2311756dbe756e63ba6258480c48f3f6c1319b5f572f67ca530af09e39413d1d432bea8f89206619618cb0e7c88e9f2033639d0eb0efc20616b64f940da99b88231984c3fb23f19e890576f555fde394dbd4351f17a7ffd5c369379001bda03"-    ],-    -- COUNT = 6-    [ "cf614bc29946bc0095f415e8bdeda10aab05392f9cc9187a86ea6ec95ee422e1"-    , "77fb5ec22dc0432cc13f4693e2e3bd9a"-    , "e4ce77914ffbc5fddf1fb51edfafdc196109139b84c741354135ec8d314c7c43"-    , "e1e83ee1205acaf6164dc287aec08e5b32789e5be818078db39e53cad589db51"-    , "4e20c0226d5e1e7e805679f03f72452b5bea2d0ba41e0c12329bf60eb3016dd1"-    , "838fdf1418a746aa52ae4005d90c3fd301f648c5770ffef2a9f3912e37a93850cc4b8bfcce910aead0cb75958823b1a62e283901c5e4a3980e4ea36257458e2e4953555819b8852a26489b1d74821f80c9908469b43f124ff7ea62497c36159a47353098a1b9ec32e54800d6704371cc37f357ad74aacc203e9b6db97f94d0c4"-    ],-    -- COUNT = 7-    [ "a8da1d3e233f393fd44d204c200202f7d01896e72c5ac652940cfd15b5d4b0bd"-    , "0a112b4cb0890af0a495e0f49fcf6874"-    , "d2e32799bc822b8d033299bdf63dc35774f7649e935d25be5b10512c430d1bda"-    , "920a82d76fcd2cd106ada64bba232b7b2344f3afe6b1d1d20ee8795144571009"-    , "eeaac5878275372025f8231febed64db6a11273c3c00d625fc80a95f18ad7d3f"-    , "5f6dae489b53d89027b2cc333c700f090152d77b3eaf01d47f56ce6eca9893ef877b4cb560fab0fbdb34e3d1c6cd8480b33c053d2661a10aa531df4961b97d659c7492584236582b3fe701055efa59c328194cd1e07fcffd910d9ee01b7b9e8c8fda7f7ac01a8e203b8b26eb8078a9b9a5021562c44af24089e3ef84c1d5a6bd"-    ],-    -- COUNT = 8-    [ "a77b1ed4ecaa650374e1052c405f1d88881c25c87d13dbe1334d8c1a847fa76b"-    , "05c143e2f145db216fe7be9ed23635d0"-    , "b5c750968ff09ed251d4a1c05342ac843db5246b19045728a634fa4f6e752e54"-    , "ff5937bcd01a363696bf8e40adc8e4ab3e56dbf7e7d09451c99e538785fe6697"-    , "4acb34eea8266badcf8f6557a0eecf3eb4d7a295c876d6175598cb66a388efb8"-    , "ec13eadfcc84e77d2a2efa1a2cd8b1355587cb27feb3d19d75b37f0446333ddb8236e751c63b7a6e595ec24a25051a696dbe8c062dd8896d1446db228a2f10e8094ee07e7ee648ed6bebb2f5ec5aae24c9c640665c28355cc11c116795ecc070790f7fdfc4398900311b6695d5da0175091ed1828d2731085bfb4a20bd86cce0"-    ],-    -- COUNT = 9-    [ "491686c781e83eb4e21d9989e8d718100b0d21a2c56295888baef1a65f219651"-    , "499085296d21065feabf3106101c8d6f"-    , "d208a72f9ae34f0817669fb04f49239dd31700f3dc9a93db8d75fb79f9b686c1"-    , "9ffc61893a293a864008fdd56d3292600d9e2ec8a1ea8f34ac5931e968905a23"-    , "4ff3a397dfdae0912032a302a5e7a07dceca8d9013a21545689319b7c024cd07"-    , "3c258ebf2203fca3b322ad1b016e21c7f5c148425f81e4fb0a0e462dce9dfa569c37a006527768297a5b68461b08912642a341b88c85597e30e7561206886098c4e2d861f11513f0ffdbbc78d3a2dd60c105abbb33c5e05ae27081b690fb8b3610917aa9bf1a4ad74481b5ff8334f14e5ad6a6a1eb2259476078076fb7e3a992"-    ],-    -- COUNT = 10-    [ "36a5267eeeb5a1a7d46de0f8f9281f73cd9611f01198fdaa78c5315205e5a177"-    , "b66b5337970df36219321badacc624eb"-    , "c2a7b164949da102bece44a423197682ff97627d1fe9654266b8527f64e5b386"-    , "a977e2d8637b019c74063d163bb25387dc56f4eb40e502cefc5ae6ad26a6abdc"-    , "c5c9819557b1e7d8a86fa8c60be42993edc3ef539c13d9a51fb64b0de06e145e"-    , "b471711a4fc7ab7247e65d2c2fe49a50169187187b7978cd2fdb0f8318be3ec55fc68ed4577ad9b42cbb57100b5d35ac86c244c4c93a5b28c1a11c2dfe905d608ec7804dec5bb15cf8d79695534d5e13a6a7e18a887ec9cf184da0cbbc6267f3a952a769403bafcdbb559401be0d8b3300ea7258b4026fc892175efd55ba1a67"-    ],-    -- COUNT = 11-    [ "a76b0366df89e4073a6b6b9c04da1d6817ce26f1c4825cad4097bdf4d7b9445e"-    , "773d3cc3290176773847869be528d1a4"-    , "1bfd3bcfb9287a5ad055d1b2b8615fa81c94ac24bc1c219a0f8de58789e0404a"-    , "edd879fa56f21d93029da875b683ce50f6fdc4c0da41da051d000eed2afefefa"-    , "f528ffd29160039260133ed9654589ce60e39e7f667c34f82cda65ddcf5fff14"-    , "39d1ff8848e74dd2cdc6b818ad69823878062116fdf1679942f892c7e191be1c4b6ea268ecdff001b22af0d510f30c2c25b90fc34927f46e3f45d36b0e1848b3a5d54c36c7c65ee7287d325dfbb51b56a438feb6650ce13df88bf06b87ac4a35d2a199ea888629fb0d83f82f0ea160dc79ed220d8ef195b9e80c542f60c2d320"-    ],-    -- COUNT = 12-    [ "46571e1df43e5e141235e2a9ec85bb0faf1dc0566031e14d41a2fbd0315653ec"-    , "b60ef6a3347967519aabeaf748e4e991"-    , "759fd8593e3688b23c4a003b655311770d670789878570eb3b155a8e6c2d8c45"-    , "033128460b449e1accb0e9c54508759ddc2538bc64b51e6277553f0c60a02723"-    , "a5e4a717240bdeac18a0c0e231a11dc04a47d7550f342fa9a7a5ff334eb9327d"-    , "9d222df1d530ea7f8f2297a0c79d637da570b48042ecddded75956bba0f0e70b271ffa3c9a53bada6ee1b8a4203c22bfde82a5e2eb1b150f54c6483458569422c1a34a8997d42cc09750167a78bf52a0bd158397af9f83caabe689185c099bf0a9a4853dd3cf8b8e89efebb6a27dba873e65e9927741b22968f2875789b44e01"-    ],-    -- COUNT = 13-    [ "d63980e63bbe4ac08d2ac5646bf085b82c75995e3fdfc23bb9cc734cd85ca7d2"-    , "d33ed1dcae13fb634ba08272d6697590"-    , "acd0da070072a5340c4f5f4395568e1a36374e074196ae87f3692ee40487e1df"-    , "f567677b5e12e26f3544be3da9314c88fc475bf84804a89a51f12b191392c02b"-    , "c01cc7873e93c86e2bfb8fc984cfc2eab5cc58eeef018fedb5cba5aedd386156"-    , "b133446f633bcb40724bbf9fa187c39a44b9c094a0a0d40e98977e5466dc2c9adf62a5f4551eeb6406a14658de8a0ed7487c3bf6277e811101284a941745ce16176acc875f1435e14161772fa84609e8123c53dd03cbb868030835c0d11d8d6aa04a1b6f908248b028997737f54735ec4ed7a81fc868199ffb61a779d9340334"-    ],-    -- COUNT = 14-    [ "3d99f9b7ac3a2fbe9cf15d960bf41f5588fc4db1e0d2a5c9c0fe9059f03593fb"-    , "411f504bb63a9b3afa7ffa1357bb48be"-    , "0bb5ebd55981a25ba69164da49fa92f2871fd3fc65eb30d0f0d0b8d798a4f8f2"-    , "288e948a551284eb3cb23e26299955c2fb8f063c132a92683c1615ecaed80f30"-    , "d975b22f79e34acf5db25a2a167ef60a10682dd9964e15533d75f7fa9efc5dcb"-    , "ee8d707eea9bc7080d58768c8c64a991606bb808600cafab834db8bc884f866941b4a7eb8d0334d876c0f1151bccc7ce8970593dad0c1809075ce6dbca54c4d4667227331eeac97f83ccb76901762f153c5e8562a8ccf12c8a1f2f480ec6f1975ac097a49770219107d4edea54fb5ee23a8403874929d073d7ef0526a647011a"-    ]-    ]--{- Reseed test vectors -}--{--    [SHA-256]-    [PredictionResistance = False]-    [EntropyInputLen = 256]-    [NonceLen = 128]-    [PersonalizationStringLen = 0]-    [AdditionalInputLen = 0]-    [ReturnedBitsLen = 1024]--}--r1 :: [TestVector]-r1 =-    [-    -- COUNT = 0-    [ "06032cd5eed33f39265f49ecb142c511da9aff2af71203bffaf34a9ca5bd9c0d"-    , "0e66f71edc43e42a45ad3c6fc6cdc4df"-    , ""-    , "01920a4e669ed3a85ae8a33b35a74ad7fb2a6bb4cf395ce00334a9c9a5a5d552"-    , ""-    , ""-    , ""-    , "76fc79fe9b50beccc991a11b5635783a83536add03c157fb30645e611c2898bb2b1bc215000209208cd506cb28da2a51bdb03826aaf2bd2335d576d519160842e7158ad0949d1a9ec3e66ea1b1a064b005de914eac2e9d4f2d72a8616a80225422918250ff66a41bd2f864a6a38cc5b6499dc43f7f2bd09e1e0f8f5885935124"-    ],-    -- COUNT = 1-    [ "aadcf337788bb8ac01976640726bc51635d417777fe6939eded9ccc8a378c76a"-    , "9ccc9d80c89ac55a8cfe0f99942f5a4d"-    , ""-    , "03a57792547e0c98ea1776e4ba80c007346296a56a270a35fd9ea2845c7e81e2"-    , ""-    , ""-    , ""-    , "17d09f40a43771f4a2f0db327df637dea972bfff30c98ebc8842dc7a9e3d681c61902f71bffaf5093607fbfba9674a70d048e562ee88f027f630a78522ec6f706bb44ae130e05c8d7eac668bf6980d99b4c0242946452399cb032cc6f9fd96284709bd2fa565b9eb9f2004be6c9ea9ff9128c3f93b60dc30c5fc8587a10de68c"-    ],-    -- COUNT = 2-    [ "62cda441dd802c7652c00b99cac3652a64fc75388dc9adcf763530ac31df9214"-    , "5fdc897a0c1c482204ef07e0805c014b"-    , ""-    , "bd9bbf717467bf4b5db2aa344dd0d90997c8201b2265f4451270128f5ac05a1a"-    , ""-    , ""-    , ""-    , "7e41f9647a5e6750eb8acf13a02f23f3be77611e51992cedb6602c314531aff2a6e4c557da0777d4e85faefcb143f1a92e0dbac8de8b885ced62a124f0b10620f1409ae87e228994b830eca638ccdceedd3fcd07d024b646704f44d5d9c4c3a7b705f37104b45b9cfc2d933ae43c12f53e3e6f798c51be5f640115d45cf919a4"-    ],-    -- COUNT = 3-    [ "6bdc6ca8eef0e3533abd02580ebbc8a92f382c5b1c8e3eaa12566ecfb90389a3"-    , "8f8481cc7735827477e0e4acb7f4a0fa"-    , ""-    , "72eca6f1560720e6bd1ff0152c12eeff1f959462fd62c72b7dde96abcb7f79fb"-    , ""-    , ""-    , ""-    , "d5a2e2f254b5ae65590d4fd1ff5c758e425be4bacdeede7989669f0a22d34274fdfc2bf87135e30abdae2691629c2f6f425bd4e119904d4785ecd9328f15259563e5a71f915ec0c02b66655471067b01016fdf934a47b017e07c21332641400bbe5719050dba22c020b9b2d2cdb933dbc70f76fec4b1d83980fd1a13c4565836"-    ],-    -- COUNT = 4-    [ "096ef37294d369face1add3eb8b425895e921626495705c5a03ee566b34158ec"-    , "6e2e0825534d2989715cc85956e0148d"-    , ""-    , "1b4f7125f472c253837fa787d5acf0382a3b89c3f41c211d263052402dcc62c5"-    , ""-    , ""-    , ""-    , "4541f24f759b5f2ac2b57b51125077cc740b3859a719a9bab1196e6c0ca2bd057af9d3892386a1813fc8875d8d364f15e7fd69d1cc6659470415278164df656295ba9cfcee79f6cbe26ee136e6b45ec224ad379c6079b10a2e0cb5f7f785ef0ab7a7c3fcd9cb6506054d20e2f3ec610cbba9b045a248af56e4f6d3f0c8d96a23"-    ],-    -- COUNT = 5-    [ "a7dccdd431ae5726b83585b54eae4108f7b7a25c70187c0acbb94c96cc277aa8"-    , "94c8f4b8e195a47356a89a50d1389ab5"-    , ""-    , "51733eee2e922f4055e53939e222e71fae730eb037443db2c7679708abb86a65"-    , ""-    , ""-    , ""-    , "99ba2691a622afecc9472418e6a8f9f1cdc1e3583c3bc7a2a650a1ab79dcbccbd656636c573179276e782569420c97438c06be898867f628b1c01eb570263d2c0f09c7aab536f6fba7df6aad19e05c236b645674667c03d1b6a04d7fc11177fe78933b309679f5bf26a4632b9a13e314c4bf4532428d3d95c689002b6dc1fbb1"-    ],-    -- COUNT = 6-    [ "c286425ecf543a49bcc9196b0db1a80bc54e4948adba6f41712a350a02891fa6"-    , "957a659a4ec2e0b7ad185483c220fd61"-    , ""-    , "08c2129813eea0776fba72788fdf2718759cc3c4207fa20a5fe23ac6e32cc28e"-    , ""-    , ""-    , ""-    , "8e1020a4fd84c99e0fc7e3f7ce48de5ed9ec9a5c2ccd624dbe6f30e2f688a31dc55957630357a5d48ca2a456241a28bfb16d8bb000877697a7ce24d9ad4d22b0c15117996f1f270b94f46d7a9bdfa7608fa1dd849177a9b8049e51b6b7a2742623854a1fddb5efc447eed1ea1aed6f02b4b2754ecf71ea0509da2e54f524a7e7"-    ],-    -- COUNT = 7-    [ "02818bd7c1ec456ace55beeba99f646a6d3aa0ea78356ea726b763ff0dd2d656"-    , "c482687d508c9b5c2a75f7ce390014e8"-    , ""-    , "cf319bfa63980e3cb997fd28771bb5614e3acb1149ba45c133ffbbab17433193"-    , ""-    , ""-    , ""-    , "19a231ff26c1865ce75d7a7185c30dd0b333126433d0c8cbf1be0d2b384d4eb3a8aff03540fbfa5f5496521a4e4a64071b44c78bd0b7e68fac9e5695c5c13fd3b9dbe7f7739781a4c8f0b980f1b17d99bce17ceb52b56866ae02456ffef83399c8cf7826f3c45c8a19315890919d20f40fc4e18d07e9c8ccd16c3327b5988f71"-    ],-    -- COUNT = 8-    [ "77a5c86d99be7bc2502870f4025f9f7563e9174ec67c5f481f21fcf2b41cae4b"-    , "ed044ad72ee822506a6d0b1211502967"-    , ""-    , "778100749f01a4d35c3b4a958aafe296877e0acafd089f50bc7797a42a33ab71"-    , ""-    , ""-    , ""-    , "831a4da566f46289904893ef1cc1cd4ad19ee48f3857e2b69e936d10afbdc29822e85d02663d346ef3e09a848b1d9cc04f4c4c6e3b3b0e56a034e2334d34ca08f8097be307ba41d020bc94f8c1937fe85644eeb5592c2b5a2138f7ded9a5b44b200c8b5beb27597c790f94d660eb61e8248391edc3ae2d77656cbe8354275b13"-    ],-    -- COUNT = 9-    [ "0ea458cff8bfd1dd8b1addcba9c01317d53039e533104e32f96e7d342e6c7b9b"-    , "935a4b66fc74c2a48757a99c399e64e3"-    , ""-    , "6c5f3708e7b714c4ed139b4fa9e8c763af01773484005109a85e33653bb0ce98"-    , ""-    , ""-    , ""-    , "373a37af84fddec13645a9768d6a785ae5a2589d64cd9b37980dde2541499210c4f408335de1d585349064f3f53a2b4c5ec6dc2a09591f99ad9fad528ac83474164b45497bf167f81e66fa08463ffea917f6891e48f149fafc20622bb1172f34886feb45c26fd446a4a4e2891b4bc594186896141aaaeeb301b49e7c1a26fec7"-    ],-    -- COUNT = 10-    [ "bfb68be4ce1756d25bdfad5e0c2f8bec29360901cc4da51d423d1591cc57e1ba"-    , "98afe4bd194c143e099680c504cceaab"-    , ""-    , "b97caf210e82498c3408790d41c320dd4a72007778389b44b7bc3c1c4b8c53f8"-    , ""-    , ""-    , ""-    , "409e0aa949fb3b38231bf8732e7959e943a338ea399026b744df15cbfeff8d71b3da023dcce059a88cf0d4b7475f628e4764c8bef13c70cfbbbb6da2a18aabcad919db09d04fc59765edb165147c88dd473a0f3c5ee19237ca955697e001ba654c5ee0bd26761b49333154426bc63286298a8be634fe0d72cfdeef0f3fc48eca"-    ],-    -- COUNT = 11-    [ "4f6880a64610004463031d67d7924fa446c39138d4d41007e8df3d65691a9367"-    , "6b33b2c13600f4b1df6ca3d1960e8dd4"-    , ""-    , "57b87b8c8f48312b5333d43b367730c0a5ad4725a16778fcb53fe136d136cbfd"-    , ""-    , ""-    , ""-    , "73d0f324ed186e2ad06bd1800e262bdbda79ba54e626761bd60f74f43e3bb62958ec1e2f1d940af163e1cadc124e7ebaba2f72e67efd746c7f6d0cad53ef03d859d93cff778a32ee5be172fe7fdbdc232ded360d704a6fa0f70bebe942e56478345492f49dc5c6fc346b88a58947ad250e688e8c626fe1efe7624620e571976e"-    ],-    -- COUNT = 12-    [ "aae352e111843219cae8f70e7b8f6eb9bb53d246cbec1e4f07d42757143295b4"-    , "b84485dccd1bf93210e322eafcbebcd9"-    , ""-    , "f9237f00d744d8fbff21b9d0043c258e8731817e6a5fb7b4bf5011680e5bc642"-    , ""-    , ""-    , ""-    , "cfb28b93522c7d61d8d3ce3f080e435e4c83c7e13a9dab788db8fef0407267a14fbc9324e090e24df5491fedfa81116869983938d4d4d7324a310c3af33a6f7938f602c5e4e63f1771cdaabdab0782b5affb54eb53047c109a9606739dd0065bd21eca33132986554878354f5f9f852e674dd690163b0ff74c7a25e6bae8ce39"-    ],-    -- COUNT = 13-    [ "589e79e339b7d2a1b879f0b0e1a7d1ad2474eaa8025b070f1ffa877b7124d4ff"-    , "0961ed64dbd62065d96e75de6d2ff9d6"-    , ""-    , "e928388d3af48c2968527a4d2f9c2626fbc3f3f5a5d84e0583ab6f78e7f8b081"-    , ""-    , ""-    , ""-    , "fce6ced1ecf474d181ab331f79c3d2cc8a768ec2818de5b3fc7cf418322716d6a6853733561a497c0c25cb288d2c9fcfbca891bafd5a834c85f3603f402acf1a7b1ea92db847ed5c252a862ad4ab5e259715f1fc81da67f5230bf8be50ee8069758095f7d0e559e03f2c6072290e61794458437609e473eb66580cddaad19b71"-    ],-    -- COUNT = 14-    [ "714277d408ad87fde317f0a94732fce62f1352bdc90936673b4f1daa0925aa26"-    , "d16582a99f23010b4248b88d86485419"-    , ""-    , "bd9fc7cb2fd5063b2c3c0c4f346ad2e3879371a9c805e59b9f2cd2cc2a40894f"-    , ""-    , ""-    , ""-    , "62ef7a431288252e0d736c1d4e36cc9ac37107dcd0d0e971a22444a4adae73a41eff0b11c8625e118dbc9226142fd0a6aa10ac9b190919bda44e7248d6c88874612abd77fb3716ea515a2d563237c446e2a282e7c3b0a3aef27d3427cc7d0a7d38714659c3401dbc91d3595159318ebca01ae7d7fd1c89f6ad6b604173b0c744"-    ]-    ]--{--    [SHA-256]-    [PredictionResistance = False]-    [EntropyInputLen = 256]-    [NonceLen = 128]-    [PersonalizationStringLen = 0]-    [AdditionalInputLen = 256]-    [ReturnedBitsLen = 1024]--}--r2 :: [TestVector]-r2 =-    [-    -- COUNT = 0-    [ "05ac9fc4c62a02e3f90840da5616218c6de5743d66b8e0fbf833759c5928b53d"-    , "2b89a17904922ed8f017a63044848545"-    , ""-    , "2791126b8b52ee1fd9392a0a13e0083bed4186dc649b739607ac70ec8dcecf9b"-    , "43bac13bae715092cf7eb280a2e10a962faf7233c41412f69bc74a35a584e54c"-    , "3f2fed4b68d506ecefa21f3f5bb907beb0f17dbc30f6ffbba5e5861408c53a1e"-    , "529030df50f410985fde068df82b935ec23d839cb4b269414c0ede6cffea5b68"-    , "02ddff5173da2fcffa10215b030d660d61179e61ecc22609b1151a75f1cbcbb4363c3a89299b4b63aca5e581e73c860491010aa35de3337cc6c09ebec8c91a6287586f3a74d9694b462d2720ea2e11bbd02af33adefb4a16e6b370fa0effd57d607547bdcfbb7831f54de7073ad2a7da987a0016a82fa958779a168674b56524"-    ],-    -- COUNT = 1-    [ "1bea3296f24e9242b96ed00648ac6255007c91f7c1a5088b2482c28c834942bf"-    , "71073136a5cc1eb5b5fa09e1790a0bed"-    , ""-    , "d714329f3fbea1df9d0b0b0d88dfe3774beb63d011935923d048e521b710dc6f"-    , "4ef872fd211a426ea1085ab39eb220cc698fdfeabe49b8835d620ab7885de7a4"-    , "d74d1669e89875852d9ccbf11c20fe3c13a621ebcb3f7edeea39a2b3379fdcf5"-    , "0c8aa67ca310bd8e58c16aba35880f747266dbf624e88ec8f9ee9be5d08fdeb1"-    , "ce95b98f13adcdf7a32aa34709d6e02f658ae498d2ab01ce920f69e7e42c4be1d005acf0ca6b17891dfafc620dd4cd3894f8492a5c846089b9b452483eb0b91f3649ec0b6f98d1aaabc2e42cd39c2b25081b85ab50cb723007a0fd83550f32c210b7c4150b5a6bb3b0c9e3c971a09d43acb48e410a77f824b957092aa8ef98bc"-    ],-    -- COUNT = 2-    [ "a7ea449b49db48601fc3a3d5d77081fab092b8d420ed1b266f704f94352dd726"-    , "d11a159b60af8d20a0e37d27e6c74aa3"-    , ""-    , "50916ab47e8cb5dc843f9fba80639103711f86be8e3aa94f8a64a3fe0e6e5b35"-    , "e2bb6768120555e7b9e0d573537a82f8f32f54560e1050b6abb1588fb3441e66"-    , "a50cec9d1ecddb2c163d24019e81c31a2b350ccd3ad8181fd31bb8d1f64fa50e"-    , "591dbbd48b51abced67f9c6269cf0133cd3dcbb5cfafcb6ef758569c555a5773"-    , "0a464abcc8685158372d544635b953fcb1d3821c30aaa93982f9b788935f00f88115aad61d5cee003b3d1cb50f3e961a501e2dd0fc7e1724778b184a4bdf9f64e110dda7446e5544a30bd49a400ea1a5411800e1edfeea349323618afc5dc5782dc4b71d2da4d6a4785f8dd346feb9c8740ffd26bf644e3e4323ff24c30b9f10"-    ],-    -- COUNT = 3-    [ "14683ec508a29d7812e0f04a3e9d87897000dc07b4fbcfda58eb7cdabc492e58"-    , "b2243e744eb980b3ece25ce76383fd46"-    , ""-    , "18590e0ef4ee2bdae462f76d9324b3002559f74c370cfccf96a571d6955703a7"-    , "9ea3ccca1e8d791d22fcda621fc4d51b882df32d94ea8f20ee449313e6909b78"-    , "16366a578b5ea4d0cb547790ef5b4fd45d7cd845bc8a7c45e99419c8737debb4"-    , "a68caa29a53f1ba857e484d095805dc319fe6963e4c4daaf355f722eba746b92"-    , "c4e7532ee816789c2d3da9ff9f4b37139a8515dbf8f9e1d0bf00c12addd79ebbd76236f75f2aa705a09f7955038ebff0d566911c5ea13214e2c2eeb46d23ad86a33b60f7b9448d63eec3e1d59f48b39552857447dc5d7944667a230e3dbfa30ca322f6eacaf7536a286706a627c5083c32de0658b9073857c30fb1d86eb8ad1b"-    ],-    -- COUNT = 4-    [ "fa261fb230e2822458532ca2d5c39758750e6819a6fcebef10579ba995096959"-    , "564e1c9fbcb12878df2bd49202cbf821"-    , ""-    , "bf7de29e99e7f0e1b9f96f3b1902fb4049c8c6234d20de8316ebe66d97725457"-    , "8b7326621f6afbd44a726de48d03bcc5331f7306026c229ea9523497fbeaa88d"-    , "33b00b31623d6160c4c6740363a96481be14b19bc47be95641227284c366922a"-    , "2d812c8203575790ad6b6f2ed91a49d57460de779a3e881bef3be12e8766dc91"-    , "5574e0b4efc17e8ce136e592beabfe32551072bddd740929e698467b40b3991f028a22c760f7034853cc53007e3793e3c4a600d9e9d94528f8dc09aeba86146cdde2b7f71255ae0efc529b49be2205979dba6525bfe155e8819e8e2aeeaa285704242da90b4c4535101cc47d94b0e388a1b2e63ad0cbe158b9e1bbae9cc0007c"-    ],-    -- COUNT = 5-    [ "61f1471ced56aa04c57e1b512307d4cb92497d9592d7e9e35356e99d585cab1b"-    , "84714e960c403a4fac06b2828cc564d9"-    , ""-    , "7bf97db3c102edc81596d4757045fe6bdc008f35792fc6290b77d889c09c33a8"-    , "5b8bdc41f76d98cfa71ed976ea3994706375c8841adb8b6b3b6418e3132e8832"-    , "94c8a8fdf38a6ccb8571c89420d899adab169214bb0dfcd43a04622e289935b2"-    , "8a4b46e0a7a55907365f82d4ab9376509bd44728cab8cbafb0da901012ad8dcd"-    , "933eb159a6af7455b60e40586c064f05f1970f564281b1ebc4662701ac1f299e4eb908c4afcb2e065191281ab576f684aefedd6904bad04d96bd93c0516c62a496c3073a0cda0676a11cc08866b0cc74f62cb9d3db48673b2c3fbeada69f922b4b795ccba22df12ef7125909381f7d681f6b9caba02fb913c5437b98c040c576"-    ],-    -- COUNT = 6-    [ "a1d5bb7d70621dee6b668b28c56d5610c2f8ced30284cc3e0e48de331af05062"-    , "88a49e3e54c5ea54c98b95de81bcc807"-    , ""-    , "b4e2426e98f6eed97a6cdf690a89ee109e84c3dca16c883c26fa4ac671638d8d"-    , "5bd1e086ed228cfd8b55c1731fea40c3a63d022599ca2da4bb23118f4821ba62"-    , "b754b53ac226e8ebe47a3d31496ec822de06fca2e7ef5bf1dec6c83d05368ec3"-    , "fa7e76b2805d90b3d89fff545010d84f67aa3a2c9eb2ba232e75f4d53267dac3"-    , "df6b2460688fa537df3ddfe5575fca5eb8abad56cbc4e5a618a2b4a7daf6e215c3a497974c502f9d0ec35de3fc2ea5d4f10de9b2aee66dcc7e7ae6357983095959b817f0383e3030771bd2ed97406acf78a1a4a5f30fa0992289c9202e69e3eb1eabe227c11409ff430f6dfca1a923a8b17bc4b87e908007f5e9759c41482b01"-    ],-    -- COUNT = 7-    [ "68f21d14525d56233c7e263482d344c388a840103a77fb20ac60ce463cabdc79"-    , "59fa80ae570f3e0c60ac7e2578cec3cb"-    , ""-    , "7584b4166530442f06e241dd904f562167e2fdae3247ab853a4a9d4884a5fa46"-    , "f6a5482f139045c5389c9246d772c782c4ebf79c3a84b5cf779f458a69a52914"-    , "9d37b1ce99f8079993ddf0bd54bab218016685b22655a678ce4300105f3a45b7"-    , "4c97c67026ff43c2ee730e7b2ce8cce4794fd0588deb16185fa6792ddd0d46de"-    , "e5f8874be0a8345aabf2f829a7c06bb40e60869508c2bdef071d73692c0265f6a5bf9ca6cf47d75cbd9df88b9cb236cdfce37d2fd4913f177dbd41887dae116edfbdad4fd6e4c1a51aad9f9d6afe7fcafced45a4913d742a7ec00fd6170d63a68f986d8c2357765e4d38835d3fea301afab43a50bd9edd2dec6a979732b25292"-    ],-    -- COUNT = 8-    [ "7988146cbf9598d74cf88dc314af6b25c3f7de96ae9892fb0756318cea01987e"-    , "280bc1ae9bfdf8a73c2df07b82a32c9c"-    , ""-    , "2bbc607085232e5e12ccf7c0c19a5dc80e45eb4b3d4a147fe941fa6c13333474"-    , "f3f5c1bb5da59252861753c4980c23f72be1732f899fdea7183b5c024c858a12"-    , "44d0cfc4f56ab38fa465a659151b3461b65b2462d1ad6b3463b5cf96ad9dc577"-    , "34fb9a3cdacc834ff6241474c4f6e73ed6f5d9ea0337ab2b7468f01ad8a26e93"-    , "4caec9e760c4d468e47613fe50de4a366ae20ba76793744a4e14433ea4de79dc188601eb86c803b094641ab2337b99d459d37decc7d27473057be45ba848868ee0fb5f1cf303d2fcd0b3e0c36f65a65f81b3fee8778a1f22302e25dfe34e6d587fa8864e621121880f7cd55f350531c4ce0530099eec2d0059706dcd657708d9"-    ],-    -- COUNT = 9-    [ "1c974c953fa2a057c9fc9409a6843f6f839aa544bca4fa11e48afd77931d4656"-    , "ed7c08285464af7a5dbdc10b944a1270"-    , ""-    , "78146ad135acb836360d36afc50653dcc36c21662da2a6f6ae05222e75f34000"-    , "263c4984c238ded333c86472866353817379502157172cfa51371d82b1efd7b5"-    , "79b591529f9a26a0d7c8f8fd64e354b0c134ef1f757e43f9463b3dbb7a3da1ab"-    , "7d8f7204b0b5401ddce9e88dcf5facb9a44660a9f5f1c862748e7269c29f7964"-    , "72e2ca257b9edaf59b50e05a144f56fb517832fb9ad3489b1e664e3d5412cbf6b2883e891703b2e73aff9ab56da1009fcdef010ab4cdab996795c8f7c47fb1192bb160353997ad39d7d5fd0e2efc9103a7c3f158246afd53fe53ca6782f809698ef5f1f0d85536780a3fd6a8bafa475891c09213088bd1a3dc169257c34a517a"-    ],-    -- COUNT = 10-    [ "56216d71984a77154569122c777ce57e1d101a6025b28163a25971d39c1c5d0f"-    , "5cd148ba7e54f4975ac8e3e0f9b5d06a"-    , ""-    , "3580f8ca974626c77259c6e37383cb8150b4d0ab0b30e377bed0dc9d1ff1a1bf"-    , "15633e3a62b21594d49d3d26c4c3509f96011d4dbb9d48bbbea1b61c453f6abe"-    , "6068eaca85c14165b101bb3e8c387c41d3f298918c7f3da2a28786ab0738a6fc"-    , "e34f92d2b6aeeeea4ff49bfe7e4b1f462eabb853f0e86fbae0e8b3d51409ce49"-    , "587fdb856abc19ede9078797ecb44099e07aadcd83acdcb2b090601d653f4a14c68ab2ebdda63578c5633a825bae4c0c818f89aac58d30fd7b0b5d459a0f3d86fcad78f4bb14dfff08ad81e4ea9f487cb426e91d6e80dfed436ba38fce8d6f21ca2151c92dd5c323b077d6139c66395558f0537026c4a028affa271ef4e7ea23"-    ],-    -- COUNT = 11-    [ "83eb48bedc1e9294866ab8e5322ef83f6f271f8188e8fdabe5817788bd31570d"-    , "d6ed90bc692237f132441ede857a6629"-    , ""-    , "a4e5e127f992bd5ca79ee56bb8a9bccf74c21814bfaf97ffd052211e802e12e4"-    , "84136e403d9ed7f4515c188213abcfaca35715fa55de6d734aec63c4606a68f1"-    , "fe9d8ef26e2d2e94b99943148392b2b33a581b4b97a8d7a0ecd41660a61dd10b"-    , "594dad642183ce2cdc9494d6bcb358e0e7b767c5a0fa33e456971b8754a9abd5"-    , "86715d43ba95fbbca9b7193ea977a820f4b61ba1b7e3b8d161b6c51b09dfd5040d94c04338b14d97ed25af577186b36ae7251a486c8a2d24a35e84a95c89d669d49e307b4a368b72164135ac54d020a970a180dfbed135d2c86f01270846d5301bd73db2c431a8aa10a0a3d03d146e5fafb9a2aa0b4efc80edab06ff3b532236"-    ],-    -- COUNT = 12-    [ "ba2c94203dab2e6499d8c50dca7b5c34a6b4764834f9816631aa21b9f9c37361"-    , "67db133bdefb25e395085bceee5a0afc"-    , ""-    , "fa8984d16d35302cda35a3a355ab9242ec96fec0652d39282d4a0abf0a80df87"-    , "b6fed10255a3fea6772ae1ae6d9f6cbb9bfaa34804e58a5b786f9bc60b348ccd"-    , "445e072244edc716d3528f0e0a20ff0cd8f819c0d031736c8da122748f24d6c6"-    , "1f856e403c4fa035bac9aa81a20e347c7d8b213aab699d69d9d6186a06ac45c1"-    , "79f33fc36b3b47d9ac805bdbbe699909a8d0beb689a8b2723c291bd5bf7f3ce61343d4722a14e4add36312dbb0594910c8828aff1abc159915d498106f9ffb31147478d8c9ef75d1536ba5036506b313f6e85033f8f6fea2a4de817c867a59378c53c70a2f108275daedd415c05b61c4fd5d48c54be9adb9dea6c40a2ec99ee0"-    ],-    -- COUNT = 13-    [ "0db4c51492db4fe973b4bb1c52a1e873b58fc6bb37a3a4bfc252b03b994495d1"-    , "a2a3900f169bba3f78a42526c700de62"-    , ""-    , "29d5aab356876447e3a20d81c7e3fc6975e2b984180a91493044442999e1ca3a"-    , "40b34183b4e72cdff5952b317b3d45943d0fdcfa0527f3563055f7c73ae8f892"-    , "dc94220c99ffb595c7c4d6de8de5a6bb4b38847169e24a557ef6d879ad84149d"-    , "b2376626fd2f5218b3ed4a5609b43aa24d371cd2176ea017c2b99cf868060021"-    , "f0bd6bc4c506d9427a09352d9c1970b146360732841a6323f4cb602c87dedfb5ff7e6964b9144933af3c5c83017ccd6a94bdca467a504564aaa7b452591a16ff6a1e7e94ddc98f9a58016cdcb8caaed6c80671ba48cc81a832d341093dda1d4e5001ec6bf66348b21e3692a13df92538ad572bb2023822072fc95f9590293ffc"-    ],-    --  COUNT = 14-    [ "593845f0adfeffa7c169f8a610147ae8a08c0072fc0c14c3977d3de0d00b55af"-    , "9e0eb2507342ee01c02beadee7d077bd"-    , ""-    , "aefe591697eab678c52e20013aa424b95cfd217b259757fbe17335563f5b5706"-    , "cbb5be0ef9bf0555ee58955c4d971fb9baa6d6070c3f7244a4eb88b48f0793bf"-    , "6dd878394abdc0402146ba07005327c55f4d821bfebca08d04e66824e3760ab4"-    , "ba86a691d6cbf452b1e2fd1dfb5d31ef9ea5b8be92c4988dc5f560733b371f69"-    , "00735cbfafac5df82e5cb28fc619b01e2ba9571dc0023d26f09c37fb37d0e809066165a97e532bf86fa7d148078e865fe1a09e27a6889be1533b459cd9cd229494b5cf4d2abf28c38180278d47281f13820276ec85effb8d45284eb9eef5d179ab4880023ab2bd08ee3f766f990286bf32430c042f5521bbfd0c7ee09e2254d7"-    ]-    ]--{--    [SHA-256]-    [PredictionResistance = False]-    [EntropyInputLen = 256]-    [NonceLen = 128]-    [PersonalizationStringLen = 256]-    [AdditionalInputLen = 0]-    [ReturnedBitsLen = 1024]--}--r3 :: [TestVector]-r3 =-    [-    -- COUNT = 0-    [ "fa0ee1fe39c7c390aa94159d0de97564342b591777f3e5f6a4ba2aea342ec840"-    , "dd0820655cb2ffdb0da9e9310a67c9e5"-    , "f2e58fe60a3afc59dad37595415ffd318ccf69d67780f6fa0797dc9aa43e144c"-    , "e0629b6d7975ddfa96a399648740e60f1f9557dc58b3d7415f9ba9d4dbb501f6"-    , ""-    , ""-    , ""-    , "f92d4cf99a535b20222a52a68db04c5af6f5ffc7b66a473a37a256bd8d298f9b4aa4af7e8d181e02367903f93bdb744c6c2f3f3472626b40ce9bd6a70e7b8f93992a16a76fab6b5f162568e08ee6c3e804aefd952ddd3acb791c50f2ad69e9a04028a06a9c01d3a62aca2aaf6efe69ed97a016213a2dd642b4886764072d9cbe"-    ],-    -- COUNT = 1-    [ "cff72f345115376a57f4db8a5c9f64053e7379171a5a1e81e82aad3448d17d44"-    , "d1e971ec795d098b3dae14ffcbeecfd9"-    , "6ec0c798c240f22740cad7e27b41f5e42dccaf66def3b7f341c4d827294f83c9"-    , "45ec80f0c00cad0ff0b7616d2a930af3f5cf23cd61be7fbf7c65be0031e93e38"-    , ""-    , ""-    , ""-    , "17a7901e2550de088f472518d377cc4cc6979f4a64f4975c74344215e4807a1234eefef99f64cb8abc3fb86209f6fc7ddd03e94f83746c5abe5360cdde4f2525ccf7167e6f0befae05b38fd6089a2ab83719874ce8f670480d5f3ed9bf40538a15aaad112db1618a58b10687b68875f00f139a72bdf043f736e4a320c06efd2c"-    ],-    -- COUNT = 2-    [ "b7099b06fc7a8a74c58219729db6b0f780d7b4fa307bc3d3f9f22bfb763596a3"-    , "b8772059a135a6b61da72f375411de26"-    , "2ac1bfb24e0b8c6ac2803e89261822b7f72a0320df2b199171b79bcbdb40b719"-    , "9aec4f56ec5e96fbd96048b9a63ac8d047aedbbeea7712e241133b1a357ecfc4"-    , ""-    , ""-    , ""-    , "0e1f2bfef778f5e5be671ecb4971624ec784ed2732abc4fbb98a8b482fb68737df91fd15acfad2951403ac77c5ca3edffc1e03398ae6cf6ac24a91678db5c7290abc3fa001aa02d50399326f85d2b8942199a1575f6746364740a5910552c639804d7530c0d41339345a58ff0080eccf1711895192a3817a8dc3f00f28cc10cc"-    ],-    -- COUNT = 3-    [ "7ba02a734c8744b15ef8b4074fe639b32e4431762ab5b7cd4d5df675ea90672b"-    , "8a424f32108607c8f1f45d97f500ee12"-    , "3ad627433f465187c48141e30c2678106091e7a680229a534b851b8d46feb957"-    , "d8f02b59b6a3dd276bc69cba68efcf11ab83ead1397afd9841786bd1bb5da97a"-    , ""-    , ""-    , ""-    , "1fb91186ba4b4459d994b4b9f4ca252c7be6294d6cdb5fe56f8ff784d4b190a1c6456e0a41223bbbdf83ed8e7cfbfa765d9d8bc7ea5f4d79ea7eccb4928081a21de4cca36620d6267f55d9a352b76fc0a57375884112c31f65ff28e76d315698c29e6c4c05cb58b0a07ae66143b4abc78b9d25c78b4121e1e45bef1a6c1793e2"-    ],-    -- COUNT = 4-    [ "9a8865dfe053ae77cb6a9365b88f34eec17ea5cbfb0b1f04d1459e7fa9c4f3cb"-    , "180c0a74da3ec464df11fac172d1c632"-    , "336372ec82d0d68befad83691966ef6ffc65105388eb2d6eed826c2285037c77"-    , "75b95108eff1fabe83613e1c4de575e72a5cdc4bb9311dd006f971a052386692"-    , ""-    , ""-    , ""-    , "3c683f6d4f8f5a4018d01633dfee74266aaa68ed6fc649e81b64dfdf5f75e75d5c058d66cf5fd01a4f143a6ff695517a4a43bd3adfd1fb2c28ba9a41063140bedbffdb4d21b1ace1550d59209ec61f1e2dbacb2a9116a79cb1410bf2deca5218080aacd9c68e1d6557721a8913e23f617e30f2e594f61267d5ed81464ee730b2"-    ],-    -- COUNT = 5-    [ "22c1af2f2a4c885f06988567da9fc90f34f80f6dd5101c281beef497a6a1b2f8"-    , "3fafdecf79a4174801f133131629037b"-    , "80327dac486111b8a8b2c8e8381fb2d713a67695c2e660b2b0d4af696cc3e1de"-    , "f95a0e4bd24f0e2e9e444f511b7632868ead0d5bb3846771264e03f8ab8ed074"-    , ""-    , ""-    , ""-    , "77a7fea2f35a188f6d1bfdd49b569d8c45e2dd431d35a18c6f432c724f1e33ae92cb89a9cf91519e50705a53199f5b572dc85c1aef8f28fb52dc7986228f66954d54eda84a86962cf25cf765bd9949876349291b1aae5f88fcf4b376912d205add4f53b2770c657946c0d824281f441509153f48356d9d43f8a927e0693db8fc"-    ],-    -- COUNT = 6-    [ "d0840e3a8d629d5b883d33e053a341b21c674e67e1999f068c497ecfaabfd6f6"-    , "071de7244ecb2fdf7ab27f2d84aa7b7a"-    , "90d609527fad96ffe64ab153860346f3d237c8940555ae17b47842d82d3b0943"-    , "1dd1a8b59856c49a388f594c5f42cc2e4a56b3ccb8a65e7066e44c12f4344d50"-    , ""-    , ""-    , ""-    , "7ab28a9b2d3ae999195553e6550cced4c2daccbe7ec9dcbb0d467fabba185b727fbfd9830242cd098f4db3cf4a85e8bf8e8d5974b62b28550922b32ed5bfc1a522b6605cf93bf8d90bdec1c5b9e59c6fc37a817d437068a87254be1f7c4618ada46fbc3a2efb02e44524e21d91be7534cf05fbfd858304b706d6a91ea1cc6ad5"-    ],-    -- COUNT = 7-    [ "2e2dd56869104492767a59778652831919e1c8b970f84e824ae4116597a0ab7f"-    , "01c42a7e983641de46c82fd09b4f2f76"-    , "bcd9e1508fcc22820a8be07180fea5045367333b569e111b011cd57dc1858765"-    , "7306507cd3ca7eec667e640d270cfbb033063d97520b6b7e38ff3cea0e79d12b"-    , ""-    , ""-    , ""-    , "b915726c7b8c5dc3975f1a334684b973abf6a9495d930088cf5d071548e4fd29a67b55cc561ed6949ad28150a9fb4307c1fa5f783a7ea872e8d7c7e67ff0c2906081ee915737d813c25be5c30b952a36f393e6baa56ab01adc2b4776ad7b5d036a53659877c7a4e5220a897d6c0799af37beeed91173fbe9c613c3b6b9bb28e5"-    ],-    -- COUNT = 8-    [ "d1aab0f16bd47a5ccd67c22e094daa3735eae21aa57f0bcd9e053d9d0d545cb8"-    , "199310dfe1b01265b8c0d2b46d6c7c9f"-    , "625b4b8f4de72ea9cb6f70556322dc2a19d6b2b32de623f557e419a084ba60fd"-    , "f50cabae4e060f3971096b78e550cda2837a26a693d905db2d992d589b268f44"-    , ""-    , ""-    , ""-    , "987e1fdfe004c619cf1e9034576707eccd849400e19c87a1fef5b0179ec51c42a2f8c45d7942d0023a023c89f188b2634362703985695369863322f58619c50a7385a2dc91fc78f94b59f0131dc2b56a0d7c699d427285da1c104b0ad1739da10d8071c23993787045dc21f0070e1e9aa1658fc8e3add73dac7262e80e0aa2ee"-    ],-    -- COUNT = 9-    [ "449480eaa100aff6f48dc6286a5a81b9728b084864f78a9da98f606a00a6a41f"-    , "e53c6c5ac3da9f4726389a03f97bb640"-    , "6b8fedc084d8e28d333aef6db3702b6351f0d24e30908cccb63794282655886b"-    , "73a6d64e1966ae324388dc12c14544e9dc5ae4fcb331e99d350c456ff16f9aa0"-    , ""-    , ""-    , ""-    , "a06912d362da7eb25598857f6d65344c3e23ec3deb80c6e43158845b95eaeca241c0bbbd67ac385e24693444455cc1c2c08c1134d956b8bc93b28be9c2d3322b3e09252979dfb8d39d04c94f81bebda5c73110605a237b561216bda9ee9bdee1cc0c7728bcc8304682334ca944e467a27a85313fa5395a9c790e35defd2edb12"-    ],-    -- COUNT = 10-    [ "9a6174166e97aa4981ddf580bc01c96754b9f0ba042750aabfda1cffe56e8581"-    , "d7512ff6b7db7ce141b2bb01dcd0425e"-    , "ed75288f23275f9422444da5d3b53ccb3c4ac8acfb659a1e9b7655c2db52f879"-    , "6888b9277e57dc57663d402eba8d03cf56a070dc868e6a128b18040002baf690"-    , ""-    , ""-    , ""-    , "03519dfb2ff88cc2b53eecc48ae2a18ddcf91a5d69d5aefcdda8444e6df790a5240e67b2a4de75b4bb8a31f0f8aeb5e785ffb7a1341bb52fe00a05ee66fa2d44ea9956e055f9ffa6647c3bfe851ab364ade71a0d356de710ddafb7622b1da1bc53fd4d3210407289c68d8aeb346bf15806dbe787e781b94f63da3e1f61b5ac60"-    ],-    -- COUNT = 11-    [ "9c6ae1002ee1b0add0be563ce50f899da936e13efa620d08c2688c192514763a"-    , "fde7db5160c73044be73e9d4c1b22d86"-    , "8fdaaeffd64e53f7b4374d902d441209964e12b65d29afec258e65db6de167ca"-    , "bcc28fd58e397f53f494ad8132df82c5d8c4c22ea0b7139bd81eeba65667bb69"-    , ""-    , ""-    , ""-    , "021d938c9b4db780c7d8134aeff1053e5b8843370b8ae9a6749fca7199d809810f1bc8dfa49426470c30c3616f903e35fbacb23420a32f1bee567cc32300f704246ddc0217f236ef52c3ec9e2433ca66f05c25721f7661c43f22c1a125ed5db531bd0836eb435c27eefc7424ce9d845e1d4cc4c503097b4ffca788e674a5cb53"-    ],-    -- COUNT = 12-    [ "fe96a85b69d46b540918927bb609dc57642eeaefd46bb5da2163a0bc60294b58"-    , "22195a410d24db45589448dfe979d3fd"-    , "20f698833a4472fd7b78fb9b0c4eb68604f166a2694c4af48dac2b2376790e1e"-    , "09cb870879d3f734214f6a4bd2e08c62a2a954bebe559416d8c3551aafe71d6a"-    , ""-    , ""-    , ""-    , "d3e96dbe29e1fcb8ed83b19dbfb240e6f41679fbe83853aa71446617e63e5af78cf98b331d15bccb8c673c4e5d5dcec467a1fe26a6cd1696d0c9bc49f78139d051287df7f3ae0dbb4bbf581cb8211931063c3f4612ced53f59d1b4ebb875729139f5d2a7d60642e8f2835eed888b7e3e49c0dffd012cd746abfa3e1c5c2308c6"-    ],-    -- COUNT = 13-    [ "a4fd693ff0a8af24bcec352d3196549fd0da5ee5d99ca58416ca03ce4c50f38e"-    , "8cd67f2bf71d4366ce61396642531ff5"-    , "368969c15a4849d7593be8b162113b9298a535c148ff668a9e8b147fb3af4eba"-    , "83d2be9a0d74e6a42159ae630acebf4e15271ef7f14f3de14752be0e0e822b11"-    , ""-    , ""-    , ""-    , "e9188fc0eaec74b2608e21e3a40be94aaf4ae08eb684de8f8bba2d5fd3b073aa5531c938c0fc628da65725c54b5c68bb91d7d326565e96685e0a4e7b220c50e0caf1628edba5bd755b31894f8cb90afa76e88c5eb9e61b4932444c1397dee3e32241a3fb70a3929e49f6da02eea54812abb3d6b5cee18f03af1e0b4958430ab3"-    ],-    -- COUNT = 14-    [ "254ff5687a6dad3f1d237dc762f58d24ef2e2c084d0a48d26a3dc81e5490cda3"-    , "f2ec392acca491e03ce47b95963a49fc"-    , "f806b9b4a56682c61b55cb6a334caf87ffe135adfea6d0c3fc22b39898fbd078"-    , "b8494b1c1f1752fb6f80d732a89b08115857f7cc96e7dff05ebb822706889917"-    , ""-    , ""-    , ""-    , "0e527e00494d55564f9d9b28e7110f9a61ce36c883b5be2dcb055444164cdddd1a9f2731716f22d6ff476ce413c77abfc0e946871d5481345c2e97b4bfdd12ac03df606fc56bdb99ac7b71a69b5b9160373bbec3e9dde477180af454e7acc6bc58dc0afb4281c0de4354c1bf599054e3800c6d60d892858865b5361f50bfca9b"-    ]-    ]--{--    [SHA-256]-    [PredictionResistance = False]-    [EntropyInputLen = 256]-    [NonceLen = 128]-    [PersonalizationStringLen = 256]-    [AdditionalInputLen = 256]-    [ReturnedBitsLen = 1024]--}--r4 :: [TestVector]-r4 =-    [-    -- COUNT = 0-    [ "cdb0d9117cc6dbc9ef9dcb06a97579841d72dc18b2d46a1cb61e314012bdf416"-    , "d0c0d01d156016d0eb6b7e9c7c3c8da8"-    , "6f0fb9eab3f9ea7ab0a719bfa879bf0aaed683307fda0c6d73ce018b6e34faaa"-    , "8ec6f7d5a8e2e88f43986f70b86e050d07c84b931bcf18e601c5a3eee3064c82"-    , "1ab4ca9014fa98a55938316de8ba5a68c629b0741bdd058c4d70c91cda5099b3"-    , "16e2d0721b58d839a122852abd3bf2c942a31c84d82fca74211871880d7162ff"-    , "53686f042a7b087d5d2eca0d2a96de131f275ed7151189f7ca52deaa78b79fb2"-    , "dda04a2ca7b8147af1548f5d086591ca4fd951a345ce52b3cd49d47e84aa31a183e31fbc42a1ff1d95afec7143c8008c97bc2a9c091df0a763848391f68cb4a366ad89857ac725a53b303ddea767be8dc5f605b1b95f6d24c9f06be65a973a089320b3cc42569dcfd4b92b62a993785b0301b3fc452445656fce22664827b88f"-    ],-    -- COUNT = 1-    [ "3e42348bf76c0559cce9a44704308c85d9c205b676af0ac6ba377a5da12d3244"-    , "9af783973c632a490f03dbb4b4852b1e"-    , "2e51c7a8ac70adc37fc7e40d59a8e5bf8dfd8f7b027c77e6ec648bd0c41a78de"-    , "45718ac567fd2660b91c8f5f1f8f186c58c6284b6968eadc9810b7beeca148a1"-    , "63a107246a2070739aa4bed6746439d8c2ce678a54fc887c5aba29c502da7ba9"-    , "e4576291b1cde51c5044fdc5375624cebf63333c58c7457ca7490da037a9556e"-    , "b5a3fbd57784b15fd875e0b0c5e59ec5f089829fac51620aa998fff003534d6f"-    , "c624d26087ffb8f39836c067ba37217f1977c47172d5dcb7d40193a1cfe20158b774558cbee8eb6f9c62d629e1bcf70a1439e46c5709ba4c94a006ba94994796e10660d6cb1e150a243f7ba5d35c8572fd96f43c08490131797e86d3ed8467b692f92f668631b1d32862c3dc43bfba686fe72fdd947db2792463e920522eb4bc"-    ],-    -- COUNT = 2-    [ "b63fdd83c674699ba473faab9c358434771c5fa0348ca0faf7ebd7cf5891826b"-    , "5fd204e2598d9626edab4158a8cfd95f"-    , "2a5dfad8494306d9d4648a805c4602216a746ae3493492693a50a86d1ba05c64"-    , "adea5ba92f8010bb1a6a4b6fae2caa0b384165adf721253afd635d6021f764af"-    , "07c69d8d2b8aa1454c5c48083dd41477fda6bfcf0385638379933a60ed2e0a77"-    , "a14e902247a3d6493d3fbc8519518b71a660e5502cf7ecfc796cfaa5b4ee4baa"-    , "60e690e4a1eba14aec5187112a383e9991347fab7bac7cb2a40a52579a0d2718"-    , "792b47b6ed221623bb187d63e3f039c6983d94efd5771dc9b4c40bee65924513485a6332baeda6a96f9bb431f592d73462b61d9d914a72b56fa9d87597426fb246424ebcd7abd51b2eefec8f5b839c0b3c34015342ace296b5f2218fa194b50aea1c89663460292c92c45f112ddbf6b9406f6e7ccee9c47ed2d90a27be5dd73e"-    ],-    -- COUNT = 3-    [ "dab85f98eaf0cfba013b97de4d9c264ca6fe120366cb83e8b3113c68b34e39d5"-    , "d05108e1028ae67b4ea63bdc6d75eb88"-    , "09fed3822f6f5e5b9e575d31dc215de1607b0dfc927412618c2d8f79166dbaba"-    , "1794885a64470744198b7d0bc24472ffe8daf3c7eb219df6ddf180e484fe0aa5"-    , "8d74d01b582f70b92f53b43468084e1586d9b36465d333d5faaf6911e62fe40e"-    , "ef7f6b6eb479ab05b3f9ab6dd72eac8b1e86d887f1bcae363cae386d0275a06f"-    , "7442b2a792a6a29559bb8a515d56916ee18200580aa02e1237dd358619382d8f"-    , "49d2cbfa0897b7d961c293c1e572fb26f28e7b956e746f6eda90454c1370a29e25303ceadc7837514dc638553b487ef9487c977c10625409178ad6506d103c487a66655d08659d92a4d5994d1c8ddb28fe60f2e49577d6e80cae1478068c98268f45e6293c9326c7f726ec89601351c0a26fd3a6549f8a41c6f58692c86594c0"-    ],-    -- COUNT = 4-    [ "0f0aa84ef12e10ae2b279e799c683441862457b9bc25581c2cd3d5b58a5b3246"-    , "f74f4230c2427a52f01f39e825d250ac"-    , "d02b2f53da48b923c2921e0f75bd7e6139d7030aead5aeebe46c20b9ca47a38a"-    , "5222b26e79f7c3b7066d581185b1a1f6376796f3d67f59d025dd2a7b1886d258"-    , "d11512457bf3b92d1b1c0923989911f58f74e136b1436f00bad440dd1d6f1209"-    , "54d9ea7d40b7255ef3d0ab16ea9fdf29b9a281920962b5c72d97b0e371b9d816"-    , "601cef261da8864f1e30196c827143e4c363d3fa865b808e9450b13e251d47fa"-    , "e9847cefea3b88062ea63f92dc9e96767ce9202a6e049c98dc1dcbc6d707687bd0e98ed2cc215780c454936292e44a7c6856d664581220b8c8ca1d413a2b81120380bfd0da5ff2bf737b602727709523745c2ced8daef6f47d1e93ef9bc141a135674cba23045e1f99aa78f8cead12eeffff20de2008878b1f806a2652db565a"-    ],-    -- COUNT = 5-    [ "6a868ce39a3adcd189bd704348ba732936628f083de8208640dbd42731447d4e"-    , "efdde4e22b376e5e7385e79024350699"-    , "f7285cd5647ff0e2c71a9b54b57f04392641a4bde4a4024fa11c859fecaad713"-    , "0174f7f456ac06c1d789facc071701f8b60e9accebced73a634a6ad0e1a697d4"-    , "5463bb2241d10c970b68c3abc356c0fe5ef87439fc6457c5ee94be0a3fb89834"-    , "3ab62cdbc638c1b2b50533d28f31b1758c3b8435fe24bb6d4740005a73e54ce6"-    , "2dbf4c9123e97177969139f5d06466c272f60d067fefadf326ccc47971115469"-    , "8afce49dccc4ff64c65a83d8c0638bd8e3b7c13c52c3c59d110a8198753e96da512c7e03aeed30918706f3ad3b819e6571cfa87369c179fb9c9bbc88110baa490032a9d41f9931434e80c40ae0051400b7498810d769fb42dddbc7aa19bdf79603172efe9c0f5d1a65372b463a31178cbae581fa287f39c4fbf8434051b7419f"-    ],-    -- COUNT = 6-    [ "bb6b339eae26072487084ec9e4b53f2f1d4267d205042e74c77fb9ca0591ba50"-    , "c0e7bf6eb07feccbc494af4098e59d30"-    , "34aeec7ed0cae83701b6477709c8654a1114212401dc91cbe7de39d71f0c06e1"-    , "f47fc60afbeb807236f7974d837335bc0b22288ef09ddfcb684e16b4c36a050b"-    , "e8071ccd84ac4527e5c6e85b0709ed867776f25ae0e04180dcb7105ecd3e3490"-    , "fbac45b5952200ad7c4232500f2417a1c14723bdd1cc078821bc2fe138b86597"-    , "c4292d7dbef3ba7c18bf46bcf26776add22ab8ee206d6c722665dec6576b1bc0"-    , "228aa2a314fcbfe63089ce953ac457093deaa39dd9ce2a4ece56a6028a476a98129be516d6979eff5587c032cdf4739d7ac712970f600fa781a8e542e399661183e34e4b90c59ec5dc5cad86f91083529d41c77b8f36c5a8e28ba1a548223a02eaed8426f6fe9f349ebec11bc743e767482e3472ec2799c1f530ebdc6c03bc4b"-    ],-    -- COUNT = 7-    [ "be658e56f80436039e2a9c0a62952dd7d70842244b5ab10f3b8a87d36104e629"-    , "33c9627455dfde91865aee93e5071147"-    , "d3a6eb29b180b791984deb056d72c0608a2c9044237aecf100ccb03700064c5e"-    , "bef24dc9a5aa23003d3825f9b2b00e7dab571ea6ad86415dbd30c0bbdce7b972"-    , "047c29e4d1584fa70cb66e2aa148a2aa29837c5eee64dcac60fdba356cdf90bb"-    , "41c4792161b1b00d410cb79cd56bd311a714fb78dc3471c25bdd7479f2e9a952"-    , "cd4936d7bc3ea0e7201bcbefbc908215a97680ca6ce8672360aea600b6564308"-    , "2c25557f6db07db057f56ad5b6dc0427d1a0e825c48c19a526f9a65087c6d1ead7c78363a61616c84f1022653af65173a3f9ec3275f2b0a0d0bc750194673c0eaa6c623cd88abb0c8979baee4cd85bfce2e4a20bfebf2c3be61676563767dfe229e0b7be67ad6fcd116dd0b460708b1b0e5c3d60f3dd8138030404d197375d75"-    ],-    -- COUNT = 8-    [ "ae537f31a28ca14500e759716bc207983bfeab60b25079fa30b77b8d41244cb9"-    , "fca9e27d8ab84cf9b9ce491ec5d8cb67"-    , "8c9cb2b19aa3abe83c8fe7da96e9c11648252653a29dcd5bf0ac334ac587f032"-    , "1eb52777be480f05115ae6370f30159a94d50ffcc64454678ab1d1ac6f166fa7"-    , "9cdf6f1a2bc07acd4b0f43b5f2b892a1153e2669f237d257923636094fb40b54"-    , "692d512722de6ba720fd23c8994ac63179b5f7e611addf9cfacd60e06e144a6a"-    , "bbeea7b2bea821f339f494947c0b4bae8056119db69a3cbef21914953729cdef"-    , "c0c4fb7080c0fbe425c1b756fb3a090cb0d08c7027d1bb82ed3b07613e2a757f83a78d42f9d8653954b489f800a5e058ebc4f5a1747526541d8448cb72e2232db20569dc96342c36672c4be625b363b4587f44557e58cedb4597cb57d006fda27e027818ae89e15b4c6382b9e7a4453290ea43163b4f9cae38b1023de6a47f7b"-    ],-    -- COUNT = 9-    [ "2f8994c949e08862db0204008f55d3561f3e0362df13b9d9a70fda39938f2d33"-    , "1bf3e94ea858160b832fe85d301256f5"-    , "b46671cf7fa142e7012ed261e1fe86714711c246c7d1c0330fa692141e86d5d1"-    , "5ecdb1e8fe12260b9bfe12d6e6f161474fa2311e12e39b0beb0fcd92a6737b73"-    , "3ce9a29f0207d079e6dc81fb830356e555f96a23ea71424972ea9308965786d3"-    , "db950000c0776cc0e049929ce021020adc42d29cd9b5d8f7117fbe6bde3e594f"-    , "fc18ee6dd3dac2306774f0ac36cd789e33462d72a8c75df9057123db33e5f7bc"-    , "8546362cc8af9b78dd6e8eb2c37db96e70708852bfd9380abedc7f324575a167bea18f632f3e19d099cfbf310773f9719eec036d2e09f393a023add8ebdc4fb87af43b2fe6c7eaa4d39f8022ce247aa45fdc84d1b92cacce6eae8252a03ec2ec5330c01f56d113fd2ec3d0240af0afcf13ddde205bb5e7c2d912dcb4aee5dcf3"-    ],-    -- COUNT = 10-    [ "0c85e31487de1d7ba4a7b998ac56dc42c6dc0eae7bf5c8aaf1e4e78875f5fb47"-    , "de878f728f73f83dc2a2f550b96c8b97"-    , "9aac37bce1a6a81dc7934e23747991e3cf48c55ffe5a57781c41768a35220a01"-    , "2d5ca8af1a70cfdccd015ee3bf0665dd1941fc6a7317b9d0d06658f5744cfbd9"-    , "db881e6d0dc3b62793d7da5fe5a18e33be9b93f4a63a00a878dfbecf0d383bd2"-    , "f743ce1b72f3de4c901369eed581c626ed3081ca707e6634fdaff46721ce0878"-    , "cd52da3ec8a839c537dacdea8506a3eeee879de388ff5e513322d6d1bb3ff694"-    , "a5bdd57cb8fde6298e7c5e563afcca60dd472eca484bd8c3cc17f3307be09b601744dd3ab9e8a44107c5868824575f850c0f399b280cf198006f83ede8c0b537e9be227fa140b65995ad9dfa1f2303d560c3b7f59bedd93c1282ea263924469411c2653f87fd814c74cb91c148430481d64bad0fec3cbb3dd1f39aa55c36f81b"-    ],-    -- COUNT = 11-    [ "93161b2dc08cb0fd50171141c865a841ca935cfdd2b5907d6ff8ab0348c4ceb0"-    , "5cb9f6e5912b90c3349a50ab881b35a1"-    , "0dceb4a36326c4df1685df43fddeecb5d0c76f00eb44826694f27e610290f6e1"-    , "d8e9be44b5f293482548d4787762ebfb03c73c40e45385e8b98907cd66f493dd"-    , "105a8f85d6959f3e043ef508cfea21d52123f03b7aea8034c4eec761eaba1fee"-    , "bf781f7e489d9b4b5aa5ee6d1796468af672a8d25f311edf3c4b4dbf433d703f"-    , "c81d6bcf1e5bf37e39dda1735c6f193df115b1a854a12e7cafe060afe4589335"-    , "4306628124d0100fade7eaaf5edf227d50771f9e5f2e1e983800eef9a39fde0b0c280e63c8728d836b5b93ea794a32c1c04cfc54bd5300e3febb5fe2e1023eded8d7cd180279a598f76823e8d5a7dffcc93a09deec5d1f80838e938fba4de9f47e94b99382ae55f116df9c3b3ddf7e50516e203645852a415796f03a86418107"-    ],-    -- COUNT = 12-    [ "1ae12a5e4e9a4a5bfa79da30a9e6c62ffc639572ef1254194d129a16eb53c716"-    , "5399b3481fdf24d373222267790a0fec"-    , "8280cfdcd7a575816e0199e115da0ea77cae9d30b49c891a6c225e9037ba67e2"-    , "681554ff702658122e91ba017450cfdfc8e3f4911153f7bcc428403e9c7b9d68"-    , "226732b7a457cf0ac0ef09fd4f81296573b49a68de5e7ac3070e148c95e8e323"-    , "45942b5e9a1a128e85e12c34596374ddc85fd7502e5633c7390fc6e6f1e5ef56"-    , "6fc59929b41e77072886aff45f737b449b105ed7eacbd74c7cbfedf533dbeaa1"-    , "b7547332e1509663fcfea2128f7f3a3df484cd8df034b00199157d35d61e35f1a9d481c7d2e81305616d70fc371ee459b0b2267d627e928590edcac3231898b24ef378aa9c3d381619f665379be76c7c1bd535505c563db3725f034786e35bdd90429305fd71d7bf680e8cdd6d4c348d97078f5cf5e89dee2dc410fad4f2a30f"-    ],-    -- COUNT = 13-    [ "29e20d724dfa459960df21c6ec76b1e6cabd23a9e9456d6c591d7e4529da0ef8"-    , "95df1f837eba47a1687aa5c4ddcf8aaf"-    , "3713b601e164b1a51dda1ca9242ff477514648e90d311a06e10ce5aa15da5d7f"-    , "2a2a312626ca3e20034fc4f28033c7d573f66ef61ab2ea0c7bf0411a9d247264"-    , "ec68be33ac8ff3dd127e051604898c0f9a501271859376653a0516336180993d"-    , "9935499661d699a00c622a875441b4df5204958fe95892c8ce67f7dfb2be3e4a"-    , "256a4ba9e8f439d5487fa5eb45efcf1bc1120491724db3abe328d951f2739fc9"-    , "73114cb3624d687d4cd49a6e769dfc7a3f8901dc41f6ad1df4ce480536fa82e52ae958d0528640d92b8bb981b755058e32c4733682e5c4c0df41f3505a1643a0dd49cfdeaf7a18adffca88256c6d2cceb838af6c92a64bc21cb7a760a0391291bfe3575e014fc156323f8eb5e86518c669dad8d29ad5fd4ef6e296f4a0764c26"-    ],-    -- COUNT = 14-    [ "1353f3543eb1134980e061fc4382394975dbc74f1f1ea5ecc02780a813ac5ee6"-    , "cf584db2447afbe2c8fa0c15575ee391"-    , "345b0cc016f2765a8c33fc24f1dcfa182cbe29d7eacbcdc9bcda988521458fc2"-    , "ba60219332a67b95d90ec9de6b8453d4c8af991ae9277461ff3af1b92fc985d3"-    , "6964b9b9842aec9c7ec2aad926d701f30eec76fe699265ae2a7765d716958069"-    , "6a03c28a9365c558c33d3fdc7e5ebf0b4d32caac70df71403fd70ced09757528"-    , "a58546c72a0b4d47c9bd6c19e7cf4ab73b2d7ba36c6c6dc08606f608795ebd29"-    , "5b029ef68b6799868b04dc28dbea26bc2fa9fcc8c2b2795aafeed0127b7297fa19a4ef2ba60c42ff8259d5a759f92bd90fdfb27145e82d798bb3ab7fd60bfaefb7aefb116ca2a4fa8b01d96a03c47c8d987fdd33c460e560b138891278313bb619d0c3c6f9d7c5a37e88fce83e94943705c6ff68e00484e74ad4097b0c9e5f10"-    ]-    ]
− test/Network/Haskoin/Crypto/SignatureSpec.hs
@@ -1,165 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.Crypto.SignatureSpec (spec) where--import           Data.Bits              (testBit)-import           Data.ByteString        (ByteString)-import qualified Data.ByteString        as BS (index, length)-import           Data.Serialize         as S-import           Data.Text              (Text)-import           Network.Haskoin.Crypto (SecKey, Sig, decodeStrictSig,-                                         derivePubKey, exportSig,-                                         importCompactSig, isCanonicalHalfOrder,-                                         sha256, signHash, verifyHashSig)-import           Network.Haskoin.Test   (arbitrarySignature)-import           Network.Haskoin.Util   (decodeHex, eitherToMaybe, lst3)-import           Test.Hspec             (Spec, describe, it)-import           Test.Hspec.QuickCheck  (prop)-import           Test.HUnit             (Assertion, assertBool)-import           Test.QuickCheck        (forAll)--spec :: Spec-spec = do-    describe "signatures" $ do-        prop "verify signature" $-            forAll arbitrarySignature $ \(msg, key, sig) ->-                verifyHashSig msg sig (derivePubKey key)-        prop "s component less than half order" $-            forAll arbitrarySignature $ isCanonicalHalfOrder . lst3-        prop "encoded signature is canonical" $-            forAll arbitrarySignature $ testIsCanonical . lst3-        prop "encode signature and decode strictly" $-            forAll arbitrarySignature $-            (\s -> decodeStrictSig (exportSig s) == Just s) . lst3-        prop "encodes and decodes signature" $-            forAll arbitrarySignature $-            (\s -> decodeStrictSig (exportSig s) == Just s) . lst3-    describe "trezor rfc6979 test vectors" $ do-        it "rfc6979 test vector 1" (testSigning $ head detVec)-        it "rfc6979 test vector 2" (testSigning $ detVec !! 1)-        it "rfc6979 test vector 3" (testSigning $ detVec !! 2)-        it "rfc6979 test vector 4" (testSigning $ detVec !! 3)-        it "rfc6979 test vector 5" (testSigning $ detVec !! 4)-        it "rfc6979 test vector 6" (testSigning $ detVec !! 5)-        it "rfc6979 test vector 7" (testSigning $ detVec !! 6)-        it "rfc6979 test vector 8" (testSigning $ detVec !! 7)-        it "rfc6979 test vector 9" (testSigning $ detVec !! 8)-        it "rfc6979 test vector 10" (testSigning $ detVec !! 9)-        it "rfc6979 test vector 11" (testSigning $ detVec !! 10)-        it "rfc6979 test vector 12" (testSigning $ detVec !! 11)---- No longer validating that generated signature is exactly equal to provided--- one. Fedora includes libsecp256k1 from Bitcoin ABC that computes--- deterministic signatures using a slightly different nonce generation--- algorithm.-testSigning :: (SecKey, ByteString, Text) -> Assertion-testSigning (prv, msg, str) = do-    assertBool "my sig valid" $ verifyHashSig msg' g (derivePubKey prv)-    assertBool "valid sig" $ verifyHashSig msg' g' (derivePubKey prv)-  where-    Just g' = importCompactSig =<< eitherToMaybe . decode =<< decodeHex str-    g = signHash prv msg'-    msg' = sha256 msg---{- ECDSA Canonical -}---- github.com/bitcoin/bitcoin/blob/master/src/script.cpp--- from function IsCanonicalSignature-testIsCanonical :: Sig -> Bool-testIsCanonical sig = not $-    -- Non-canonical signature: too short-    (len < 8) ||-    -- Non-canonical signature: too long-    (len > 72) ||-    -- Non-canonical signature: wrong type-    (BS.index s 0 /= 0x30) ||-    -- Non-canonical signature: wrong length marker-    (BS.index s 1 /= len - 2) ||-    -- Non-canonical signature: S length misplaced-    (5 + rlen >= len) ||-    -- Non-canonical signature: R+S length mismatch-    (rlen + slen + 6 /= len) ||-    -- Non-canonical signature: R value type mismatch-    (BS.index s 2 /= 0x02) ||-    -- Non-canonical signature: R length is zero-    (rlen == 0) ||-    -- Non-canonical signature: R value negative-    testBit (BS.index s 4) 7 ||-    -- Non-canonical signature: R value excessively padded-    (  rlen > 1-    && BS.index s 4 == 0-    && not (testBit (BS.index s 5) 7)-    ) ||-    -- Non-canonical signature: S value type mismatch-    (BS.index s (fromIntegral rlen + 4) /= 0x02) ||-    -- Non-canonical signature: S length is zero-    (slen == 0) ||-    -- Non-canonical signature: S value negative-    testBit (BS.index s (fromIntegral rlen+6)) 7 ||-    -- Non-canonical signature: S value excessively padded-    (  slen > 1-    && BS.index s (fromIntegral rlen + 6) == 0-    && not (testBit (BS.index s (fromIntegral rlen + 7)) 7)-    )-  where-    s = exportSig sig-    len = fromIntegral $ BS.length s-    rlen = BS.index s 3-    slen = BS.index s (fromIntegral rlen + 5)---{- Trezor RFC 6979 Test Vectors -}--- github.com/trezor/python-ecdsa/blob/master/ecdsa/test_pyecdsa.py--detVec :: [(SecKey, ByteString, Text)]-detVec =-    [-      ( "0000000000000000000000000000000000000000000000000000000000000001"-      , "Satoshi Nakamoto"-      , "934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d82442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5"-      )-    , ( "0000000000000000000000000000000000000000000000000000000000000001"-      , "All those moments will be lost in time, like tears in rain. Time to die..."-      , "8600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21"-      )-    , ( "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140"-      , "Satoshi Nakamoto"-      , "fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d06b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5"-      )-    , ( "f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181"-      , "Alan Turing"-      , "7063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c58dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea"-      )-    , ( "e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2"-      , "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!"-      , "b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6"-      )-    , ( "0000000000000000000000000000000000000000000000000000000000000001"-      , "Everything should be made as simple as possible, but not simpler."-      , "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c96f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"-      )-    , ( "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140"-      , "Equations are more important to me, because politics is for the present, but an equation is something for eternity."-      , "54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5"-      )-    , ( "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140"-      , "Not only is the Universe stranger than we think, it is stranger than we can think."-      , "ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd06fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283"-      )-    , ( "0000000000000000000000000000000000000000000000000000000000000001"-      , "How wonderful that we have met with a paradox. Now we have some hope of making progress."-      , "c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d375afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3"-      )-    , ( "69ec59eaa1f4f2e36b639716b7c30ca86d9a5375c7b38d8918bd9c0ebc80ba64"-      , "Computer science is no more about computers than astronomy is about telescopes."-      , "7186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d0de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6"-      )-    , ( "00000000000000000000000000007246174ab1e92e9149c6e446fe194d072637"-      , "...if you aren't, at any given time, scandalized by code you wrote five or even three years ago, you're not learning anywhere near enough"-      , "fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda4870e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37"-      )-    , ( "000000000000000000000000000000000000000000056916d0f9b31dc9b637f3"-      , "The question of whether computers can think is like the question of whether submarines can swim."-      , "cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf906ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef"-      )-    ]
− test/Network/Haskoin/CryptoSpec.hs
@@ -1,165 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.CryptoSpec (spec) where--import           Control.Monad-import           Data.ByteString           (ByteString)-import qualified Data.ByteString.Char8     as C (pack)-import           Data.Maybe                (fromMaybe, isJust, isNothing)-import           Data.Text                 (Text)-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto-import           Network.Haskoin.Keys-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.HUnit                (Assertion, assertBool)---- Unit tests copied from bitcoind implementation--- https://github.com/bitcoin/bitcoin/blob/master/src/test/key_tests.cpp--strSecret1 :: Text-strSecret1  = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"--strSecret2 :: Text-strSecret2  = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"--strSecret1C :: Text-strSecret1C = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"--strSecret2C :: Text-strSecret2C = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"--addr1 :: Text-addr1  = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ"--addr2 :: Text-addr2  = "1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ"--addr1C :: Text-addr1C = "1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs"--addr2C :: Text-addr2C = "1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs"--strAddressBad :: Text-strAddressBad = "1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF"--sigMsg :: [ByteString]-sigMsg =-    [ mconcat ["Very secret message ", C.pack (show (i :: Int)), ": 11"]-    | i <- [0..15]-    ]--sec1 :: SecKeyI-sec1 =-    fromMaybe (error "Could not decode WIF secret 1") (fromWif btc strSecret1)--sec2 :: SecKeyI-sec2 =-    fromMaybe (error "Could not decode WIF secret 2") (fromWif btc strSecret2)--sec1C :: SecKeyI-sec1C =-    fromMaybe-        (error "Could not decode WIF compressed secret 1")-        (fromWif btc strSecret1C)--sec2C :: SecKeyI-sec2C =-    fromMaybe-        (error "Could not decode WIF compressed secret 2")-        (fromWif btc strSecret2C)--pub1 :: PubKeyI-pub1  = derivePubKeyI sec1--pub2 :: PubKeyI-pub2  = derivePubKeyI sec2--pub1C :: PubKeyI-pub1C = derivePubKeyI sec1C--pub2C :: PubKeyI-pub2C = derivePubKeyI sec2C--spec :: Spec-spec =-    describe "bitcoind /src/test/key_tests.cpp" $ do-        it "decode valid wif" checkPrivkey-        it "decode invalid wif" checkInvalidKey-        it "decode minikey format" checkMiniKey-        it "check private key compression" checkPrvKeyCompressed-        it "check public key compression" checkKeyCompressed-        it "check matching address" checkMatchingAddress-        it "check various signatures" sigCheck--sigCheck :: Assertion-sigCheck = forM_ sigMsg $ checkSignatures . doubleSHA256--{- bitcoind /src/test/key_tests.cpp -}--checkPrivkey :: Assertion-checkPrivkey = do-    assertBool "Key 1"  $ isJust $ fromWif btc strSecret1-    assertBool "Key 2"  $ isJust $ fromWif btc strSecret2-    assertBool "Key 1C" $ isJust $ fromWif btc strSecret1C-    assertBool "Key 2C" $ isJust $ fromWif btc strSecret2C--checkInvalidKey :: Assertion-checkInvalidKey =-    assertBool "Bad key" $ isNothing $ fromWif btc strAddressBad--checkMiniKey :: Assertion-checkMiniKey =-    assertBool "Bad mini key" $-    isJust res && fromMiniKey "S6c56bnXQiBjk9mqSYE7ykVQ7NzrRy" == res-  where-    res = do-        bs <--            decodeHex-                "4C7A9640C72DC2099F23715D0C8A0D8A35F8906E3CAB61DD3F78B67BF887C9AB"-        wrapSecKey False <$> secKey bs--checkPrvKeyCompressed :: Assertion-checkPrvKeyCompressed = do-    assertBool "Key 1"  $ not $ secKeyCompressed sec1-    assertBool "Key 2"  $ not $ secKeyCompressed sec2-    assertBool "Key 1C" $ secKeyCompressed sec1C-    assertBool "Key 2C" $ secKeyCompressed sec2C--checkKeyCompressed :: Assertion-checkKeyCompressed = do-    assertBool "Key 1"  $ not $ pubKeyCompressed pub1-    assertBool "Key 2"  $ not $ pubKeyCompressed pub2-    assertBool "Key 1C" $ pubKeyCompressed pub1C-    assertBool "Key 2C" $ pubKeyCompressed pub2C--checkMatchingAddress :: Assertion-checkMatchingAddress = do-    assertBool "Key 1"  $ Just addr1  == addrToString btc (pubKeyAddr pub1)-    assertBool "Key 2"  $ Just addr2  == addrToString btc (pubKeyAddr pub2)-    assertBool "Key 1C" $ Just addr1C == addrToString btc (pubKeyAddr pub1C)-    assertBool "Key 2C" $ Just addr2C == addrToString btc (pubKeyAddr pub2C)--checkSignatures :: Hash256 -> Assertion-checkSignatures h = do-    let sign1  = signHash (secKeyData sec1) h-        sign2  = signHash (secKeyData sec2) h-        sign1C = signHash (secKeyData sec1C) h-        sign2C = signHash (secKeyData sec2C) h-    assertBool "Key 1, Sign1"   $ verifyHashSig h sign1 (pubKeyPoint pub1)-    assertBool "Key 1, Sign2"   $ not $ verifyHashSig h sign2 (pubKeyPoint pub1)-    assertBool "Key 1, Sign1C"  $ verifyHashSig h sign1C (pubKeyPoint pub1)-    assertBool "Key 1, Sign2C"  $ not $ verifyHashSig h sign2C (pubKeyPoint pub1)-    assertBool "Key 2, Sign1"   $ not $ verifyHashSig h sign1 (pubKeyPoint pub2)-    assertBool "Key 2, Sign2"   $ verifyHashSig h sign2 (pubKeyPoint pub2)-    assertBool "Key 2, Sign1C"  $ not $ verifyHashSig h sign1C (pubKeyPoint pub2)-    assertBool "Key 2, Sign2C"  $ verifyHashSig h sign2C (pubKeyPoint pub2)-    assertBool "Key 1C, Sign1"  $ verifyHashSig h sign1 (pubKeyPoint pub1C)-    assertBool "Key 1C, Sign2"  $ not $ verifyHashSig h sign2 (pubKeyPoint pub1C)-    assertBool "Key 1C, Sign1C" $ verifyHashSig h sign1C (pubKeyPoint pub1C)-    assertBool "Key 1C, Sign2C" $ not $ verifyHashSig h sign2C (pubKeyPoint pub1C)-    assertBool "Key 2C, Sign1"  $ not $ verifyHashSig h sign1 (pubKeyPoint pub2C)-    assertBool "Key 2C, Sign2"  $ verifyHashSig h sign2 (pubKeyPoint pub2C)-    assertBool "Key 2C, Sign1C" $ not $ verifyHashSig h sign1C (pubKeyPoint pub2C)-    assertBool "Key 2C, Sign2C" $ verifyHashSig h sign2C (pubKeyPoint pub2C)
− test/Network/Haskoin/Keys/ExtendedSpec.hs
@@ -1,521 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.Keys.ExtendedSpec (spec) where--import           Data.Aeson                 as A-import           Data.Aeson.Types           as A-import           Data.Bits                  ((.&.))-import qualified Data.ByteString.Lazy.Char8 as B8-import           Data.Either                (isLeft)-import           Data.Map.Strict            (singleton)-import           Data.Maybe                 (fromJust, isJust, isNothing)-import           Data.Serialize             as S-import           Data.String                (fromString)-import           Data.String.Conversions    (cs)-import           Data.Text                  (Text)-import           Data.Word                  (Word32)-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Keys-import           Network.Haskoin.Test-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.HUnit                 (Assertion, assertBool, assertEqual)-import           Test.QuickCheck            hiding ((.&.))--spec :: Spec-spec = do-    describe "bip32 derivation vector 1" $ do-        it "chain m" $ runXKeyVec (head xKeyVec)-        it "chain m/0'" $ runXKeyVec (xKeyVec !! 1)-        it "chain m/0'/1" $ runXKeyVec (xKeyVec !! 2)-        it "chain m/0'/1/2'" $ runXKeyVec (xKeyVec !! 3)-        it "chain m/0'/1/2'/2" $ runXKeyVec (xKeyVec !! 4)-        it "chain m/0'/1/2'/2/1000000000" $ runXKeyVec (xKeyVec !! 5)-    describe "bip32 subkey derivation vector 2" $ do-        it "chain m" $ runXKeyVec (head xKeyVec2)-        it "chain m/0" $ runXKeyVec (xKeyVec2 !! 1)-        it "chain m/0/2147483647'" $ runXKeyVec (xKeyVec2 !! 2)-        it "chain m/0/2147483647'/1" $ runXKeyVec (xKeyVec2 !! 3)-        it "chain m/0/2147483647'/1/2147483646'" $ runXKeyVec (xKeyVec2 !! 4)-        it "Chain m/0/2147483647'/1/2147483646'/2" $ runXKeyVec (xKeyVec2 !! 5)-    describe "bip32 subkey derivation using string path" $ do-        it "either derivations" testApplyPath-        it "either derivations" testBadApplyPath-        it "dublic derivations" testDerivePubPath-        it "private derivations" testDerivePrvPath-        it "path parsing" testParsePath-        it "from json" testFromJsonPath-        it "to json" testToJsonPath-    describe "extended keys" $ do-        let net = btc-        it "computes pubkey of a subkey is subkey of the pubkey" $-            property $ forAll arbitraryXPrvKey pubKeyOfSubKeyIsSubKeyOfPubKey-        it "exports and imports extended private key" $-            property $-            forAll arbitraryXPrvKey $ \k ->-                xPrvImport net (xPrvExport net k) == Just k-        it "exports and imports extended public key" $-            property $-            forAll arbitraryXPubKey $ \(_, k) ->-                xPubImport net (xPubExport net k) == Just k-        it "show and read derivation path" $-            property $ forAll arbitraryDerivPath $ \p -> read (show p) == p-        it "show and read hard derivation path" $-            property $ forAll arbitraryHardPath $ \p -> read (show p) == p-        it "show and read soft derivation path" $-            property $ forAll arbitrarySoftPath $ \p -> read (show p) == p-        it "from string derivation path" $-            property $-            forAll arbitraryDerivPath $ \p -> fromString (cs $ pathToStr p) == p-        it "from string hard derivation path" $-            property $-            forAll arbitraryHardPath $ \p -> fromString (cs $ pathToStr p) == p-        it "from string soft derivation path" $-            property $-            forAll arbitrarySoftPath $ \p -> fromString (cs $ pathToStr p) == p-        it "from and to lists of derivation paths" $-            property $-            forAll arbitraryDerivPath $ \p -> listToPath (pathToList p) == p-        it "from and to lists of hard derivation paths" $-            property $-            forAll arbitraryHardPath $ \p ->-                toHard (listToPath $ pathToList p) == Just p-        it "from and to lists of soft derivation paths" $-            property $-            forAll arbitrarySoftPath $ \p ->-                toSoft (listToPath $ pathToList p) == Just p-        it "read and show parsed path" $-            property $ forAll arbitraryParsedPath $ \p -> read (show p) == p-        it "encodes and decodes extended private key" $-            forAll-                arbitraryXPrvKey-                (testCustom (xPrvFromJSON net) (xPrvToJSON net))-        it "encodes and decodes extended public key" $-            forAll-                arbitraryXPubKey-                (testCustom (xPubFromJSON net) (xPubToJSON net) . snd)-        it "encodes and decodes derivation path" $-            forAll arbitraryDerivPath testID-        it "encodes and decodes parsed derivation path" $-            forAll arbitraryParsedPath testID-        it "encodes and decodes extended private key" $-            property $-            forAll arbitraryXPrvKey $-            testPutGet (getXPrvKey net) (putXPrvKey net)-        it "shows and reads extended private key" $-            property $-            forAll arbitraryXPrvKey $ \k -> read (show k) `shouldBe` k-        it "shows and reads extended private key" $-            property $-            forAll arbitraryXPubKey $ \(prv, pub) ->-                read (show (prv, pub)) `shouldBe` (prv, pub)--testFromJsonPath :: Assertion-testFromJsonPath =-    sequence_ $ do-        path <- jsonPathVectors-        return $-            assertEqual-                path-                (Just [fromString path :: DerivPath])-                (A.decode $ B8.pack $ "[\"" ++ path ++ "\"]")--testToJsonPath :: Assertion-testToJsonPath =-    sequence_ $ do-        path <- jsonPathVectors-        return $-            assertEqual-                path-                (B8.pack $ "[\"" ++ path ++ "\"]")-                (A.encode [fromString path :: ParsedPath])--jsonPathVectors :: [String]-jsonPathVectors =-    [ "m"-    , "m/0"-    , "m/0'"-    , "M/0'"-    , "m/2147483647"-    , "M/2147483647"-    , "m/1/2/3/4/5/6/7/8"-    , "M/1/2/3/4/5/6/7/8"-    , "m/1'/2'/3/4"-    , "M/1'/2'/3/4"-    ]--testParsePath :: Assertion-testParsePath =-    sequence_ $ do-        (path, t) <- parsePathVectors-        return $ assertBool path (t $ parsePath path)--parsePathVectors :: [(String, Maybe ParsedPath -> Bool)]-parsePathVectors =-    [ ("m", isJust)-    , ("m/0'", isJust)-    , ("M/0'", isJust)-    , ("m/2147483648", isNothing)-    , ("m/2147483647", isJust)-    , ("M/2147483648", isNothing)-    , ("M/2147483647", isJust)-    , ("M/-1", isNothing)-    , ("M/-2147483648", isNothing)-    , ("m/1/2/3/4/5/6/7/8", isJust)-    , ("M/1/2/3/4/5/6/7/8", isJust)-    , ("m/1'/2'/3/4", isJust)-    , ("M/1'/2'/3/4", isJust)-    , ("m/1/2'/3/4'", isJust)-    , ("M/1/2'/3/4'", isJust)-    , ("meh", isNothing)-    , ("infinity", isNothing)-    , ("NaN", isNothing)-    ]--testApplyPath :: Assertion-testApplyPath =-    sequence_ $ do-        (key, path, final) <- applyPathVectors-        return $-            assertEqual path final $ applyPath (fromJust $ parsePath path) key--testBadApplyPath :: Assertion-testBadApplyPath =-    sequence_ $ do-        (key, path) <- badApplyPathVectors-        return $-            assertBool path $ isLeft $ applyPath (fromJust $ parsePath path) key--testDerivePubPath :: Assertion-testDerivePubPath =-    sequence_ $ do-        (key, path, final) <- derivePubPathVectors-        return $-            assertEqual path final $-            derivePubPath (fromString path :: SoftPath) key--testDerivePrvPath :: Assertion-testDerivePrvPath =-    sequence_ $ do-        (key, path, final) <- derivePrvPathVectors-        return $-            assertEqual path final $-            derivePath (fromString path :: DerivPath) key--derivePubPathVectors :: [(XPubKey, String, XPubKey)]-derivePubPathVectors =-    [ ( xpub, "M", xpub )-    , ( xpub, "M/8", pubSubKey xpub 8 )-    , ( xpub, "M/8/30/1", foldl pubSubKey xpub [8,30,1] )-    ]-  where-    xprv = fromJust $ xPrvImport btc-        "xprv9s21ZrQH143K46iDVRSyFfGfMgQjzC4BV3ZUfNbG7PHQrJjE53ofAn5gYkp6KQ\-        \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB"-    xpub = deriveXPubKey xprv--derivePrvPathVectors :: [(XPrvKey, String, XPrvKey)]-derivePrvPathVectors =-    [ ( xprv, "m", xprv )-    , ( xprv, "M", xprv )-    , ( xprv, "m/8'", hardSubKey xprv 8 )-    , ( xprv, "M/8'", hardSubKey xprv 8 )-    , ( xprv, "m/8'/30/1"-      , foldl prvSubKey (hardSubKey xprv 8) [30,1]-      )-    , ( xprv, "M/8'/30/1"-      , foldl prvSubKey (hardSubKey xprv 8) [30,1]-      )-    , ( xprv, "m/3/20"-      , foldl prvSubKey xprv [3,20]-      )-    , ( xprv, "M/3/20"-      , foldl prvSubKey xprv [3,20]-      )-    ]-  where-    xprv = fromJust $ xPrvImport btc-        "xprv9s21ZrQH143K46iDVRSyFfGfMgQjzC4BV3ZUfNbG7PHQrJjE53ofAn5gYkp6KQ\-        \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB"--applyPathVectors :: [(XKey, String, Either String XKey)]-applyPathVectors =-    [ (XPrv xprv btc, "m", Right (XPrv xprv btc))-    , (XPrv xprv btc, "M", Right (XPub xpub btc))-    , (XPrv xprv btc, "m/8'", Right (XPrv (hardSubKey xprv 8) btc))-    , ( XPrv xprv btc-      , "M/8'"-      , Right (XPub (deriveXPubKey (hardSubKey xprv 8)) btc))-    , ( XPrv xprv btc-      , "m/8'/30/1"-      , Right (XPrv (foldl prvSubKey (hardSubKey xprv 8) [30, 1]) btc))-    , ( XPrv xprv btc-      , "M/8'/30/1"-      , Right-            (XPub-                 (deriveXPubKey (foldl prvSubKey (hardSubKey xprv 8) [30, 1]))-                 btc))-    , (XPrv xprv btc, "m/3/20", Right (XPrv (foldl prvSubKey xprv [3, 20]) btc))-    , ( XPrv xprv btc-      , "M/3/20"-      , Right (XPub (deriveXPubKey (foldl prvSubKey xprv [3, 20])) btc))-    , ( XPub xpub btc-      , "M/3/20"-      , Right (XPub (deriveXPubKey (foldl prvSubKey xprv [3, 20])) btc))-    ]-  where-    xprv =-        fromJust $-        xPrvImport-            btc-            "xprv9s21ZrQH143K46iDVRSyFfGfMgQjzC4BV3ZUfNbG7PHQrJjE53ofAn5gYkp6KQ\-        \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB"-    xpub = deriveXPubKey xprv--badApplyPathVectors :: [(XKey, String)]-badApplyPathVectors =-    [ (XPub xpub btc, "m/8'")-    , (XPub xpub btc, "M/8'")-    , (XPub xpub btc, "M/1/2/3'/4/5")-    ]-  where-    xprv =-        fromJust $-        xPrvImport-            btc-            "xprv9s21ZrQH143K46iDVRSyFfGfMgQjzC4BV3ZUfNbG7PHQrJjE53ofAn5gYkp6KQ\-        \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB"-    xpub = deriveXPubKey xprv---runXKeyVec :: ([Text], XPrvKey) -> Assertion-runXKeyVec (v, m) = do-    assertBool "xPrvID" $ encodeHex (S.encode $ xPrvID m) == head v-    assertBool "xPrvFP" $ encodeHex (S.encode $ xPrvFP m) == v !! 1-    assertBool "xPrvAddr" $-        addrToString btc (xPubAddr $ deriveXPubKey m) == Just (v !! 2)-    assertBool "prvKey" $ encodeHex (getSecKey $ xPrvKey m) == v !! 3-    assertBool "xPrvWIF" $ xPrvWif btc m == v !! 4-    assertBool "pubKey" $-        encodeHex (exportPubKey True $ xPubKey $ deriveXPubKey m) == v !! 5-    assertBool "chain code" $ encodeHex (S.encode $ xPrvChain m) == v !! 6-    assertBool "Hex PubKey" $-        encodeHex (runPut $ putXPubKey btc $ deriveXPubKey m) == v !! 7-    assertBool "Hex PrvKey" $ encodeHex (runPut (putXPrvKey btc m)) == v !! 8-    assertBool "Base58 PubKey" $ xPubExport btc (deriveXPubKey m) == v !! 9-    assertBool "Base58 PrvKey" $ xPrvExport btc m == v !! 10---- BIP 0032 Test Vectors--- https://en.bitcoin.it/wiki/BIP_0032_TestVectors--xKeyVec :: [([Text], XPrvKey)]-xKeyVec = zip xKeyResVec $ foldl f [m] der-    where f acc d = acc ++ [d $ last acc]-          m   = makeXPrvKey $ fromJust $ decodeHex m0-          der = [ flip hardSubKey 0-                , flip prvSubKey 1-                , flip hardSubKey 2-                , flip prvSubKey 2-                , flip prvSubKey 1000000000-                ]--xKeyVec2 :: [([Text], XPrvKey)]-xKeyVec2 = zip xKeyResVec2 $ foldl f [m] der-    where f acc d = acc ++ [d $ last acc]-          m   = makeXPrvKey $ fromJust $ decodeHex m1-          der = [ flip prvSubKey 0-                , flip hardSubKey 2147483647-                , flip prvSubKey 1-                , flip hardSubKey 2147483646-                , flip prvSubKey 2-                ]--m0 :: Text-m0 = "000102030405060708090a0b0c0d0e0f"--xKeyResVec :: [[Text]]-xKeyResVec =-    [-      -- m-      [ "3442193e1bb70916e914552172cd4e2dbc9df811"-      , "3442193e"-      , "15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma"-      , "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"-      , "L52XzL2cMkHxqxBXRyEpnPQZGUs3uKiL3R11XbAdHigRzDozKZeW"-      , "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2"-      , "873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508"-      , "0488b21e000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d5080339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2"-      , "0488ade4000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d50800e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"-      , "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"-      , "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"-      ]-      -- m/0'-    , [ "5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7"-      , "5c1bd648"-      , "19Q2WoS5hSS6T8GjhK8KZLMgmWaq4neXrh"-      , "edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea"-      , "L5BmPijJjrKbiUfG4zbiFKNqkvuJ8usooJmzuD7Z8dkRoTThYnAT"-      , "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56"-      , "47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141"-      , "0488b21e013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56"-      , "0488ade4013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae623614100edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea"-      , "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"-      , "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7"-      ]-      -- m/0'/1-    , [ "bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe"-      , "bef5a2f9"-      , "1JQheacLPdM5ySCkrZkV66G2ApAXe1mqLj"-      , "3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368"-      , "KyFAjQ5rgrKvhXvNMtFB5PCSKUYD1yyPEe3xr3T34TZSUHycXtMM"-      , "03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c"-      , "2a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19"-      , "0488b21e025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c1903501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c"-      , "0488ade4025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19003c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368"-      , "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"-      , "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"-      ]-      -- m/0'/1/2'-    , [ "ee7ab90cde56a8c0e2bb086ac49748b8db9dce72"-      , "ee7ab90c"-      , "1NjxqbA9aZWnh17q1UW3rB4EPu79wDXj7x"-      , "cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca"-      , "L43t3od1Gh7Lj55Bzjj1xDAgJDcL7YFo2nEcNaMGiyRZS1CidBVU"-      , "0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2"-      , "04466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f"-      , "0488b21e03bef5a2f98000000204466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2"-      , "0488ade403bef5a2f98000000204466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f00cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca"-      , "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5"-      , "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM"-      ]-      -- m/0'/1/2'/2-    , [ "d880d7d893848509a62d8fb74e32148dac68412f"-      , "d880d7d8"-      , "1LjmJcdPnDHhNTUgrWyhLGnRDKxQjoxAgt"-      , "0f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4"-      , "KwjQsVuMjbCP2Zmr3VaFaStav7NvevwjvvkqrWd5Qmh1XVnCteBR"-      , "02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29"-      , "cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd"-      , "0488b21e04ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29"-      , "0488ade404ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd000f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4"-      , "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV"-      , "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334"-      ]-      -- m/0'/1/2'/2/1000000000-    , [ "d69aa102255fed74378278c7812701ea641fdf32"-      , "d69aa102"-      , "1LZiqrop2HGR4qrH1ULZPyBpU6AUP49Uam"-      , "471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8"-      , "Kybw8izYevo5xMh1TK7aUr7jHFCxXS1zv8p3oqFz3o2zFbhRXHYs"-      , "022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011"-      , "c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e"-      , "0488b21e05d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011"-      , "0488ade405d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e00471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8"-      , "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy"-      , "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76"-      ]-    ]--m1 :: Text-m1 = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"--xKeyResVec2 :: [[Text]]-xKeyResVec2 =-    [-      -- m-      [ "bd16bee53961a47d6ad888e29545434a89bdfe95"-      , "bd16bee5"-      , "1JEoxevbLLG8cVqeoGKQiAwoWbNYSUyYjg"-      , "4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e"-      , "KyjXhyHF9wTphBkfpxjL8hkDXDUSbE3tKANT94kXSyh6vn6nKaoy"-      , "03cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7"-      , "60499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689"-      , "0488b21e00000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd968903cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7"-      , "0488ade400000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689004b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e"-      , "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"-      , "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U"-      ]-      -- m/0-    , [ "5a61ff8eb7aaca3010db97ebda76121610b78096"-      , "5a61ff8e"-      , "19EuDJdgfRkwCmRzbzVBHZWQG9QNWhftbZ"-      , "abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e"-      , "L2ysLrR6KMSAtx7uPqmYpoTeiRzydXBattRXjXz5GDFPrdfPzKbj"-      , "02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea"-      , "f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c"-      , "0488b21e01bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea"-      , "0488ade401bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c00abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e"-      , "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"-      , "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt"-      ]-      -- m/0/2147483647'-    , [ "d8ab493736da02f11ed682f88339e720fb0379d1"-      , "d8ab4937"-      , "1Lke9bXGhn5VPrBuXgN12uGUphrttUErmk"-      , "877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93"-      , "L1m5VpbXmMp57P3knskwhoMTLdhAAaXiHvnGLMribbfwzVRpz2Sr"-      , "03c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b"-      , "be17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d9"-      , "0488b21e025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d903c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b"-      , "0488ade4025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d900877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93"-      , "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a"-      , "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9"-      ]-      -- m/0/2147483647'/1-    , [ "78412e3a2296a40de124307b6485bd19833e2e34"-      , "78412e3a"-      , "1BxrAr2pHpeBheusmd6fHDP2tSLAUa3qsW"-      , "704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7"-      , "KzyzXnznxSv249b4KuNkBwowaN3akiNeEHy5FWoPCJpStZbEKXN2"-      , "03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9"-      , "f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb"-      , "0488b21e03d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9"-      , "0488ade403d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb00704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7"-      , "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon"-      , "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef"-      ]-      -- m/0/2147483647'/1/2147483646'-    , [ "31a507b815593dfc51ffc7245ae7e5aee304246e"-      , "31a507b8"-      , "15XVotxCAV7sRx1PSCkQNsGw3W9jT9A94R"-      , "f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d"-      , "L5KhaMvPYRW1ZoFmRjUtxxPypQ94m6BcDrPhqArhggdaTbbAFJEF"-      , "02d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0"-      , "637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e29"-      , "0488b21e0478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2902d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0"-      , "0488ade40478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2900f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d"-      , "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"-      , "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc"-      ]-      -- m/0/2147483647'/1/2147483646'/2-    , [ "26132fdbe7bf89cbc64cf8dafa3f9f88b8666220"-      , "26132fdb"-      , "14UKfRV9ZPUp6ZC9PLhqbRtxdihW9em3xt"-      , "bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23"-      , "L3WAYNAZPxx1fr7KCz7GN9nD5qMBnNiqEJNJMU1z9MMaannAt4aK"-      , "024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c"-      , "9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271"-      , "0488b21e0531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c"-      , "0488ade40531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed27100bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23"-      , "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"-      , "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j"-      ]-    ]--pubKeyOfSubKeyIsSubKeyOfPubKey :: XPrvKey -> Word32 -> Bool-pubKeyOfSubKeyIsSubKeyOfPubKey k i =-    deriveXPubKey (prvSubKey k i') == pubSubKey (deriveXPubKey k) i'-  where-    i' = fromIntegral $ i .&. 0x7fffffff -- make it a public derivation---testID :: (FromJSON a, ToJSON a, Eq a) => a -> Bool-testID x =-    (A.decode . A.encode) (singleton ("object" :: String) x) ==-    Just (singleton ("object" :: String) x)--testCustom :: Eq a => (Value -> Parser a) -> (a -> Value) -> a -> Bool-testCustom f g x = parseMaybe f (g x) == Just x--testPutGet :: Eq a => Get a -> Putter a -> a -> Bool-testPutGet g p a = runGet g (runPut (p a)) == Right a
− test/Network/Haskoin/Keys/MnemonicSpec.hs
@@ -1,494 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.Keys.MnemonicSpec (spec) where--import           Control.Monad           (zipWithM_)-import           Data.Bits               (shiftR, (.&.))-import           Data.ByteString         (ByteString)-import qualified Data.ByteString         as BS-import qualified Data.ByteString.Char8   as C-import           Data.Either             (fromRight)-import           Data.List               (isPrefixOf)-import           Data.Maybe              (fromJust)-import           Data.Serialize          (Serialize, encode)-import           Data.String.Conversions (cs)-import           Data.Text               (Text)-import qualified Data.Text               as T-import           Data.Word               (Word32, Word64)-import           Network.Haskoin.Crypto-import           Network.Haskoin.Keys-import           Network.Haskoin.Test-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.HUnit-import           Test.QuickCheck         hiding ((.&.))--spec :: Spec-spec =-    describe "mnemonic" $ do-        it "entropy to mnemonic sentence" toMnemonicTest-        it "mnemonic sentence to entropy" fromMnemonicTest-        it "mnemonic sentence to seed" mnemonicToSeedTest-        it "mnemonic sentence with invalid checksum" fromMnemonicInvalidTest-        it "empty mnemonic sentence is invalid" $ sequence_ [emptyMnemonicTest]-        it "generate 12 words" $ property toMnemonic128-        it "generate 18 words" $ property toMnemonic160-        it "generate 24 words" $ property toMnemonic256-        it "generate 48 words" $ property toMnemonic512-        it "generate any number of words" $ property toMnemonicVar-        it "encode and decode 128-bit entropy" $ property fromToMnemonic128-        it "encode and decode 160-bit entropy" $ property fromToMnemonic160-        it "encode and decode 256-bit entropy" $ property fromToMnemonic256-        it "encode and decode 512-bit entropy" $ property fromToMnemonic512-        it "encode and decode n-bit entropy" $ property fromToMnemonicVar-        it "convert 128-bit mnemonic to seed" $ property mnemonicToSeed128-        it "convert 160-bit mnemonic to seed" $ property mnemonicToSeed160-        it "convert 256-bit mnemonic to seed" $ property mnemonicToSeed256-        it "convert 512-bit mnemonic to seed" $ property mnemonicToSeed512-        it "convert n-bit mnemonic to seed" $ property mnemonicToSeedVar-        it "get bits" $ property getBitsByteCount-        it "get end bits" $ property getBitsEndBits--toMnemonicTest :: Assertion-toMnemonicTest = zipWithM_ f ents mss-  where-    f e m = assertEqual "" m . h $ e-    h =-        fromRight (error "Could not decode mnemonic sentence") .-        toMnemonic . fromJust . decodeHex--fromMnemonicTest :: Assertion-fromMnemonicTest = zipWithM_ f ents mss-  where-    f e = assertEqual "" e . h-    h =-        encodeHex .-        fromRight (error "Could not decode mnemonic sentence") . fromMnemonic--mnemonicToSeedTest :: Assertion-mnemonicToSeedTest = zipWithM_ f mss seeds-  where-    f m s = assertEqual "" s . h $ m-    h =-        encodeHex .-        fromRight (error "Could not decode mnemonic seed") .-        mnemonicToSeed "TREZOR"--fromMnemonicInvalidTest :: Assertion-fromMnemonicInvalidTest = mapM_ f invalidMss-  where-    f = assertBool "" . h-    h m = case fromMnemonic m of-            Right _  -> False-            Left err -> "fromMnemonic: checksum failed:" `isPrefixOf` err--emptyMnemonicTest :: Assertion-emptyMnemonicTest =-    assertBool "" $-    case fromMnemonic "" of-        Right _  -> False-        Left err -> "fromMnemonic: empty mnemonic" `isPrefixOf` err--ents :: [Text]-ents =-    [ "00000000000000000000000000000000"-    , "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f"-    , "80808080808080808080808080808080"-    , "ffffffffffffffffffffffffffffffff"-    , "000000000000000000000000000000000000000000000000"-    , "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f"-    , "808080808080808080808080808080808080808080808080"-    , "ffffffffffffffffffffffffffffffffffffffffffffffff"-    , "0000000000000000000000000000000000000000000000000000000000000000"-    , "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f"-    , "8080808080808080808080808080808080808080808080808080808080808080"-    , "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"-    , "77c2b00716cec7213839159e404db50d"-    , "b63a9c59a6e641f288ebc103017f1da9f8290b3da6bdef7b"-    , "3e141609b97933b66a060dcddc71fad1d91677db872031e85f4c015c5e7e8982"-    , "0460ef47585604c5660618db2e6a7e7f"-    , "72f60ebac5dd8add8d2a25a797102c3ce21bc029c200076f"-    , "2c85efc7f24ee4573d2b81a6ec66cee209b2dcbd09d8eddc51e0215b0b68e416"-    , "eaebabb2383351fd31d703840b32e9e2"-    , "7ac45cfe7722ee6c7ba84fbc2d5bd61b45cb2fe5eb65aa78"-    , "4fa1a8bc3e6d80ee1316050e862c1812031493212b7ec3f3bb1b08f168cabeef"-    , "18ab19a9f54a9274f03e5209a2ac8a91"-    , "18a2e1d81b8ecfb2a333adcb0c17a5b9eb76cc5d05db91a4"-    , "15da872c95a13dd738fbf50e427583ad61f18fd99f628c417a61cf8343c90419"-    ]--mss :: [Mnemonic]-mss =-    [ "abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon about"-    , "legal winner thank year wave sausage worth useful legal winner thank\-      \ yellow"-    , "letter advice cage absurd amount doctor acoustic avoid letter advice\-      \ cage above"-    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong"-    , "abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon abandon abandon abandon abandon abandon agent"-    , "legal winner thank year wave sausage worth useful legal winner thank\-      \ year wave sausage worth useful legal will"-    , "letter advice cage absurd amount doctor acoustic avoid letter advice\-      \ cage absurd amount doctor acoustic avoid letter always"-    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\-      \ when"-    , "abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon abandon abandon art"-    , "legal winner thank year wave sausage worth useful legal winner thank\-      \ year wave sausage worth useful legal winner thank year wave sausage\-      \ worth title"-    , "letter advice cage absurd amount doctor acoustic avoid letter advice\-      \ cage absurd amount doctor acoustic avoid letter advice cage absurd\-      \ amount doctor acoustic bless"-    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\-      \ zoo zoo zoo zoo zoo vote"-    , "jelly better achieve collect unaware mountain thought cargo oxygen act\-      \ hood bridge"-    , "renew stay biology evidence goat welcome casual join adapt armor shuffle\-      \ fault little machine walk stumble urge swap"-    , "dignity pass list indicate nasty swamp pool script soccer toe leaf photo\-      \ multiply desk host tomato cradle drill spread actor shine dismiss\-      \ champion exotic"-    , "afford alter spike radar gate glance object seek swamp infant panel\-      \ yellow"-    , "indicate race push merry suffer human cruise dwarf pole review arch keep\-      \ canvas theme poem divorce alter left"-    , "clutch control vehicle tonight unusual clog visa ice plunge glimpse\-      \ recipe series open hour vintage deposit universe tip job dress radar\-      \ refuse motion taste"-    , "turtle front uncle idea crush write shrug there lottery flower risk\-      \ shell"-    , "kiss carry display unusual confirm curtain upgrade antique rotate hello\-      \ void custom frequent obey nut hole price segment"-    , "exile ask congress lamp submit jacket era scheme attend cousin alcohol\-      \ catch course end lucky hurt sentence oven short ball bird grab wing top"-    , "board flee heavy tunnel powder denial science ski answer betray cargo\-      \ cat"-    , "board blade invite damage undo sun mimic interest slam gaze truly\-      \ inherit resist great inject rocket museum chief"-    , "beyond stage sleep clip because twist token leaf atom beauty genius food\-      \ business side grid unable middle armed observe pair crouch tonight away\-      \ coconut"-    ]--seeds :: [Text]-seeds =-    [ "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a69\-      \87599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04"-    , "2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe1\-      \296106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607"-    , "d71de856f81a8acc65e6fc851a38d4d7ec216fd0796d0a6827a3ad6ed5511a30fa280f1\-      \2eb2e47ed2ac03b5c462a0358d18d69fe4f985ec81778c1b370b652a8"-    , "ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a133325729\-      \17f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069"-    , "035895f2f481b1b0f01fcf8c289c794660b289981a78f8106447707fdd9666ca06da5a9\-      \a565181599b79f53b844d8a71dd9f439c52a3d7b3e8a79c906ac845fa"-    , "f2b94508732bcbacbcc020faefecfc89feafa6649a5491b8c952cede496c214a0c7b3c3\-      \92d168748f2d4a612bada0753b52a1c7ac53c1e93abd5c6320b9e95dd"-    , "107d7c02a5aa6f38c58083ff74f04c607c2d2c0ecc55501dadd72d025b751bc27fe913f\-      \fb796f841c49b1d33b610cf0e91d3aa239027f5e99fe4ce9e5088cd65"-    , "0cd6e5d827bb62eb8fc1e262254223817fd068a74b5b449cc2f667c3f1f985a76379b43\-      \348d952e2265b4cd129090758b3e3c2c49103b5051aac2eaeb890a528"-    , "bda85446c68413707090a52022edd26a1c9462295029f2e60cd7c4f2bbd3097170af7a4\-      \d73245cafa9c3cca8d561a7c3de6f5d4a10be8ed2a5e608d68f92fcc8"-    , "bc09fca1804f7e69da93c2f2028eb238c227f2e9dda30cd63699232578480a4021b146a\-      \d717fbb7e451ce9eb835f43620bf5c514db0f8add49f5d121449d3e87"-    , "c0c519bd0e91a2ed54357d9d1ebef6f5af218a153624cf4f2da911a0ed8f7a09e2ef61a\-      \f0aca007096df430022f7a2b6fb91661a9589097069720d015e4e982f"-    , "dd48c104698c30cfe2b6142103248622fb7bb0ff692eebb00089b32d22484e1613912f0\-      \a5b694407be899ffd31ed3992c456cdf60f5d4564b8ba3f05a69890ad"-    , "b5b6d0127db1a9d2226af0c3346031d77af31e918dba64287a1b44b8ebf63cdd52676f6\-      \72a290aae502472cf2d602c051f3e6f18055e84e4c43897fc4e51a6ff"-    , "9248d83e06f4cd98debf5b6f010542760df925ce46cf38a1bdb4e4de7d21f5c39366941\-      \c69e1bdbf2966e0f6e6dbece898a0e2f0a4c2b3e640953dfe8b7bbdc5"-    , "ff7f3184df8696d8bef94b6c03114dbee0ef89ff938712301d27ed8336ca89ef9635da2\-      \0af07d4175f2bf5f3de130f39c9d9e8dd0472489c19b1a020a940da67"-    , "65f93a9f36b6c85cbe634ffc1f99f2b82cbb10b31edc7f087b4f6cb9e976e9faf76ff41\-      \f8f27c99afdf38f7a303ba1136ee48a4c1e7fcd3dba7aa876113a36e4"-    , "3bbf9daa0dfad8229786ace5ddb4e00fa98a044ae4c4975ffd5e094dba9e0bb289349db\-      \e2091761f30f382d4e35c4a670ee8ab50758d2c55881be69e327117ba"-    , "fe908f96f46668b2d5b37d82f558c77ed0d69dd0e7e043a5b0511c48c2f1064694a956f\-      \86360c93dd04052a8899497ce9e985ebe0c8c52b955e6ae86d4ff4449"-    , "bdfb76a0759f301b0b899a1e3985227e53b3f51e67e3f2a65363caedf3e32fde42a66c4\-      \04f18d7b05818c95ef3ca1e5146646856c461c073169467511680876c"-    , "ed56ff6c833c07982eb7119a8f48fd363c4a9b1601cd2de736b01045c5eb8ab4f57b079\-      \403485d1c4924f0790dc10a971763337cb9f9c62226f64fff26397c79"-    , "095ee6f817b4c2cb30a5a797360a81a40ab0f9a4e25ecd672a3f58a0b5ba0687c096a6b\-      \14d2c0deb3bdefce4f61d01ae07417d502429352e27695163f7447a8c"-    , "6eff1bb21562918509c73cb990260db07c0ce34ff0e3cc4a8cb3276129fbcb300bddfe0\-      \05831350efd633909f476c45c88253276d9fd0df6ef48609e8bb7dca8"-    , "f84521c777a13b61564234bf8f8b62b3afce27fc4062b51bb5e62bdfecb23864ee6ecf0\-      \7c1d5a97c0834307c5c852d8ceb88e7c97923c0a3b496bedd4e5f88a9"-    , "b15509eaa2d09d3efd3e006ef42151b30367dc6e3aa5e44caba3fe4d3e352e65101fbdb\-      \86a96776b91946ff06f8eac594dc6ee1d3e82a42dfe1b40fef6bcc3fd"-    ]--invalidMss :: [Mnemonic]-invalidMss =-    [ "abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon"-    , "legal winner thank year wave sausage worth useful legal winner thank\-      \ thank"-    , "letter advice cage absurd amount doctor acoustic avoid letter advice\-      \ cage sausage"-    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo"-    , "abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon abandon abandon abandon abandon abandon abandon"-    , "legal winner thank year wave sausage worth useful legal winner thank\-      \ year wave sausage worth useful legal letter"-    , "letter advice cage absurd amount doctor acoustic avoid letter advice\-      \ cage absurd amount doctor acoustic avoid letter abandon"-    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\-      \ zoo"-    , "abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon abandon abandon abandon"-    , "legal winner thank year wave sausage worth useful legal winner thank\-      \ year wave sausage worth useful legal winner thank year wave sausage\-      \ worth letter"-    , "letter advice cage absurd amount doctor acoustic avoid letter advice\-      \ cage absurd amount doctor acoustic avoid letter advice cage absurd\-      \ amount doctor acoustic letter"-    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\-      \ zoo zoo zoo zoo zoo zoo"-    , "jelly better achieve collect unaware mountain thought cargo oxygen act\-      \ hood zoo"-    , "renew stay biology evidence goat welcome casual join adapt armor shuffle\-      \ fault little machine walk stumble urge zoo"-    , "dignity pass list indicate nasty swamp pool script soccer toe leaf photo\-      \ multiply desk host tomato cradle drill spread actor shine dismiss\-      \ champion zoo"-    , "afford alter spike radar gate glance object seek swamp infant panel\-      \ zoo"-    , "indicate race push merry suffer human cruise dwarf pole review arch keep\-      \ canvas theme poem divorce alter zoo"-    , "clutch control vehicle tonight unusual clog visa ice plunge glimpse\-      \ recipe series open hour vintage deposit universe tip job dress radar\-      \ refuse motion zoo"-    , "turtle front uncle idea crush write shrug there lottery flower risk\-      \ zoo"-    , "kiss carry display unusual confirm curtain upgrade antique rotate hello\-      \ void custom frequent obey nut hole price zoo"-    , "exile ask congress lamp submit jacket era scheme attend cousin alcohol\-      \ catch course end lucky hurt sentence oven short ball bird grab wing zoo"-    , "board flee heavy tunnel powder denial science ski answer betray cargo\-      \ zoo"-    , "board blade invite damage undo sun mimic interest slam gaze truly\-      \ inherit resist great inject rocket museum zoo"-    , "beyond stage sleep clip because twist token leaf atom beauty genius food\-      \ business side grid unable middle armed observe pair crouch tonight away\-      \ zoo"-    ]--binWordsToBS :: Serialize a => [a] -> BS.ByteString-binWordsToBS = foldr f BS.empty-  where-    f b a = a `BS.append` encode b--{- Encode mnemonic -}--toMnemonic128 :: (Word64, Word64) -> Bool-toMnemonic128 (a, b) = l == 12-  where-    bs = encode a `BS.append` encode b-    l =-        length .-        T.words . fromRight (error "Could not decode mnemonic senttence") $-        toMnemonic bs--toMnemonic160 :: (Word32, Word64, Word64) -> Bool-toMnemonic160 (a, b, c) = l == 15-  where-    bs = BS.concat [encode a, encode b, encode c]-    l =-        length .-        T.words . fromRight (error "Could not decode mnemonic sentence") $-        toMnemonic bs--toMnemonic256 :: (Word64, Word64, Word64, Word64) -> Bool-toMnemonic256 (a, b, c, d) = l == 24-  where-    bs = BS.concat [encode a, encode b, encode c, encode d]-    l =-        length .-        T.words . fromRight (error "Could not decode mnemonic sentence") $-        toMnemonic bs--toMnemonic512 ::-    ((Word64, Word64, Word64, Word64), (Word64, Word64, Word64, Word64)) -> Bool-toMnemonic512 ((a, b, c, d), (e, f, g, h)) = l == 48-  where-    bs =-        BS.concat-            [ encode a-            , encode b-            , encode c-            , encode d-            , encode e-            , encode f-            , encode g-            , encode h-            ]-    l =-        length .-        T.words . fromRight (error "Could not decode mnemonic sentence") $-        toMnemonic bs--toMnemonicVar :: [Word32] -> Property-toMnemonicVar ls = not (null ls) && length ls <= 8 ==> l == wc-  where-    bs = binWordsToBS ls-    bl = BS.length bs-    cb = bl `div` 4-    wc = (cb + bl * 8) `div` 11-    l =-        length . T.words .-        fromRight (error "Could not decode mnemonic sentence") $-        toMnemonic bs--{- Encode/Decode -}--fromToMnemonic128 :: (Word64, Word64) -> Bool-fromToMnemonic128 (a, b) = bs == bs'-  where-    bs = encode a `BS.append` encode b-    bs' =-        fromRight-            (error "Could not decode mnemonic entropy")-            (fromMnemonic =<< toMnemonic bs)--fromToMnemonic160 :: (Word32, Word64, Word64) -> Bool-fromToMnemonic160 (a, b, c) = bs == bs'-  where-    bs = BS.concat [encode a, encode b, encode c]-    bs' =-        fromRight-            (error "Could not decode mnemonic entropy")-            (fromMnemonic =<< toMnemonic bs)--fromToMnemonic256 :: (Word64, Word64, Word64, Word64) -> Bool-fromToMnemonic256 (a, b, c, d) = bs == bs'-  where-    bs = BS.concat [encode a, encode b, encode c, encode d]-    bs' =-        fromRight-            (error "Could not decode mnemonic entropy")-            (fromMnemonic =<< toMnemonic bs)--fromToMnemonic512 ::-    ((Word64, Word64, Word64, Word64), (Word64, Word64, Word64, Word64)) -> Bool-fromToMnemonic512 ((a, b, c, d), (e, f, g, h)) = bs == bs'-  where-    bs =-        BS.concat-            [ encode a-            , encode b-            , encode c-            , encode d-            , encode e-            , encode f-            , encode g-            , encode h-            ]-    bs' =-        fromRight-            (error "Could not decode mnemonic entropy")-            (fromMnemonic =<< toMnemonic bs)--fromToMnemonicVar :: [Word32] -> Property-fromToMnemonicVar ls = not (null ls) && length ls <= 8 ==> bs == bs'-  where-    bs = binWordsToBS ls-    bs' =-        fromRight-            (error "Could not decode mnemonic entropy")-            (fromMnemonic =<< toMnemonic bs)--{- Mnemonic to seed -}--mnemonicToSeed128 :: (Word64, Word64) -> Bool-mnemonicToSeed128 (a, b) = l == 64-  where-    bs = encode a `BS.append` encode b-    seed =-        fromRight-            (error "Could not decode mnemonic seed")-            (mnemonicToSeed "" =<< toMnemonic bs)-    l = BS.length seed--mnemonicToSeed160 :: (Word32, Word64, Word64) -> Bool-mnemonicToSeed160 (a, b, c) = l == 64-  where-    bs = BS.concat [encode a, encode b, encode c]-    seed =-        fromRight-            (error "Could not decode mnemonic seed")-            (mnemonicToSeed "" =<< toMnemonic bs)-    l = BS.length seed--mnemonicToSeed256 :: (Word64, Word64, Word64, Word64) -> Bool-mnemonicToSeed256 (a, b, c, d) = l == 64-  where-    bs = BS.concat [encode a, encode b, encode c, encode d]-    seed =-        fromRight-            (error "Could not decode mnemonic seed")-            (mnemonicToSeed "" =<< toMnemonic bs)-    l = BS.length seed--mnemonicToSeed512 ::-    ((Word64, Word64, Word64, Word64), (Word64, Word64, Word64, Word64)) -> Bool-mnemonicToSeed512 ((a, b, c, d), (e, f, g, h)) = l == 64-  where-    bs =-        BS.concat-            [ encode a-            , encode b-            , encode c-            , encode d-            , encode e-            , encode f-            , encode g-            , encode h-            ]-    seed =-        fromRight-            (error "Could not decode mnemonic seed")-            (mnemonicToSeed "" =<< toMnemonic bs)-    l = BS.length seed--mnemonicToSeedVar :: [Word32] -> Property-mnemonicToSeedVar ls = not (null ls) && length ls <= 16 ==> l == 64-  where-    bs = binWordsToBS ls-    seed =-        fromRight-            (error "Could not decode mnemonic seed")-            (mnemonicToSeed "" =<< toMnemonic bs)-    l = BS.length seed--{- Get bits from ByteString -}--data ByteCountGen = ByteCountGen BS.ByteString Int deriving Show--instance Arbitrary ByteCountGen where-    arbitrary = do-        bs <- arbitraryBS-        i <- choose (0, BS.length bs * 8)-        return $ ByteCountGen bs i--getBitsByteCount :: ByteCountGen -> Bool-getBitsByteCount (ByteCountGen bs i) = BS.length bits == l-  where-    (q, r) = i `quotRem` 8-    bits = getBits i bs-    l = if r == 0 then q else q + 1--getBitsEndBits :: ByteCountGen -> Bool-getBitsEndBits (ByteCountGen bs i) =-    (r == 0) || (BS.last bits .&. (0xff `shiftR` r) == 0x00)-  where-    r = i `mod` 8-    bits = getBits i bs
− test/Network/Haskoin/KeysSpec.hs
@@ -1,72 +0,0 @@-module Network.Haskoin.KeysSpec (spec) where--import           Data.Aeson                as A-import qualified Data.ByteString           as BS-import           Data.Map.Strict           (singleton)-import           Data.Serialize            as S-import           Data.String               (fromString)-import           Data.String.Conversions   (cs)-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto-import           Network.Haskoin.Keys-import           Network.Haskoin.Test-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.QuickCheck--spec :: Spec-spec =-    describe "keys" $ do-        let net = btc-        it "is public key canonical" $-            property $ forAll arbitraryKeyPair (isCanonicalPubKey . snd)-        it "encode and decode wif private keys" $-            property $-            forAll arbitraryKeyPair $ \(pk, _) ->-                fromWif net (toWif net pk) == Just pk-        it "encode and decode serialized private key" $-            property $ forAll arbitrary binaryPrvKey-        it "read and show public key" $-            property $ forAll arbitraryKeyPair $ \(_, k) -> read (show k) == k-        it "read and show private key" $-            property $ forAll arbitrarySecKeyI $ \k -> read (show k) == k-        it "from string public key" $-            property $-            forAll arbitraryKeyPair $ \(_, k) ->-                fromString (cs . encodeHex $ S.encode k) == k-        it "encode and decode json public key" $-            property $ forAll arbitraryKeyPair (testID . snd)-        it "encodes and decodes serialized public key" $-            property $ forAll arbitraryKeyPair $ cerealID . snd----- github.com/bitcoin/bitcoin/blob/master/src/script.cpp--- from function IsCanonicalPubKey-isCanonicalPubKey :: PubKeyI -> Bool-isCanonicalPubKey p = not $-    -- Non-canonical public key: too short-    (BS.length bs < 33) ||-    -- Non-canonical public key: invalid length for uncompressed key-    (BS.index bs 0 == 4 && BS.length bs /= 65) ||-    -- Non-canonical public key: invalid length for compressed key-    (BS.index bs 0 `elem` [2,3] && BS.length bs /= 33) ||-    -- Non-canonical public key: compressed nor uncompressed-    (BS.index bs 0 `notElem` [2,3,4])-  where-    bs = S.encode p--{- Key formats -}--binaryPrvKey :: SecKey -> Bool-binaryPrvKey k =-    Right k == runGet secKeyGet (runPut (secKeyPut k)) &&-    Just k == secKey (getSecKey k)--testID :: (FromJSON a, ToJSON a, Eq a) => a -> Bool-testID x =-    (A.decode . A.encode) (singleton ("object" :: String) x) ==-    Just (singleton ("object" :: String) x)--cerealID :: (Serialize a, Eq a) => a -> Bool-cerealID x = S.decode (S.encode x) == Right x
− test/Network/Haskoin/NetworkSpec.hs
@@ -1,187 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.NetworkSpec (spec) where--import           Data.Maybe                  (fromJust)-import           Data.Serialize              as S-import           Data.Text                   (Text)-import           Data.Word                   (Word32)-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Keys-import           Network.Haskoin.Network-import           Network.Haskoin.Test-import           Network.Haskoin.Transaction-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.HUnit                  (Assertion, assertBool,-                                              assertEqual)-import           Test.QuickCheck--spec :: Spec-spec = do-    let net = btc-    describe "bloom filters" $ do-        it "bloom filter vector 1" bloomFilter1-        it "bloom filter vector 2" bloomFilter2-        it "bloom filter vector 3" bloomFilter3-    describe "relevant bloom filter update" $ do-        it "Relevant Update" relevantOutputUpdated-        it "Irrelevant Update" irrelevantOutputNotUpdated-    describe "serialization of protocol types" $ do-        it "encodes and decodes varint" $-            property $ forAll arbitraryVarInt cerealID-        it "encodes and decodes varstring" $-            property $ forAll arbitraryVarString cerealID-        it "encodes and decodes network address" $-            property $ forAll arbitraryNetworkAddress cerealID-        it "encodes and decodes invtype" $-            property $ forAll arbitraryInvType cerealID-        it "encodes and decodes invvector" $-            property $ forAll arbitraryInvVector cerealID-        it "encodes and decodes inv" $ property $ forAll arbitraryInv1 cerealID-        it "encodes and decodes version" $-            property $ forAll arbitraryVersion cerealID-        it "encodes and decodes addr" $ property $ forAll arbitraryAddr1 cerealID-        it "encodes and decodes alert" $ property $ forAll arbitraryAlert cerealID-        it "encodes and decodes reject" $-            property $ forAll arbitraryReject cerealID-        it "encodes and decodes getdata" $-            property $ forAll arbitraryGetData cerealID-        it "encodes and decodes notfound" $-            property $ forAll arbitraryNotFound cerealID-        it "encodes and decodes ping" $ property $ forAll arbitraryPing cerealID-        it "encodes and decodes pong" $ property $ forAll arbitraryPong cerealID-        it "encodes and decodes message command" $-            property $ forAll arbitraryMessageCommand cerealID-        it "encodes and decodes message header" $-            property $ forAll arbitraryMessageHeader cerealID-        it "encodes and decodes message" $-            property $-            forAll (arbitraryMessage net) $-            testPutGet (getMessage net) (putMessage net)-    describe "serialization of bloom types" $ do-        it "encodes and decodes bloom flags" $-            property $ forAll arbitraryBloomFlags cerealID-        it "encodes and decodes bloom filter" $-            property $ forAll arbitraryBloomFilter $ cerealID . lst3-        it "encodes and decodes filterload" $-            property $ forAll arbitraryFilterLoad cerealID-        it "encodes and decodes filteradd" $-            property $ forAll arbitraryFilterAdd cerealID--bloomFilter :: Word32 -> Text -> Assertion-bloomFilter n x = do-    assertBool "Bloom filter doesn't contain vector 1" $ bloomContains f1 v1-    assertBool "Bloom filter contains something it should not" $-        not $ bloomContains f1 v2-    assertBool "Bloom filter doesn't contain vector 3" $ bloomContains f3 v3-    assertBool "Bloom filter doesn't contain vector 4" $ bloomContains f4 v4-    assertBool "Bloom filter serialization is incorrect" $-        S.encode f4 == bs-  where-    f0 = bloomCreate 3 0.01 n BloomUpdateAll-    f1 = bloomInsert f0 v1-    f3 = bloomInsert f1 v3-    f4 = bloomInsert f3 v4-    v1 = fromJust $ decodeHex "99108ad8ed9bb6274d3980bab5a85c048f0950c8"-    v2 = fromJust $ decodeHex "19108ad8ed9bb6274d3980bab5a85c048f0950c8"-    v3 = fromJust $ decodeHex "b5a2c786d9ef4658287ced5914b37a1b4aa32eee"-    v4 = fromJust $ decodeHex "b9300670b4c5366e95b2699e8b18bc75e5f729c5"-    bs = fromJust $ decodeHex x--bloomFilter1 :: Assertion-bloomFilter1 = bloomFilter 0 "03614e9b050000000000000001"--bloomFilter2 :: Assertion-bloomFilter2 = bloomFilter 2147483649 "03ce4299050000000100008001"--bloomFilter3 :: Assertion-bloomFilter3 =-    assertBool "Bloom filter serialization is incorrect" $-        S.encode f2 == bs-  where-    f0 = bloomCreate 2 0.001 0 BloomUpdateAll-    f1 = bloomInsert f0 $ S.encode p-    f2 = bloomInsert f1 $ S.encode $ getAddrHash160 $ pubKeyAddr p-    k = fromJust $ fromWif btc "5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C"-    p = derivePubKeyI k-    bs = fromJust $ decodeHex "038fc16b080000000000000001"--cerealID :: (Serialize a, Eq a) => a -> Bool-cerealID x = S.decode (S.encode x) == Right x--testPutGet :: Eq a => Get a -> Putter a -> a -> Bool-testPutGet g p a = runGet g (runPut (p a)) == Right a--relevantOutputUpdated :: Assertion-relevantOutputUpdated = assertBool "Bloom filter output updated" $-    any (bloomContains bf2) spendTxInput-    where-        bf0 = bloomCreate 10 0.000001 0 BloomUpdateAll-        relevantOutputHash = fromJust $ decodeHex"03f47604ea2736334151081e13265b4fe38e6fa8"-        bf1 = bloomInsert bf0 relevantOutputHash-        bf2 = fromJust $ bloomRelevantUpdate bf1 relevantTx-        spendTxInput = (encode .prevOutput) <$> txIn spendRelevantTx--irrelevantOutputNotUpdated :: Assertion-irrelevantOutputNotUpdated = assertEqual "Bloom filter not updated" Nothing bf2-    where-        bf0 = bloomCreate 10 0.000001 0 BloomUpdateAll-        relevantOutputHash = fromJust $ decodeHex"03f47604ea2736334151081e13265b4fe38e6fa8"-        bf1 = bloomInsert bf0 relevantOutputHash-        bf2 = bloomRelevantUpdate bf1 unrelatedTx-        spendTxInput = (encode .prevOutput) <$> txIn spendRelevantTx---- Random transaction (57dc904f32ad4daab7b321dd469e8791ad09df784cdd273a73985150a4f225e9)-relevantTx :: Tx-relevantTx = Tx-        { txVersion = 1-        , txIn = [ TxIn-            { prevOutput = OutPoint "35fe9017b7e3af592920b56fa06ac02faf0c52cdb19dcb416129ac71c95d060e" 1-            , scriptInput = fromJust $ decodeHex "473044022032fc8eef299b7e94b9a986a6aa2dcb9733ab804bef80df995e443b9c1f8c604202203335df7a2e2b4789451cdb4b2b05a786a81c51519eb6a567fd6fe8cd7b2d33fe014104272502dc63a512dad1473cb82a71be9baf4f4303abd1ff6028fc8a78e1f3aec1218907119dec14f07354850758ff0948e88a904fa411c4df7d5444414ec64ad6"-            , txInSequence = 4294967295-            } ]-        , txOut =-            [ TxOut { outValue = 100000000, scriptOutput = fromJust $ decodeHex "76a91403f47604ea2736334151081e13265b4fe38e6fa888ac" }-            , TxOut { outValue = 107980000, scriptOutput = fromJust $ decodeHex "76a91481cc186a2f4a69f633ed4bf10ef4a78be13effdd88ac" }-            ]-        , txWitness = []-        , txLockTime = 0-        }---- Transaction that spends above (fd6e3b693b844aa431fad46765c1aa019a6b13aebfa9dae916b3ffa43283a300)-spendRelevantTx :: Tx-spendRelevantTx = Tx-        { txVersion = 1-        , txIn = [ TxIn-            { prevOutput = OutPoint "57dc904f32ad4daab7b321dd469e8791ad09df784cdd273a73985150a4f225e9" 0-            , scriptInput = fromJust $ decodeHex "483045022100ecc334821e4e94cc2fdc841d5ad147d5bb942b993ba81460cc446e0410afa811022015fcbc542b734dbb61a05ec06012095096de5839c50808fe56f2b315e877c20d012103fb64e5792fa586172339b776b7017d3d529358cb73be6406a1fc994228d14f88"-            , txInSequence = 4294967295-            }, TxIn-            { prevOutput = OutPoint "cfee6a8d6e68e8fd16df6fff010afffcd19d7e075aa7b707dd1bae6adc420042" 0-            , scriptInput = fromJust $ decodeHex "47304402200e6bb95fa606f254d17089d83c4ceeb19c5d1699b4faddcd4f1f1568286e6b650220087fb8439f31e1b30e47710d095422405f601d6151f2f93e125e1a08a6e29ad4012103b49252e8fc6d5b49c8d14ee71fab45591df4a126a6c453c724f3d356e38f0cee"-            , txInSequence = 4294967295-            } ]-        , txOut =-            [ TxOut { outValue = 3851100, scriptOutput = fromJust $ decodeHex "76a914a297cae82a9a3b932bf023ae274fe2585295c9ca88ac" }-            , TxOut { outValue = 111000000, scriptOutput = fromJust $ decodeHex "76a9148f952c38600a61385974acc30a64f74407f9801488ac" }-            ]-        , txWitness = []-        , txLockTime = 0-        }---- This random transaction is unrelated to the others-unrelatedTx :: Tx-unrelatedTx = Tx-        { txVersion = 1-        , txIn = [ TxIn-            { prevOutput = OutPoint "3ec3a71431c68e5d978a5fb4a0a1081d8bee8384d8aa4c06b1fbaf9413e2214f" 20-            , scriptInput = fromJust $ decodeHex "483045022100ec9c202c9d3140b973aca9d7f21a82138aa4cfa43fddc5419098ac5e26a6f152022010848fd688f290ae010fb5cb493410caa03145fc12445900ec1ad2bde33aecd9012102c7445e72d723f99a0064526c28269d07f47c8fd81531a94a8d3bf5ebd5e23ef1"-            , txInSequence = 4294967295-            }]-        , txOut =-            [ TxOut { outValue = 12600000, scriptOutput = fromJust $ decodeHex "76a9148fef3b7051de8cc44e966159e7ea37f4520187e888ac" }-            ]-        , txWitness = []-        , txLockTime = 0-        }
− test/Network/Haskoin/ScriptSpec.hs
@@ -1,406 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.ScriptSpec (spec) where--import           Control.Monad-import           Data.Aeson                  as A-import           Data.ByteString             (ByteString)-import qualified Data.ByteString             as B-import qualified Data.ByteString.Lazy        as L-import           Data.Either-import           Data.List-import           Data.Map.Strict             (singleton)-import           Data.Maybe-import           Data.Monoid                 ((<>))-import           Data.Serialize              as S-import           Data.String-import           Data.String.Conversions     (cs)-import           Data.Text                   (Text)-import           Data.Word-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Keys-import           Network.Haskoin.Script-import           Network.Haskoin.Test-import           Network.Haskoin.Transaction-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.HUnit                  as HUnit-import           Test.QuickCheck-import           Text.Read--spec :: Spec-spec = do-    let net = btc-    describe "btc scripts" $ props btc-    describe "bch scripts" $ props bch-    describe "multi signatures" $-        zipWithM_ (curry mapMulSigVector) mulSigVectors [0 ..]-    describe "signature decoding" $-        zipWithM_ (curry (sigDecodeMap net)) scriptSigSignatures [0 ..]-    describe "json serialization" $ do-        it "encodes and decodes script output" $-            forAll (arbitraryScriptOutput net) testID-        it "encodes and decodes outpoint" $ forAll arbitraryOutPoint testID-        it "encodes and decodes sighash" $ forAll arbitrarySigHash testID-        it "encodes and decodes siginput" $-            forAll (arbitrarySigInput net) (testID . fst)-    describe "script serialization" $ do-        it "encodes and decodes script op" $-            property $ forAll arbitraryScriptOp cerealID-        it "encodes and decodes script" $-            property $ forAll arbitraryScript cerealID--props :: Network -> Spec-props net = do-    standardSpec net-    strictSigSpec net-    scriptSpec net-    txSigHashForkIdSpec net-    forkIdScriptSpec net-    sigHashSpec net-    txSigHashSpec net--cerealID :: (Serialize a, Eq a) => a -> Bool-cerealID x = S.decode (S.encode x) == Right x--standardSpec :: Network -> Spec-standardSpec net = do-    it "has intToScriptOp . scriptOpToInt identity" $-        property $-        forAll arbitraryIntScriptOp $ \i ->-            intToScriptOp <$> scriptOpToInt i `shouldBe` Right i-    it "has decodeOutput . encodeOutput identity" $-        property $-        forAll (arbitraryScriptOutput net) $ \so ->-            decodeOutput (encodeOutput so) `shouldBe` Right so-    it "has decodeInput . encodeOutput identity" $-        property $-        forAll (arbitraryScriptInput net) $ \si ->-            decodeInput net (encodeInput si) `shouldBe` Right si-    it "can sort multisig scripts" $-        forAll arbitraryMSOutput $ \out ->-            map S.encode (getOutputMulSigKeys (sortMulSig out)) `shouldSatisfy` \xs ->-                xs == sort xs-    it "can decode inputs with empty signatures" $ do-        decodeInput net (Script [OP_0]) `shouldBe`-            Right (RegularInput (SpendPK TxSignatureEmpty))-        decodeInput net (Script [opPushData ""]) `shouldBe`-            Right (RegularInput (SpendPK TxSignatureEmpty))-        let pk =-                derivePubKeyI $-                wrapSecKey True $ fromJust $ secKey $ B.replicate 32 1-        decodeInput net (Script [OP_0, opPushData $ S.encode pk]) `shouldBe`-            Right (RegularInput (SpendPKHash TxSignatureEmpty pk))-        decodeInput net (Script [OP_0, OP_0]) `shouldBe`-            Right (RegularInput (SpendMulSig [TxSignatureEmpty]))-        decodeInput net (Script [OP_0, OP_0, OP_0, OP_0]) `shouldBe`-            Right (RegularInput (SpendMulSig $ replicate 3 TxSignatureEmpty))--scriptSpec :: Network -> Spec-scriptSpec net =-    when (getNetworkName net == "btc") $-    it "can verify standard scripts from script_tests.json file" $ do-        xs <- readTestFile "script_tests" :: IO [A.Value]-        let vectorsA =-                mapMaybe (A.decode . A.encode) xs :: [( String-                                                      , String-                                                      , String-                                                      , String-                                                      , String)]-            vectorsB =-                mapMaybe (A.decode . A.encode) xs :: [( [Word64]-                                                      , String-                                                      , String-                                                      , String-                                                      , String-                                                      , String)]-            vectors =-                map (\(a, b, c, d, e) -> ([0], a, b, c, d, e)) vectorsA <>-                vectorsB-        length vectors `shouldBe` 86-        forM_ vectors $ \([val], siStr, soStr, flags, res, desc)-          -- We can disable specific tests by adding a DISABLED flag in the data-         ->-            unless ("DISABLED" `isInfixOf` flags) $ do-                let _strict =-                        "DERSIG" `isInfixOf` flags ||-                        "STRICTENC" `isInfixOf` flags ||-                        "NULLDUMMY" `isInfixOf` flags-                    scriptSig     = parseScript siStr-                    scriptPubKey  = parseScript soStr-                    decodedOutput = decodeOutputBS scriptPubKey-                    ver = either (const False) $ \so ->-                        verifyStdInput-                            net-                            (spendTx scriptPubKey 0 scriptSig)-                            0-                            so-                            (val * 100000000)-                case res of-                    "OK" -> assertBool desc $ ver decodedOutput-                    _    -> assertBool desc (not $ ver decodedOutput)--forkIdScriptSpec :: Network -> Spec-forkIdScriptSpec net =-    when (isJust (getSigHashForkId net)) $-    it "can verify scripts from forkid_script_tests.json file" $ do-        xs <- readTestFile "forkid_script_tests" :: IO [A.Value]-        let vectors =-                mapMaybe (A.decode . A.encode) xs :: [( [Word64]-                                                      , String-                                                      , String-                                                      , String-                                                      , String-                                                      , String)]-        length vectors `shouldBe` 3-        forM_ vectors $ \([valBTC], siStr, soStr, _, res, _) -> do-            let val = valBTC * 100000000-                scriptSig = parseScript siStr-                scriptPubKey = parseScript soStr-                decodedOutput =-                    fromRight (error $ "Could not decode output: " <> soStr) $-                    decodeOutputBS scriptPubKey-                ver =-                    verifyStdInput-                        net-                        (spendTx scriptPubKey val scriptSig)-                        0-                        decodedOutput-                        val-            case res of-                "OK" -> ver `shouldBe` True-                _    -> ver `shouldBe` False--creditTx :: ByteString -> Word64 -> Tx-creditTx scriptPubKey val =-    Tx 1 [txI] [txO] [] 0-  where-    txO = TxOut {outValue = val, scriptOutput = scriptPubKey}-    txI =-        TxIn-        { prevOutput = nullOutPoint-        , scriptInput = S.encode $ Script [OP_0, OP_0]-        , txInSequence = maxBound-        }--spendTx :: ByteString -> Word64 -> ByteString -> Tx-spendTx scriptPubKey val scriptSig =-    Tx 1 [txI] [txO] [] 0-  where-    txO = TxOut {outValue = val, scriptOutput = B.empty}-    txI =-        TxIn-        { prevOutput = OutPoint (txHash $ creditTx scriptPubKey val) 0-        , scriptInput = scriptSig-        , txInSequence = maxBound-        }--parseScript :: String -> ByteString-parseScript str =-    B.concat $ fromMaybe err $ mapM f $ words str-  where-    f = decodeHex . cs . dropHex . replaceToken-    dropHex ('0':'x':xs) = xs-    dropHex xs           = xs-    err = error $ "Could not decode script: " <> str--replaceToken :: String -> String-replaceToken str = case readMaybe $ "OP_" <> str of-    Just opcode -> "0x" <> cs (encodeHex $ S.encode (opcode :: ScriptOp))-    _           -> str--strictSigSpec :: Network -> Spec-strictSigSpec net =-    when (getNetworkName net == "btc") $ do-        it "can decode strict signatures" $ do-            xs <- readTestFile "sig_strict"-            let vectors = mapMaybe decodeHex xs-            length vectors `shouldBe` 3-            forM_ vectors $ \sig ->-                decodeTxSig net sig `shouldSatisfy` isRight-        it "can detect non-strict signatures" $ do-            xs <- readTestFile "sig_nonstrict"-            let vectors = mapMaybe decodeHex xs-            length vectors `shouldBe` 17-            forM_ vectors $ \sig ->-                decodeTxSig net sig `shouldSatisfy` isLeft--txSigHashSpec :: Network -> Spec-txSigHashSpec net =-    when (getNetworkName net == "btc") $-    it "can produce valid sighashes from sighash.json test vectors" $ do-        xs <- readTestFile "sighash" :: IO [A.Value]-        let vectors =-                mapMaybe (A.decode . A.encode) xs :: [( String-                                                      , String-                                                      , Int-                                                      , Integer-                                                      , String)]-        length vectors `shouldBe` 500-        forM_ vectors $ \(txStr, scpStr, i, shI, resStr) -> do-            let tx = fromString txStr-                s =-                    fromMaybe (error $ "Could not decode script: " <> cs scpStr) $-                    eitherToMaybe . S.decode =<< decodeHex (cs scpStr)-                sh = fromIntegral shI-                res =-                    eitherToMaybe . S.decode . B.reverse =<<-                    decodeHex (cs resStr)-            Just (txSigHash net tx s 0 i sh) `shouldBe` res--txSigHashForkIdSpec :: Network -> Spec-txSigHashForkIdSpec net =-    when (getNetworkName net == "btc") $-    it "can produce valid sighashes from forkid_sighash.json test vectors" $ do-        xs <- readTestFile "forkid_sighash" :: IO [A.Value]-        let vectors =-                mapMaybe (A.decode . A.encode) xs :: [( String-                                                      , String-                                                      , Int-                                                      , Word64-                                                      , Integer-                                                      , String)]-        length vectors `shouldBe` 13-        forM_ vectors $ \(txStr, scpStr, i, val, shI, resStr) -> do-            let tx = fromString txStr-                s =-                    fromMaybe (error $ "Could not decode script: " <> cs scpStr) $-                    eitherToMaybe . S.decode =<< decodeHex (cs scpStr)-                sh = fromIntegral shI-                res = eitherToMaybe . S.decode =<< decodeHex (cs resStr)-            Just (txSigHashForkId net tx s val i sh) `shouldBe` res--sigHashSpec :: Network -> Spec-sigHashSpec net = do-    it "can read . show" $-        property $ forAll arbitrarySigHash $ \sh -> read (show sh) `shouldBe` sh-    it "can correctly show" $ do-        show (0x00 :: SigHash) `shouldBe` "SigHash " <> show (0x00 :: Word32)-        show (0x01 :: SigHash) `shouldBe` "SigHash " <> show (0x01 :: Word32)-        show (0xff :: SigHash) `shouldBe` "SigHash " <> show (0xff :: Word32)-        show (0xabac3344 :: SigHash) `shouldBe` "SigHash " <>-            show (0xabac3344 :: Word32)-    it "can add a forkid" $ do-        0x00 `sigHashAddForkId` 0x00 `shouldBe` 0x00-        0xff `sigHashAddForkId` 0x00ffffff `shouldBe` 0xffffffff-        0xffff `sigHashAddForkId` 0x00aaaaaa `shouldBe` 0xaaaaaaff-        0xffff `sigHashAddForkId` 0xaaaaaaaa `shouldBe` 0xaaaaaaff-        0xffff `sigHashAddForkId` 0x00004444 `shouldBe` 0x004444ff-        0xff01 `sigHashAddForkId` 0x44440000 `shouldBe` 0x44000001-        0xff03 `sigHashAddForkId` 0x00550000 `shouldBe` 0x55000003-    it "can extract a forkid" $ do-        sigHashGetForkId 0x00000000 `shouldBe` 0x00000000-        sigHashGetForkId 0x80000000 `shouldBe` 0x00800000-        sigHashGetForkId 0xffffffff `shouldBe` 0x00ffffff-        sigHashGetForkId 0xabac3403 `shouldBe` 0x00abac34-    it "can build some vectors" $ do-        sigHashAll `shouldBe` 0x01-        sigHashNone `shouldBe` 0x02-        sigHashSingle `shouldBe` 0x03-        setForkIdFlag sigHashAll `shouldBe` 0x41-        setAnyoneCanPayFlag sigHashAll `shouldBe` 0x81-        setAnyoneCanPayFlag (setForkIdFlag sigHashAll) `shouldBe` 0xc1-    it "can test flags" $ do-        hasForkIdFlag sigHashAll `shouldBe` False-        hasForkIdFlag (setForkIdFlag sigHashAll) `shouldBe` True-        hasAnyoneCanPayFlag sigHashAll `shouldBe` False-        hasAnyoneCanPayFlag (setAnyoneCanPayFlag sigHashAll) `shouldBe` True-        isSigHashAll sigHashNone `shouldBe` False-        isSigHashAll sigHashAll `shouldBe` True-        isSigHashNone sigHashSingle `shouldBe` False-        isSigHashNone sigHashNone `shouldBe` True-        isSigHashSingle sigHashAll `shouldBe` False-        isSigHashSingle sigHashSingle `shouldBe` True-        isSigHashUnknown sigHashAll `shouldBe` False-        isSigHashUnknown sigHashNone `shouldBe` False-        isSigHashUnknown sigHashSingle `shouldBe` False-        isSigHashUnknown 0x00 `shouldBe` True-        isSigHashUnknown 0x04 `shouldBe` True-    it "can decodeTxSig . encode a TxSignature" $-        property $-        forAll (arbitraryTxSignature net) $ \(_, _, ts) ->-            decodeTxSig net (encodeTxSig ts) `shouldBe` Right ts-    it "can produce the sighash one" $-        property $-        forAll (arbitraryTx net) $ forAll arbitraryScript . testSigHashOne net--testSigHashOne :: Network -> Tx -> Script -> Word64 -> Bool -> Property-testSigHashOne net tx s val acp =-    not (null $ txIn tx) ==>-    if length (txIn tx) > length (txOut tx)-        then res `shouldBe` one-        else res `shouldNotBe` one-  where-    res = txSigHash net tx s val (length (txIn tx) - 1) (f sigHashSingle)-    one = "0100000000000000000000000000000000000000000000000000000000000000"-    f =-        if acp-            then setAnyoneCanPayFlag-            else id--{-- Test Utilities --}--readTestFile :: A.FromJSON a => FilePath -> IO a-readTestFile fp = do-    bs <- L.readFile $ "data/" <> fp <> ".json"-    maybe (error $ "Could not read test file " <> fp) return $ A.decode bs--{- Parse tests from bitcoin-qt repository -}--mapMulSigVector :: ((Text, Text), Int) -> Spec-mapMulSigVector (v, i) =-    it name $ runMulSigVector v-  where-    name = "check multisig vector " <> show i--runMulSigVector :: (Text, Text) -> Assertion-runMulSigVector (a, ops) = assertBool "multisig vector" $ Just a == b-  where-    s = do-        s' <- decodeHex ops-        eitherToMaybe $ S.decode s'-    b = do-        o <- s-        d <- eitherToMaybe $ decodeOutput o-        addrToString btc $ payToScriptAddress d--sigDecodeMap :: Network -> (Text, Int) -> Spec-sigDecodeMap net (_, i) =-    it ("check signature " ++ show i) func-  where-    func = testSigDecode net $ scriptSigSignatures !! i--testSigDecode :: Network -> Text -> Assertion-testSigDecode net str =-    let bs = fromJust $ decodeHex str-        eitherSig = decodeTxSig net bs-    in assertBool-           (unwords-                [ "Decode failed:"-                , fromLeft (error "Decode did not fail") eitherSig-                ]) $-       isRight eitherSig--mulSigVectors :: [(Text, Text)]-mulSigVectors =-    [ ( "3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC"-      , "52410491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8334f864104865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccffef4ec687441048d2455d2403e08708fc1f556002f1b6cd83f992d085097f9974ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f322a1863d4621353ae"-      )-    ]--scriptSigSignatures :: [Text]-scriptSigSignatures =-     -- Signature in input of txid 1983a69265920c24f89aac81942b1a59f7eb30821a8b3fb258f88882b6336053-    [ "304402205ca6249f43538908151fe67b26d020306c0e59fa206cf9f3ccf641f33357119d02206c82f244d04ac0a48024fb9cc246b66e58598acf206139bdb7b75a2941a2b1e401"-      -- Signature in input of txid-      -- fb0a1d8d34fa5537e461ac384bac761125e1bfa7fec286fa72511240fa66864d.-      -- Strange DER sizes, but in Blockchain. Now invalid as Haskoin can only-      -- decode strict signatures.-      -- "3048022200002b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378db4b14e736602220000334a96676e58b1bb01784cb7c556dd8ce1c220171904da22e18fe1e7d1510db501"-    ]---testID :: (FromJSON a, ToJSON a, Eq a) => a -> Bool-testID x =-    (A.decode . A.encode) (singleton ("object" :: String) x) ==-    Just (singleton ("object" :: String) x)
− test/Network/Haskoin/Transaction/PartialSpec.hs
@@ -1,336 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Network.Haskoin.Transaction.PartialSpec (spec) where--import           Control.Monad.Fail                  (MonadFail)-import           Data.ByteString                     (ByteString)-import           Data.Either                         (fromRight, isLeft,-                                                      isRight)-import           Data.HashMap.Strict                 (fromList, singleton)-import           Data.Maybe                          (fromJust, isJust)-import           Data.Serialize                      as S-import           Data.Text                           (Text)-import           Test.Hspec-import           Test.HUnit                          (Assertion, assertBool,-                                                      assertEqual)-import           Test.QuickCheck--import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto-import           Network.Haskoin.Keys-import           Network.Haskoin.Script-import           Network.Haskoin.Test-import           Network.Haskoin.Transaction-import           Network.Haskoin.Transaction.Partial-import           Network.Haskoin.Util--spec :: Spec-spec = describe "partially signed bitcoin transaction unit tests" $ do-    it "encodes trivial psbt" $-        encodeHex (S.encode trivialPSBT) == trivialPSBTHex-    it "decodes trivial psbt" $-        decodeHexPSBT trivialPSBTHex == Right trivialPSBT-    it "encodes and decodes non-empty transactions" $-        S.decode (S.encode nonEmptyTransactionPSBT) == Right nonEmptyTransactionPSBT-    it "does not decode invalid bip vectors" $-        mapM_ invalidVecTest invalidVec-    it "encodes valid bip vecs" $-        mapM_ (uncurry encodeVecTest) validEncodeVec-    it "decodes valid bip vecs" $-        mapM_ (uncurry decodeVecTest) $ zip [1..] validVec-    it "decodes vector 2" vec2Test-    it "decodes vector 3" vec3Test-    it "decodes vector 4" vec4Test-    it "decodes vector 5" vec5Test-    it "decodes vector 6" vec6Test-    it "signed and finalized p2pkh PSBTs verify" $ property $-        forAll arbitraryKeyPair $ verifyNonWitnessPSBT btc . unfinalizedPkhPSBT btc-    it "signed and finalized multisig PSBTs verify" $ property $-        forAll arbitraryMultiSig $ verifyNonWitnessPSBT btc . unfinalizedMsPSBT btc--vec2Test :: Assertion-vec2Test = do-    psbt <- decodeHexPSBTM "Cannot parse validVec2" validVec2Hex-    assertEqual "2 inputs" 2 (length $ inputs psbt)-    assertEqual "2 outputs" 2 (length $ outputs psbt)-    assertBool "final script sig" $ isJust (finalScriptSig . head $ inputs psbt)--    let rdmScript = fromJust . inputRedeemScript $ inputs psbt !! 1-    assertBool "p2wpkh" $ (isPayWitnessPKHash <$> decodeOutput rdmScript) == Right True--    let scrptPubKey = witnessScriptPubKey $ inputs psbt !! 1-        rdmScriptP2SH = toP2SH rdmScript-    assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey-    assertEqual "expected redeem script"  expectedOut rdmScriptP2SH--    mapM_ (assertEqual "outputs are empty" emptyOutput) (outputs psbt)--vec3Test :: Assertion-vec3Test = do-    psbt <- decodeHexPSBTM "Cannot parse validVec3" validVec3Hex-    assertEqual "1 input" 1 (length $ inputs psbt)-    assertEqual "2 outputs" 2 (length $ outputs psbt)-    let txInput = head . txIn $ unsignedTransaction psbt-        firstInput = head $ inputs psbt-        Just utx = nonWitnessUtxo firstInput-        OutPoint prevHash prevVOut = prevOutput txInput-    assertEqual "txids of inputs match" prevHash (txHash utx)-    let prevOutputKey = fromRight (error "Could not decode key")-                      . decodeOutputBS . scriptOutput $ txOut utx !! fromIntegral prevVOut-    assertBool "p2pkh" $ isPayPKHash prevOutputKey-    assertEqual "sighash type" sigHashAll (fromJust $ sigHashType firstInput)--vec4Test :: Assertion-vec4Test = do-    psbt <- decodeHexPSBTM "Cannot parse validVec4" validVec4Hex-    assertEqual "2 inputs" 2 (length $ inputs psbt)-    assertEqual "2 outputs" 2 (length $ outputs psbt)-    let firstInput = head $ inputs psbt-        secondInput = inputs psbt !! 1-    assertEqual "first input not final script sig" Nothing (finalScriptSig firstInput)-    assertEqual "second input not final script sig" Nothing (finalScriptSig secondInput)--    let rdmScript = fromJust $ inputRedeemScript secondInput-    assertBool "p2wpkh" $ (isPayWitnessPKHash <$> decodeOutput rdmScript) == Right True--    let scrptPubKey = witnessScriptPubKey secondInput-        rdmScriptP2SH = toP2SH rdmScript-    assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey-    assertEqual "expected redeem script"  expectedOut rdmScriptP2SH--    assertBool "all non-empty outputs" $ emptyOutput `notElem` outputs psbt--vec5Test :: Assertion-vec5Test = do-    psbt <- decodeHexPSBTM "Cannot parse validVec5" validVec5Hex-    assertEqual "1 input" 1 (length $ inputs psbt)-    assertEqual "1 output" 1 (length $ outputs psbt)-    let input = head $ inputs psbt-    assertEqual "input not final script sig" Nothing (finalScriptSig input)--    let rdmScript = fromJust $ inputRedeemScript input-    assertBool "p2wsh" $ (isPayWitnessScriptHash <$> decodeOutput rdmScript) == Right True--    let scrptPubKey = witnessScriptPubKey input-        rdmScriptP2SH = toP2SH rdmScript-    assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey-    assertEqual "expected redeem script"  expectedOut2 rdmScriptP2SH-  where-    expectedOut2 = fromRight (error "could not decode expected output")-                . decodeOutputBS . fromJust $ decodeHex "a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87"--vec6Test :: Assertion-vec6Test = do-    psbt <- decodeHexPSBTM "Cannot parse validVec6" validVec6Hex-    assertEqual "1 input" 1 (length $ inputs psbt)-    assertEqual "1 output" 1 (length $ outputs psbt)--    let tx = unsignedTransaction psbt-    assertEqual "correct transaction" "75c5c9665a570569ad77dd1279e6fd4628a093c4dcbf8d41532614044c14c115" (txHash tx)--    assertEqual "correct unknowns" expectedUnknowns (inputUnknown . head $ inputs psbt)-  where-    expectedUnknowns = UnknownMap $ singleton-        (Key 0x0f (fromJust $ decodeHex "010203040506070809"))-        (fromJust $ decodeHex "0102030405060708090a0b0c0d0e0f")--expectedOut :: ScriptOutput-expectedOut = fromRight (error "could not decode expected output")-            . decodeOutputBS . fromJust $ decodeHex "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"--witnessScriptPubKey :: Input -> ScriptOutput-witnessScriptPubKey = fromRight (error "could not decode witness utxo")-                    . decodeOutputBS . scriptOutput . fromJust . witnessUtxo--decodeHexPSBT :: Text -> Either String PartiallySignedTransaction-decodeHexPSBT = S.decode . fromJust . decodeHex--decodeHexPSBTM :: (Monad m, MonadFail m) => String -> Text -> m PartiallySignedTransaction-decodeHexPSBTM errMsg = either (fail . (errMsg <>) . (": " <>)) return . decodeHexPSBT--hexScript :: Text -> ByteString-hexScript =-    either (error "Could not decode script") encodeScript . S.decode . fromJust . decodeHex-  where-    encodeScript :: Script -> ByteString-    encodeScript = S.encode--invalidVecTest :: Text -> Assertion-invalidVecTest = assertBool "invalid psbt" . isLeft . decodeHexPSBT--decodeVecTest :: Int -> Text -> Assertion-decodeVecTest i = assertBool (show i <> " decodes correctly") . isRight . decodeHexPSBT--encodeVecTest :: PartiallySignedTransaction -> Text -> Assertion-encodeVecTest psbt hex = assertEqual "encodes correctly" (S.encode psbt) (fromJust $ decodeHex hex)--trivialPSBT :: PartiallySignedTransaction-trivialPSBT = PartiallySignedTransaction-    { unsignedTransaction = Tx { txVersion = 2, txIn = [], txOut = [], txWitness = [], txLockTime = 0 }-    , globalUnknown = UnknownMap mempty-    , inputs = []-    , outputs = []-    }--trivialPSBTHex :: Text-trivialPSBTHex = "70736274ff01000a0200000000000000000000"--nonEmptyTransactionPSBT :: PartiallySignedTransaction-nonEmptyTransactionPSBT = emptyPSBT testTx1--verifyNonWitnessPSBT :: Network -> PartiallySignedTransaction -> Bool-verifyNonWitnessPSBT net psbt = verifyStdTx net (finalTransaction (complete psbt)) sigData-  where-    sigData = inputSigData =<< zip (inputs psbt) (txIn $ unsignedTransaction psbt)-    decodeOutScript = fromRight (error "Could not parse output script") . decodeOutputBS-    inputSigData (input, txInput) =-        map (\(TxOut val script) -> (decodeOutScript script, val, prevOutput txInput))-            (txOut . fromJust $ nonWitnessUtxo input)--unfinalizedPkhPSBT :: Network -> (SecKeyI, PubKeyI) -> PartiallySignedTransaction-unfinalizedPkhPSBT net (prvKey, pubKey) = (emptyPSBT currTx)-    { inputs = [ emptyInput { nonWitnessUtxo = Just prevTx, partialSigs = singleton pubKey sig } ] }-  where-    currTx = unfinalizedTx (txHash prevTx)-    prevTx = testUtxo [prevOut]-    prevOutScript = addressToScript (pubKeyAddr pubKey)-    prevOut = TxOut { outValue = 200000000, scriptOutput = S.encode prevOutScript }-    h = txSigHash net currTx prevOutScript (outValue prevOut) 0 sigHashAll-    sig = encodeTxSig $ TxSignature (signHash (secKeyData prvKey) h) sigHashAll--arbitraryMultiSig :: Gen ([(SecKeyI, PubKeyI)], Int)-arbitraryMultiSig = do-    (m, n) <- arbitraryMSParam-    keys <- vectorOf n arbitraryKeyPair-    return (keys, m)--unfinalizedMsPSBT :: Network -> ([(SecKeyI, PubKeyI)], Int) -> PartiallySignedTransaction-unfinalizedMsPSBT net (keys, m) = (emptyPSBT currTx)-    { inputs = [ emptyInput { nonWitnessUtxo = Just prevTx, partialSigs = sigs-                            , inputRedeemScript = Just prevOutScript-                            } ] }-  where-    currTx = unfinalizedTx (txHash prevTx)-    prevTx = testUtxo [prevOut]-    prevOutScript = encodeOutput $ PayMulSig (map snd keys) m-    prevOut = TxOut { outValue = 200000000, scriptOutput = encodeOutputBS (toP2SH prevOutScript) }-    h = txSigHash net currTx prevOutScript (outValue prevOut) 0 sigHashAll-    sigs = fromList $ map sig keys-    sig (prvKey, pubKey) = (pubKey, encodeTxSig $ TxSignature (signHash (secKeyData prvKey) h) sigHashAll)--unfinalizedTx :: TxHash -> Tx-unfinalizedTx prevHash = Tx-        { txVersion = 2-        , txIn = [ TxIn-            { prevOutput = OutPoint prevHash 0-            , scriptInput = ""-            , txInSequence = 4294967294-            } ]-        , txOut =-            [ TxOut { outValue = 99999699, scriptOutput = hexScript "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac" }-            , TxOut { outValue = 100000000, scriptOutput = hexScript "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787" }-            ]-        , txWitness = []-        , txLockTime = 1257139-        }--invalidVec :: [Text]-invalidVec =-    [ "0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300"-    , "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000"-    , "70736274ff0100fd0a010200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be4000000006a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa88292feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000"-    , "70736274ff000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000"-    , "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000000"-    , "70736274ff020001550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"-    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac000000000002010020955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"-    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87210203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"-    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a01020400220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"-    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d568102050047522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"-    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae210603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd10b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"-    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f0000000000020000bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"-    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000020700da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"-    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903020800da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"-    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00210203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58710d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"-    , "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0203000100000000010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00"-    , "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0002000016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00"-    , "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c00010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a6521010025512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00"-    ]---validEncodeVec :: [(PartiallySignedTransaction, Text)]-validEncodeVec = [(validVec1, validVec1Hex)]--testTx1 :: Tx-testTx1 = Tx-        { txVersion = 2-        , txIn = [ TxIn-            { prevOutput = OutPoint "f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126" 0-            , scriptInput = ""-            , txInSequence = 4294967294-            } ]-        , txOut =-            [ TxOut { outValue = 99999699, scriptOutput = hexScript "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac" }-            , TxOut { outValue = 100000000, scriptOutput = hexScript "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787" }-            ]-        , txWitness = []-        , txLockTime = 1257139-        }--testUtxo :: [TxOut] -> Tx-testUtxo prevOuts = Tx-        { txVersion = 1-        , txIn =-            [ TxIn-                { prevOutput = OutPoint "e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389" 1-                , scriptInput = hexScript "160014be18d152a9b012039daf3da7de4f53349eecb985"-                , txInSequence = 4294967295-                }-            , TxIn-                { prevOutput = OutPoint "b490486aec3ae671012dddb2bb08466bef37720a533a894814ff1da743aaf886" 1-                , scriptInput = hexScript "160014fe3e9ef1a745e974d902c4355943abcb34bd5353"-                , txInSequence = 4294967295-                }-            ]-        , txOut = prevOuts-        , txWitness =-            [-              [ fromJust $ decodeHex "304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c01"-              , fromJust $ decodeHex "03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105"-              ]-            , [ fromJust $ decodeHex "3045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01"-              , fromJust $ decodeHex "0223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab3"-              ]-            ]-        , txLockTime = 0-        }--testUtxo1 :: Tx-testUtxo1 = testUtxo-    [ TxOut { outValue = 200000000, scriptOutput = hexScript "76a91485cff1097fd9e008bb34af709c62197b38978a4888ac" }-    , TxOut { outValue = 190303501938, scriptOutput = hexScript "a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587" }-    ]--validVec1 :: PartiallySignedTransaction-validVec1 = (emptyPSBT testTx1) { inputs = [ emptyInput { nonWitnessUtxo = Just testUtxo1 } ] }--validVec :: [Text]-validVec = [validVec1Hex, validVec2Hex, validVec3Hex, validVec4Hex, validVec5Hex, validVec6Hex]--validVec1Hex :: Text-validVec1Hex = "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab300000000000000"--validVec2Hex :: Text-validVec2Hex = "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac000000000001076a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa882920001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000"--validVec3Hex :: Text-validVec3Hex = "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001030401000000000000"--validVec4Hex :: Text-validVec4Hex = "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000100df0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e13000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb8230800220202ead596687ca806043edc3de116cdf29d5e9257c196cd055cf698c8d02bf24e9910b4a6ba670000008000000080020000800022020394f62be9df19952c5587768aeb7698061ad2c4a25c894f47d8c162b4d7213d0510b4a6ba6700000080010000800200008000"--validVec5Hex :: Text-validVec5Hex = "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"--validVec6Hex :: Text-validVec6Hex = "70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f0000"
− test/Network/Haskoin/TransactionSpec.hs
@@ -1,396 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Network.Haskoin.TransactionSpec (spec) where--import           Data.Aeson                         as A-import qualified Data.ByteString                    as B-import           Data.Either-import           Data.Map.Strict                    (singleton)-import           Data.Maybe-import           Data.Serialize                     as S-import           Data.String                        (fromString)-import           Data.String.Conversions-import           Data.Text                          (Text)-import           Data.Word                          (Word32, Word64)-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Keys-import           Network.Haskoin.Script-import           Network.Haskoin.Test-import           Network.Haskoin.Transaction-import           Network.Haskoin.Transaction.Segwit (isSegwit)-import           Network.Haskoin.Util-import           Safe                               (readMay)-import           Test.Hspec-import           Test.HUnit                         (Assertion, assertBool)-import           Test.QuickCheck--spec :: Spec-spec = do-    let net = btc-    describe "transaction unit tests" $ do-        it "compute txid from tx" $-            mapM_ runTxIDVec txIDVec-        it "build pkhash transaction (generated from bitcoind)" $-            mapM_ runPKHashVec pkHashVec-        it "encode satoshi core script pubkey" tEncodeSatoshiCoreScriptPubKey-        ---        -- These tests depend on signatures matching exactly those in the spec.-        -- Fedora includes a version of libsecp256k1 that computes signatures-        -- using a slighlty different deterministic nonce algorithm.-        ---        -- it "agrees with BIP143 p2wpkh example" testBip143p2wpkh-        -- it "agrees with BIP143 p2sh-p2wpkh example" testBip143p2shp2wpkh-        -- it "builds a p2wsh multisig transaction" testP2WSHMulsig-        -- it "agrees with BIP143 p2sh-p2wsh multisig example" testBip143p2shp2wpkhMulsig-    describe "btc transaction" $ do-        it "decode and encode txid" $-            property $-            forAll arbitraryTxHash $ \h -> hexToTxHash (txHashToHex h) == Just h-        it "from string transaction id" $-            property $-            forAll arbitraryTxHash $ \h -> fromString (cs $ txHashToHex h) == h-        it "building address tx" $-            property $-            forAll arbitraryAddress $-            forAll (arbitrarySatoshi net) . testBuildAddrTx net-        it "guess transaction size" $-            property $ forAll (arbitraryAddrOnlyTxFull net) (testGuessSize net)-        it "choose coins" $-            property $ forAll (listOf (arbitrarySatoshi net)) testChooseCoins-        it "choose multisig coins" $-            property $-            forAll arbitraryMSParam $-            forAll (listOf (arbitrarySatoshi net)) . testChooseMSCoins-        it "sign and validate transaction" $-            property $ forAll (arbitrarySigningData net) (testDetSignTx net)-        it "sign and validate (nested) transaction" $-            property $ forAll (arbitrarySigningData net) (testDetSignNestedTx net)-        it "merge partially signed transactions" $-            property $ forAll (arbitraryPartialTxs net) (testMergeTx net)-    describe "json serialization" $ do-        it "encodes and decodes transaction" $-            property $ forAll (arbitraryTx net) testID-        it "encodes and decodes transaction hash" $-            property $ forAll arbitraryTxHash testID-    describe "transaction serialization" $ do-        it "encodes and decodes tx input" $-            property $ forAll (arbitraryTxIn net) cerealID-        it "encodes and decodes tx output" $-            property $ forAll (arbitraryTxOut net) cerealID-        it "encodes and decodes outpoint" $-            property $ forAll arbitraryOutPoint cerealID-        it "encodes and decodes transaction" $-            property $ forAll (arbitraryTx net) cerealID-        it "encodes and decodes witness transaction" $-            property $ forAll (arbitraryWitnessTx net) cerealID-        it "encodes and decodes legacy transaction" $-            property $ forAll (arbitraryLegacyTx net) cerealID--cerealID :: (Serialize a, Eq a) => a -> Bool-cerealID x = S.decode (S.encode x) == Right x--runTxIDVec :: (Text, Text) -> Assertion-runTxIDVec (tid, tx) = assertBool "txid" $ txHashToHex (txHash txBS) == tid-  where-    txBS = fromJust $ either (const Nothing) return . S.decode =<< decodeHex tx--txIDVec :: [(Text, Text)]-txIDVec =-    [ ( "23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"-      , "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000"-      )-    , ( "c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73"-      , "01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d00000000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac00000000"-      )-    , ( "f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb"-      , "01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000"-      )-    , ( "afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae"-      , "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf950650802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a3788dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac00000000"-      )-    ]--runPKHashVec :: ([(Text, Word32)], [(Text, Word64)], Text) -> Assertion-runPKHashVec (xs, ys, res) =-    assertBool "Build PKHash Tx" $ encodeHex (S.encode tx) == res-  where-    tx =-        fromRight (error "Could not decode transaction") $-        buildAddrTx btc (map f xs) ys-    f (tid, ix) = OutPoint (fromJust $ hexToTxHash tid) ix---- These test vectors have been generated from bitcoind raw transaction api--pkHashVec :: [([(Text, Word32)], [(Text, Word64)], Text)]-pkHashVec =-    [-      ( [("eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",14)]-      , [("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb",90000000)]-      , "0100000001db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0e00000000ffffffff01804a5d05000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac00000000"-      )-    , ( [ ("eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",0)-        , ("0001000000000000000000000000000000000000000000000000000000000000",2147483647)-        ]-      , [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb",1)-        , ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n",2100000000000000)-        ]-      , "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0000000000ffffffff0000000000000000000000000000000000000000000000000000000000000100ffffff7f00ffffffff0201000000000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac0040075af07507001976a9145d16672f53981ff21c5f42b40d1954993cbca54f88ac00000000"-      )-    , ( [ ("eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",0)-        , ("0001000000000000000000000000000000000000000000000000000000000000",2147483647)-        ]-      , []-      , "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0000000000ffffffff0000000000000000000000000000000000000000000000000000000000000100ffffff7f00ffffffff0000000000"-      )-    , ( []-      , [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb",1)-        , ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n",2100000000000000)-        ]-      , "01000000000201000000000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac0040075af07507001976a9145d16672f53981ff21c5f42b40d1954993cbca54f88ac00000000"-      )-    ]--tEncodeSatoshiCoreScriptPubKey :: Assertion-tEncodeSatoshiCoreScriptPubKey = assertBool "tEncodeSatoshiCoreScriptPubKey" $-  t1BsOutputScriptPubKey == encodeSatoshiCoreScriptPubKey t1SatoshiCoreJsonScriptPubKey-  where-    t1BsOutputScriptPubKey :: Text-    t1BsOutputScriptPubKey = "514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af52ae"-    t1SatoshiCoreJsonScriptPubKey :: String-    t1SatoshiCoreJsonScriptPubKey = "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"--encodeSatoshiCoreScriptPubKey :: String -> Text-encodeSatoshiCoreScriptPubKey =-  mconcat . map encodeSatoshiCoreScriptPiece . words-  where-    encodeSatoshiCoreScriptPiece :: String -> Text-    encodeSatoshiCoreScriptPiece s = case (readMay ("OP_" ++ s) :: Maybe ScriptOp) of-      Just op -> encodeHex . S.encode $ op-      Nothing -> case take 2 s of-          "OP" -> encodeHex . S.encode . (read :: String -> ScriptOp) $ s-          "0x" -> (fromString . drop 2 :: String -> Text) s-          _ -> case (readMay s :: Maybe Int) of -- can we get rid of this case now?-            Just i  -> encodeHex . S.encode . intToScriptOp $ i-            Nothing -> error $ "encodeSatoshiCoreScriptPubKey: " ++ s------- Following tests depend on specific secp256k1 deterministic nonce computation.--- Fedora distributes the Bitcoin ABC fork of this library, which computes nonces differently.------- -- Reproduce the P2WPKH example from BIP 143--- testBip143p2wpkh :: Assertion--- testBip143p2wpkh = assertEqual "BIP143 p2wpkh" (Right exampleSignedTx) generatedSignedTx---   where---     exampleSignedTx = "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000494830450221008b9d1dc26ba6a9cb62127b02742fa9d754cd3bebf337f7a55d114c8e5cdd30be022040529b194ba3f9281a99f2b1c0a19c0489bc22ede944ccf4ecbab4cc618ef3ed01eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac000247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000"----     unsignedTx = "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000"----     Just key0 = secHexKey "bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866"---     pubKey0   = toPubKey key0----     Just key1 = secHexKey "619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9"----     [op0, op1] = prevOutput <$> txIn unsignedTx----     sigIn0 = SigInput (PayPK pubKey0) 625000000 op0 sigHashAll Nothing----     WitnessPubKeyAddress h = pubKeyWitnessAddr $ toPubKey key1---     sigIn1                 = SigInput (PayWitnessPKHash h) 600000000 op1 sigHashAll Nothing----     generatedSignedTx = signTx btc unsignedTx [sigIn0, sigIn1] [key0, key1]---- -- Reproduce the P2SH-P2WPKH example from BIP 143--- testBip143p2shp2wpkh :: Assertion--- testBip143p2shp2wpkh = assertEqual "BIP143 p2sh-p2wpkh" (Right exampleSignedTx) generatedSignedTx---   where---     exampleSignedTx = "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009bdf0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000"----     unsignedTx = "0100000001db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a54770100000000feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac92040000"----     Just key0 = secHexKey "eb696a065ef48a2192da5b28b694f87544b30fae8327c4510137a922f32c6dcf"----     op0                    = prevOutput . head $ txIn unsignedTx---     WitnessPubKeyAddress h = pubKeyWitnessAddr $ toPubKey key0---     sigIn0                 = SigInput (PayWitnessPKHash h) 1000000000 op0 sigHashAll Nothing----     generatedSignedTx = signNestedWitnessTx btc unsignedTx [sigIn0] [key0]---- -- P2WSH multisig example (tested against bitcoin-core 0.19.0.1)--- testP2WSHMulsig :: Assertion--- testP2WSHMulsig = assertEqual "p2wsh multisig" (Right exampleSignedTx) generatedSignedTx---   where---     exampleSignedTx = "01000000000101d2e34df5d7ee565208eddd231548916b9b0e99f4f5071f896134a448c5fb07bf0100000000ffffffff01f0b9f505000000001976a9143d5a352cab583b12fbcb26d1269b4a2c951a33ad88ac0400483045022100fad4fedd2bb4c439c64637eb8e9150d9020a7212808b8dc0578d5ff5b4ad65fe0220714640f261b37eb3106310bf853f4b706e51436fb6b64c2ab00768814eb55b9801473044022100baff4e4ceea4022b9725a2e6f6d77997a554f858165b91ac8c16c9833008bee9021f5f70ebc3f8580dc0a5e96451e3697bdf1f1f5883944f0f33ab0cfb272354040169522102ba46d3bb8db74c77c6cf082db57fc0548058fcdea811549e186526e3d10caf6721038ac8aef2dd9cea5e7d66e2f6e23f177a6c21f69ea311fa0c85d81badb6b37ceb2103d96d2bfbbc040faaf93491d69e2bfe9695e2d8e007a7f26db96c2ee42db15dc953ae00000000"----     unsignedTx = "0100000001d2e34df5d7ee565208eddd231548916b9b0e99f4f5071f896134a448c5fb07bf0100000000ffffffff01f0b9f505000000001976a9143d5a352cab583b12fbcb26d1269b4a2c951a33ad88ac00000000"----     op0 = head $ prevOutput <$> txIn unsignedTx----     Just keys = traverse secHexKey [ "3030303030303030303030303030303030303030303030303030303030303031"---                                    , "3030303030303030303030303030303030303030303030303030303030303032"---                                    , "3030303030303030303030303030303030303030303030303030303030303033"---                                    ]----     rdm               = PayMulSig (toPubKey <$> keys) 2---     sigIn             = SigInput (toP2WSH $ encodeOutput rdm) 100000000 op0 sigHashAll (Just rdm)---     generatedSignedTx = signTx btc unsignedTx [sigIn] (take 2 keys)---- -- Reproduce the P2SH-P2WSH multisig example from BIP 143--- testBip143p2shp2wpkhMulsig :: Assertion--- testBip143p2shp2wpkhMulsig =---     assertEqual "BIP143 p2sh-p2wsh multisig" (Right exampleSignedTx) generatedSignedTx---   where---     exampleSignedTx = "0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac080047304402206ac44d672dac41f9b00e28f4df20c52eeb087207e8d758d76d92c6fab3b73e2b0220367750dbbe19290069cba53d096f44530e4f98acaa594810388cf7409a1870ce01473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502473044022059ebf56d98010a932cf8ecfec54c48e6139ed6adb0728c09cbe1e4fa0915302e022007cd986c8fa870ff5d2b3a89139c9fe7e499259875357e20fcbb15571c76795403483045022100fbefd94bd0a488d50b79102b5dad4ab6ced30c4069f1eaa69a4b5a763414067e02203156c6a5c9cf88f91265f5a942e96213afae16d83321c8b31bb342142a14d16381483045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a08824730440220525406a1482936d5a21888260dc165497a90a15669636d8edca6b9fe490d309c022032af0c646a34a44d1f4576bf6a4a74b67940f8faa84c7df9abe12a01a11e2b4783cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000"----     unsignedTx = "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000"----     op0 = head $ prevOutput <$> txIn unsignedTx----     rawKeys = [ "730fff80e1413068a05b57d6a58261f07551163369787f349438ea38ca80fac6"---               , "11fa3d25a17cbc22b29c44a484ba552b5a53149d106d3d853e22fdd05a2d8bb3"---               , "77bf4141a87d55bdd7f3cd0bdccf6e9e642935fec45f2f30047be7b799120661"---               , "14af36970f5025ea3e8b5542c0f8ebe7763e674838d08808896b63c3351ffe49"---               , "fe9a95c19eef81dde2b95c1284ef39be497d128e2aa46916fb02d552485e0323"---               , "428a7aee9f0c2af0cd19af3cf1c78149951ea528726989b2e83e4778d2c3f890"---               ]----     Just keys@[key0, key1, key2, key3, key4, key5] = traverse secHexKey rawKeys----     rdm      = PayMulSig (toPubKey <$> keys) 6---     sigIn sh = SigInput (toP2WSH $ encodeOutput rdm) 987654321 op0 sh (Just rdm)----     sigHashesA = [sigHashAll, sigHashNone, sigHashSingle]---     sigHashesB = setAnyoneCanPayFlag <$> sigHashesA---     sigIns     = sigIn <$> (sigHashesA <> sigHashesB)----     generatedSignedTx      = foldM addSig unsignedTx $ zip sigIns keys---     addSig tx (sigIn, key) = signNestedWitnessTx btc tx [sigIn] [key]---- secHexKey :: Text -> Maybe SecKey--- secHexKey = decodeHex >=> secKey---- toPubKey :: SecKey -> PubKeyI--- toPubKey = derivePubKeyI . wrapSecKey True--{- Building Transactions -}--testBuildAddrTx :: Network -> Address -> TestCoin -> Bool-testBuildAddrTx net a (TestCoin v)-    | isPubKeyAddress a = Right (PayPKHash (getAddrHash160 a)) == out-    | isScriptAddress a = Right (PayScriptHash (getAddrHash160 a)) == out-    | otherwise = undefined-  where-    tx = buildAddrTx net [] [(fromJust (addrToString net a), v)]-    out =-        decodeOutputBS $-        scriptOutput $-        head $ txOut (fromRight (error "Could not build transaction") tx)--testGuessSize :: Network -> Tx -> Bool-testGuessSize net tx-    -- We compute an upper bound but it should be close enough to the real size-    -- We give 2 bytes of slack on every signature (1 on r and 1 on s)- = guess >= len && guess <= len + 2 * delta-  where-    delta = pki + sum (map fst msi)-    guess = guessTxSize pki msi pkout msout-    len = B.length $ S.encode tx-    ins = map f $ txIn tx-    f i =-        fromRight (error "Could not decode input") $-        decodeInputBS net $ scriptInput i-    pki = length $ filter isSpendPKHash ins-    msi = concatMap shData ins-    shData (ScriptHashInput _ (PayMulSig keys r)) = [(r, length keys)]-    shData _                                      = []-    out =-        map-            (fromRight (error "Could not decode transaction output") .-             decodeOutputBS . scriptOutput) $-        txOut tx-    pkout = length $ filter isPayPKHash out-    msout = length $ filter isPayScriptHash out--testChooseCoins :: [TestCoin] -> Word64 -> Word64 -> Int -> Property-testChooseCoins coins target byteFee nOut = nOut >= 0 ==>-    case chooseCoins target byteFee nOut True coins of-        Right (chosen, change) ->-            let outSum = sum $ map coinValue chosen-                fee    = guessTxFee byteFee nOut (length chosen)-            in outSum == target + change + fee-        Left _ ->-            let fee = guessTxFee byteFee nOut (length coins)-            in target == 0 || s < target + fee-  where-    s  = sum $ map coinValue coins--testChooseMSCoins :: (Int, Int) -> [TestCoin]-                  -> Word64 -> Word64 -> Int -> Property-testChooseMSCoins (m, n) coins target byteFee nOut = nOut >= 0 ==>-    case chooseMSCoins target byteFee (m,n) nOut True coins of-        Right (chosen,change) ->-            let outSum = sum $ map coinValue chosen-                fee    = guessMSTxFee byteFee (m,n) nOut (length chosen)-            in outSum == target + change + fee-        Left _ ->-            let fee = guessMSTxFee byteFee (m,n) nOut (length coins)-            in target == 0 || s < target + fee-  where-    s = sum $ map coinValue coins--{- Signing Transactions -}--testDetSignTx :: Network -> (Tx, [SigInput], [SecKeyI]) -> Bool-testDetSignTx net  (tx, sigis, prv) =-    not (verifyStdTx net tx verData) &&-    not (verifyStdTx net txSigP verData) && verifyStdTx net txSigC verData-  where-    txSigP =-        fromRight (error "Could not decode transaction") $-        signTx net tx sigis (map secKeyData (tail prv))-    txSigC =-        fromRight (error "Could not decode transaction") $-        signTx net txSigP sigis [secKeyData (head prv)]-    verData = map (\(SigInput s v o _ _) -> (s, v, o)) sigis--testDetSignNestedTx :: Network -> (Tx, [SigInput], [SecKeyI]) -> Bool-testDetSignNestedTx net  (tx, sigis, prv) =-    not (verifyStdTx net tx verData) &&-    not (verifyStdTx net txSigP verData) && verifyStdTx net txSigC verData-  where-    txSigP =-        fromRight (error "Could not decode transaction") $-        signNestedWitnessTx net tx sigis (secKeyData <$> tail prv)-    txSigC =-        fromRight (error "Could not decode transaction") $-        signNestedWitnessTx net txSigP sigis [secKeyData (head prv)]-    verData = handleSegwit <$> sigis-    handleSegwit (SigInput s v o _ _)-        | isSegwit s = (toP2SH $ encodeOutput s, v, o)-        | otherwise  = (s, v, o)--testMergeTx :: Network -> ([Tx], [(ScriptOutput, Word64, OutPoint, Int, Int)]) -> Bool-testMergeTx net (txs, os) = and-    [ isRight mergeRes-    , length (txIn mergedTx) == length os-    , if enoughSigs then isValid else not isValid-    -- Signature count == min (length txs) (sum required signatures)-    , sum (map snd sigMap) == min (length txs) (sum (map fst sigMap))-    ]-  where-    outs = map (\(so, val, op, _, _) -> (so, val, op)) os-    mergeRes = mergeTxs net txs outs-    mergedTx = fromRight (error "Could not merge") mergeRes-    isValid = verifyStdTx net mergedTx outs-    enoughSigs = all (\(m,c) -> c >= m) sigMap-    sigMap = map (\((_,_,_,m,_), inp) -> (m, sigCnt inp)) $ zip os $ txIn mergedTx-    sigCnt inp = case decodeInputBS net $ scriptInput inp of-        Right (RegularInput (SpendMulSig sigs)) -> length sigs-        Right (ScriptHashInput (SpendMulSig sigs) _) -> length sigs-        _ -> error "Invalid input script type"--testID :: (FromJSON a, ToJSON a, Eq a) => a -> Bool-testID x =-    (A.decode . A.encode) (singleton ("object" :: String) x) ==-    Just (singleton ("object" :: String) x)
− test/Network/Haskoin/UtilSpec.hs
@@ -1,64 +0,0 @@-module Network.Haskoin.UtilSpec (spec) where--import qualified Data.ByteString      as BS-import           Data.Either          (fromLeft, fromRight, isLeft, isRight)-import           Data.Foldable        (toList)-import           Data.List            (permutations)-import           Data.Maybe-import qualified Data.Sequence        as Seq-import           Network.Haskoin.Test-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.QuickCheck--spec :: Spec-spec =-    describe "utility functions" $ do-        it "bsToInteger . integerToBS" $ property getPutInteger-        it "decodeHex . encodeHex" $ property $ forAll arbitraryBS fromToHex-        it "compare updateIndex with Data.Sequence" $ property testUpdateIndex-        it "matchTemplate" $ property testMatchTemplate-        it "testing matchTemplate with two lists" $-            property testMatchTemplateLen-        it "either helper functions" $ property testEither--{- Various utilities -}--getPutInteger :: Integer -> Bool-getPutInteger i = bsToInteger (integerToBS $ abs i) == abs i--fromToHex :: BS.ByteString -> Bool-fromToHex bs = decodeHex (encodeHex bs) == Just bs--testUpdateIndex :: [Int] -> Int -> Int -> Bool-testUpdateIndex xs v i =-    updateIndex i xs (const v) == toList (Seq.update i v $ Seq.fromList xs)--testMatchTemplate :: [Int] -> Int -> Bool-testMatchTemplate as i = catMaybes res == bs-  where-    res = matchTemplate as bs (==)-    idx =-        if null as-            then 0-            else i `mod` length as-    bs = permutations as !! idx--testMatchTemplateLen :: [Int] -> [Int] -> Bool-testMatchTemplateLen as bs = length bs == length res-  where-    res = matchTemplate as bs (==)--testEither :: Either String Int -> Bool-testEither e =-    case e of-        (Right v) ->-            isRight e &&-            not (isLeft e) &&-            fromRight (error "Unexpected Left") e == v &&-            eitherToMaybe e == Just v-        (Left v) ->-            isLeft e &&-            not (isRight e) &&-            fromLeft (error "Unexpected Right") e == v &&-            isNothing (eitherToMaybe e)