haskoin-core 0.21.2 → 1.2.2
raw patch · 92 files changed
Files
- CHANGELOG.md +245/−61
- README.md +2/−1
- data/rfc6979DERabc.json +0/−102
- data/rfc6979DERcash.json +102/−0
- data/rfc6979abc.json +0/−62
- data/rfc6979cash.json +62/−0
- data/test_vectors_aserti3-2d_run01.txt +19/−0
- data/test_vectors_aserti3-2d_run02.txt +19/−0
- data/test_vectors_aserti3-2d_run03.txt +19/−0
- data/test_vectors_aserti3-2d_run04.txt +234/−0
- data/test_vectors_aserti3-2d_run05.txt +234/−0
- data/test_vectors_aserti3-2d_run06.txt +1009/−0
- data/test_vectors_aserti3-2d_run07.txt +1009/−0
- data/test_vectors_aserti3-2d_run08.txt +509/−0
- data/test_vectors_aserti3-2d_run09.txt +19/−0
- data/test_vectors_aserti3-2d_run10.txt +19/−0
- data/test_vectors_aserti3-2d_run11.txt +1009/−0
- data/test_vectors_aserti3-2d_run12.txt +10009/−0
- haskoin-core.cabal +42/−28
- src/Haskoin.hs +16/−20
- src/Haskoin/Address.hs +224/−228
- src/Haskoin/Address/Base58.hs +63/−62
- src/Haskoin/Address/Bech32.hs +98/−101
- src/Haskoin/Address/CashAddr.hs +136/−134
- src/Haskoin/Block.hs +14/−12
- src/Haskoin/Block/Common.hs +278/−295
- src/Haskoin/Block/Headers.hs +972/−1006
- src/Haskoin/Block/Merkle.hs +162/−169
- src/Haskoin/Constants.hs +0/−615
- src/Haskoin/Crypto.hs +16/−12
- src/Haskoin/Crypto/Hash.hs +147/−143
- src/Haskoin/Crypto/Keys.hs +22/−0
- src/Haskoin/Crypto/Keys/Common.hs +185/−0
- src/Haskoin/Crypto/Keys/Extended.hs +1057/−0
- src/Haskoin/Crypto/Keys/Extended/Internal.hs +88/−0
- src/Haskoin/Crypto/Keys/Mnemonic.hs +2236/−0
- src/Haskoin/Crypto/Signature.hs +69/−56
- src/Haskoin/Data.hs +0/−93
- src/Haskoin/Keys.hs +0/−20
- src/Haskoin/Keys/Common.hs +0/−190
- src/Haskoin/Keys/Extended.hs +0/−1084
- src/Haskoin/Keys/Extended/Internal.hs +0/−83
- src/Haskoin/Keys/Mnemonic.hs +0/−2233
- src/Haskoin/Network.hs +19/−13
- src/Haskoin/Network/Bloom.hs +208/−191
- src/Haskoin/Network/Common.hs +749/−746
- src/Haskoin/Network/Constants.hs +582/−0
- src/Haskoin/Network/Data.hs +89/−0
- src/Haskoin/Network/Message.hs +155/−151
- src/Haskoin/Script.hs +15/−15
- src/Haskoin/Script/Common.hs +594/−577
- src/Haskoin/Script/SigHash.hs +207/−197
- src/Haskoin/Script/Standard.hs +239/−235
- src/Haskoin/Transaction.hs +14/−12
- src/Haskoin/Transaction/Builder.hs +441/−394
- src/Haskoin/Transaction/Builder/Sign.hs +258/−255
- src/Haskoin/Transaction/Common.hs +261/−254
- src/Haskoin/Transaction/Genesis.hs +28/−28
- src/Haskoin/Transaction/Partial.hs +949/−925
- src/Haskoin/Transaction/Segwit.hs +140/−121
- src/Haskoin/Transaction/Taproot.hs +222/−217
- src/Haskoin/Util.hs +15/−379
- src/Haskoin/Util/Arbitrary.hs +13/−13
- src/Haskoin/Util/Arbitrary/Address.hs +25/−26
- src/Haskoin/Util/Arbitrary/Block.hs +44/−43
- src/Haskoin/Util/Arbitrary/Crypto.hs +11/−12
- src/Haskoin/Util/Arbitrary/Keys.hs +55/−47
- src/Haskoin/Util/Arbitrary/Message.hs +39/−38
- src/Haskoin/Util/Arbitrary/Network.hs +89/−91
- src/Haskoin/Util/Arbitrary/Script.hs +316/−319
- src/Haskoin/Util/Arbitrary/Transaction.hs +206/−204
- src/Haskoin/Util/Arbitrary/Util.hs +140/−106
- src/Haskoin/Util/Helpers.hs +426/−0
- src/Haskoin/Util/Marshal.hs +44/−0
- test/Haskoin/Address/Bech32Spec.hs +192/−206
- test/Haskoin/Address/CashAddrSpec.hs +289/−321
- test/Haskoin/AddressSpec.hs +137/−143
- test/Haskoin/BlockSpec.hs +326/−325
- test/Haskoin/Crypto/HashSpec.hs +321/−357
- test/Haskoin/Crypto/Keys/ExtendedSpec.hs +602/−0
- test/Haskoin/Crypto/Keys/MnemonicSpec.hs +637/−0
- test/Haskoin/Crypto/KeysSpec.hs +276/−0
- test/Haskoin/Crypto/SignatureSpec.hs +349/−344
- test/Haskoin/Keys/ExtendedSpec.hs +0/−621
- test/Haskoin/Keys/MnemonicSpec.hs +0/−498
- test/Haskoin/KeysSpec.hs +0/−266
- test/Haskoin/NetworkSpec.hs +122/−116
- test/Haskoin/ScriptSpec.hs +377/−368
- test/Haskoin/Transaction/PartialSpec.hs +477/−430
- test/Haskoin/Transaction/TaprootSpec.hs +121/−129
- test/Haskoin/TransactionSpec.hs +258/−280
- test/Haskoin/UtilSpec.hs +33/−35
CHANGELOG.md view
@@ -1,289 +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.21.2+## [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+## [0.21.1] - 2021-12-13+ ### Changed+ - Make Base58 faster. -## 0.21.0+## [0.21.0] - 2022-11-23+ ### Added+ - BCH Testnet4 support. ### Changed-- Use a newtype for Fingerprint, which uses an 8 digit hex string for various- instances. This fixes inconsistent (de)serialization across the package. +- 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+## [0.20.5] - 2021-09-13+ ### Added+ - Support Bech32m address format for Taproot. -## 0.20.4+## [0.20.4] - 2021-06-08+ ### Fixed+ - Add missing case for witness version. -## 0.20.3+## [0.20.3] - 2021-05-17+ ### Fixed+ - Allow unknown inv types. -## 0.20.2+## [0.20.2] - 2021-05-17+ ### Fixed+ - Allow unknown messages of zero length. -## 0.20.1+## [0.20.1] - 2021-05-14+ ### Fixed+ - Correct case where binary search returned the wrong element. -## 0.20.0+## [0.20.0] - 2021-02-22+ ### Chaged+ - Use bytes instead of binary or cereal. -## 0.19.0+## [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+## [0.18.0] - 2020-12-10+ ### Added+ - Support SegWit addresses with version other than 0. -## 0.17.6+## [0.17.6] - 2020-12-07+ ### Added+ - Serialize instances for `XPubKey` and `XPrvKey`. -## 0.17.5+## [0.17.5] - 2020-12-03+ ### Fixed+ - Handle special case in block header binary search function. -## 0.17.4+## [0.17.4] - 2020-12-03+ ### Fixed+ - Bounds check too restrictive in block header binary search function. -## 0.17.3+## [0.17.3] - 2020-11-17+ ### Changed+ - Reduce minimum version of text package dependency. -## 0.17.2+## [0.17.2] - 2020-11-17+ ### Changed+ - Update lists of seeds for all networks. -## 0.17.1+## [0.17.1] - 2020-11-02+ ### Changed-- Use the C-preprocessor to handle versions of `base16-bytestring` including 1.0- (with a breaking API change) -## 0.17.0+- 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+## [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.+- (breaking) rename `OP_NOP2` and `OP_NOP3` to `OP_CHECKLOCKTIMEVERIFY` and `OP_CHECKSEQUENCEVERIFY` resp. - Update to latest secp256k1 bindings. -## 0.14.1+## [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+## [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+## [0.13.6] - 2020-06-05+ ### Changed+ - Expose the Arbitrary test instances under Haskoin.Util.Arbitrary -## 0.13.5+## [0.13.5] - 2020-05-16+ ### Changed+ - Provide meaningful JSON instances for most types. -## 0.13.4+## [0.13.4] - 2020-05-14+ ### Added+ - Support for Bitcoin Cash May 2020 hard fork. -## 0.13.3+## [0.13.3] - 2020-05-08+ ### Changed+ - Improve code and documentation organisation. -## 0.13.2+## [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+## [0.13.1] - 2020-05-06+ ### Changed+ - Faster JSON serialization. -## 0.13.0+## [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-### Added-- Support for signing segwit transactions.+## [0.12.0] - 2020-04-10 -## 0.11.0 ### 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.@@ -299,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`.@@ -309,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.
README.md view
@@ -17,4 +17,5 @@ ## Contributing -Please use `ormolu` (or `fourmolu`) to format code prior to submission. See `scripts/pre-commit.sh` for an example pre-commit hook.+All code is formatted with [Ormolu](https://github.com/tweag/ormolu).+Convenience formatting script available at [scripts/format](scripts/format)
− data/rfc6979DERabc.json
@@ -1,102 +0,0 @@-[- [- "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/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/rfc6979abc.json
@@ -1,62 +0,0 @@-[- [- "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/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/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,19 +1,17 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.34.4.+-- This file has been generated from package.yaml by hpack version 0.38.0. -- -- see: https://github.com/sol/hpack name: haskoin-core-version: 0.21.2+version: 1.2.2 synopsis: Bitcoin & Bitcoin Cash library for Haskell-description: Please see the README on GitHub at <https://github.com/haskoin/haskoin-core#readme>+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: MIT license-file: LICENSE@@ -25,20 +23,32 @@ data/forkid_sighash.json data/key_io_invalid.json data/key_io_valid.json- data/rfc6979abc.json+ data/rfc6979cash.json data/rfc6979core.json- data/rfc6979DERabc.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:@@ -51,18 +61,18 @@ Haskoin.Block.Common Haskoin.Block.Headers Haskoin.Block.Merkle- Haskoin.Constants Haskoin.Crypto Haskoin.Crypto.Hash+ Haskoin.Crypto.Keys+ Haskoin.Crypto.Keys.Common+ Haskoin.Crypto.Keys.Extended+ Haskoin.Crypto.Keys.Mnemonic Haskoin.Crypto.Signature- Haskoin.Data- Haskoin.Keys- Haskoin.Keys.Common- Haskoin.Keys.Extended- Haskoin.Keys.Mnemonic Haskoin.Network Haskoin.Network.Bloom Haskoin.Network.Common+ Haskoin.Network.Constants+ Haskoin.Network.Data Haskoin.Network.Message Haskoin.Script Haskoin.Script.Common@@ -87,8 +97,10 @@ Haskoin.Util.Arbitrary.Script Haskoin.Util.Arbitrary.Transaction Haskoin.Util.Arbitrary.Util+ Haskoin.Util.Helpers+ Haskoin.Util.Marshal other-modules:- Haskoin.Keys.Extended.Internal+ Haskoin.Crypto.Keys.Extended.Internal hs-source-dirs: src build-depends:@@ -96,14 +108,15 @@ , aeson >=1.4.6.0 , array >=0.5.4.0 , base >=4.9 && <5- , base16 >=0.3.0.1+ , 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- , cryptonite >=0.26+ , 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@@ -114,7 +127,7 @@ , network >=3.1.1.1 , safe >=0.3.18 , scientific >=0.3.6.2- , secp256k1-haskell >=0.4.0+ , secp256k1-haskell >=1.2.0 , split >=0.2.3.3 , string-conversions >=0.4.0.1 , text >=1.2.3.0@@ -133,10 +146,10 @@ Haskoin.AddressSpec Haskoin.BlockSpec Haskoin.Crypto.HashSpec+ Haskoin.Crypto.Keys.ExtendedSpec+ Haskoin.Crypto.Keys.MnemonicSpec+ Haskoin.Crypto.KeysSpec Haskoin.Crypto.SignatureSpec- Haskoin.Keys.ExtendedSpec- Haskoin.Keys.MnemonicSpec- Haskoin.KeysSpec Haskoin.NetworkSpec Haskoin.ScriptSpec Haskoin.Transaction.PartialSpec@@ -152,15 +165,16 @@ , aeson >=1.4.6.0 , array >=0.5.4.0 , base >=4.9 && <5- , base16 >=0.3.0.1- , base64 ==0.4.*+ , 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- , cryptonite >=0.26+ , 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@@ -174,7 +188,7 @@ , network >=3.1.1.1 , safe >=0.3.18 , scientific >=0.3.6.2- , secp256k1-haskell >=0.4.0+ , secp256k1-haskell >=1.2.0 , split >=0.2.3.3 , string-conversions >=0.4.0.1 , text >=1.2.3.0
src/Haskoin.hs view
@@ -1,34 +1,30 @@-{- |-Module : Haskoin-Description : Bitcoin (BTC/BCH) Libraries for Haskell-Copyright : No rights reserved-License : MIT-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 (- module Data,- module Constants,- module Address,+-- |+-- 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+ ( module Address, module Block, module Transaction, module Script,- module Keys, module Crypto, module Network, module Util,-) where+ )+where import Haskoin.Address as Address import Haskoin.Block as Block-import Haskoin.Constants as Constants import Haskoin.Crypto as Crypto-import Haskoin.Data as Data-import Haskoin.Keys as Keys import Haskoin.Network as Network import Haskoin.Script as Script import Haskoin.Transaction as Transaction
src/Haskoin/Address.hs view
@@ -1,22 +1,26 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-}--{- |-Module : Haskoin.Address-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE NoFieldSelectors #-} -Base58, CashAddr, Bech32 address and WIF private key serialization support.--}-module Haskoin.Address (- -- * Addresses+-- |+-- 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,@@ -28,9 +32,6 @@ bech32ToAddr, cashToAddr, base58ToAddr,- addrToJSON,- addrToEncoding,- addrFromJSON, pubKeyAddr, pubKeyWitnessAddr, pubKeyCompatWitnessAddr,@@ -51,248 +52,241 @@ module Haskoin.Address.Base58, module Haskoin.Address.Bech32, module Haskoin.Address.CashAddr,-) where+ )+where -import Control.Applicative+import Control.Applicative (Alternative ((<|>))) import Control.Arrow (second)-import Control.DeepSeq-import Control.Monad-import Data.Aeson as A-import Data.Aeson.Encoding as A-import Data.Aeson.Types+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 qualified Data.ByteString as B-import Data.Bytes.Get-import Data.Bytes.Put-import Data.Bytes.Serial-import Data.Hashable-import Data.Maybe+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 qualified Data.Text as T+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-import Haskoin.Data-import Haskoin.Keys.Common-import Haskoin.Script+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- getAddrHash160 :: !Hash160- }- | -- | pay to script hash- ScriptAddress- { -- | RIPEMD160 hash of script's SHA256 hash- getAddrHash160 :: !Hash160- }- | -- | pay to witness public key hash- WitnessPubKeyAddress- { -- | RIPEMD160 hash of public key's SHA256 hash- getAddrHash160 :: !Hash160- }- | -- | pay to witness script hash- WitnessScriptAddress- { -- | HASH256 hash of script- getAddrHash256 :: !Hash256- }- | -- | other witness address- WitnessAddress- { getAddrVersion :: !Word8- , getAddrData :: !ByteString- }- deriving- (Eq, Ord, Generic, Show, Read, Hashable, NFData)+ = -- | 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+ 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)+ 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+ put = serialize+ get = deserialize instance Binary Address where- put = serialize- get = deserialize+ put = serialize+ get = deserialize -- | 'Address' pays to a public key hash. isPubKeyAddress :: Address -> Bool-isPubKeyAddress PubKeyAddress{} = True+isPubKeyAddress PubKeyAddress {} = True isPubKeyAddress _ = False -- | 'Address' pays to a script hash. isScriptAddress :: Address -> Bool-isScriptAddress ScriptAddress{} = True+isScriptAddress ScriptAddress {} = True isScriptAddress _ = False -{- | 'Address' pays to a witness public key hash. Only valid for SegWit- networks.--}+-- | 'Address' pays to a witness public key hash. Only valid for SegWit+-- networks. isWitnessPubKeyAddress :: Address -> Bool-isWitnessPubKeyAddress WitnessPubKeyAddress{} = True+isWitnessPubKeyAddress WitnessPubKeyAddress {} = True isWitnessPubKeyAddress _ = False isWitnessScriptAddress :: Address -> Bool-isWitnessScriptAddress WitnessScriptAddress{} = True+isWitnessScriptAddress WitnessScriptAddress {} = True isWitnessScriptAddress _ = False isWitnessAddress :: Address -> Bool-isWitnessAddress WitnessAddress{} = True+isWitnessAddress WitnessAddress {} = True isWitnessAddress _ = False -addrToJSON :: Network -> Address -> Value-addrToJSON net a = toJSON (addrToText net a)--addrToEncoding :: Network -> Address -> Encoding-addrToEncoding net = maybe null_ text . addrToText net--{- | JSON parsing for Bitcoin addresses. Works with 'Base58', 'CashAddr' and- 'Bech32'.--}-addrFromJSON :: Network -> Value -> Parser Address-addrFromJSON net =+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+ 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.--}+-- | Convert address to human-readable string. Uses 'Base58', 'Bech32', or+-- 'CashAddr' depending on network. addrToText :: Network -> Address -> Maybe Text-addrToText net a@PubKeyAddress{getAddrHash160 = h}- | isNothing (getCashAddrPrefix net) =- Just . encodeBase58Check . runPutS $ base58put net a- | otherwise = cashAddrEncode net 0 (runPutS $ serialize h)-addrToText net a@ScriptAddress{getAddrHash160 = h}- | isNothing (getCashAddrPrefix net) =- Just . encodeBase58Check . runPutS $ base58put net a- | otherwise =- cashAddrEncode net 1 (runPutS $ serialize h)-addrToText net WitnessPubKeyAddress{getAddrHash160 = h} = do- hrp <- getBech32Prefix net- segwitEncode hrp 0 (B.unpack (runPutS $ serialize h))-addrToText net WitnessScriptAddress{getAddrHash256 = h} = do- hrp <- getBech32Prefix net- segwitEncode hrp 0 (B.unpack (runPutS $ serialize h))-addrToText net WitnessAddress{getAddrVersion = v, getAddrData = d} = do- hrp <- getBech32Prefix net- segwitEncode hrp v (B.unpack d)+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 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+ (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 <- getBech32Prefix net- (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+ 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+ eitherToMaybe . runGetS (base58get net) =<< decodeBase58Check txt -base58get :: MonadGet m => Network -> m Address+base58get :: (MonadGet m) => Network -> m Address base58get net = do- pfx <- getWord8- addr <- deserialize- f pfx addr+ pfx <- getWord8+ addr <- deserialize+ 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"+ | 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 :: (MonadPut m) => Network -> Address -> m () base58put net (PubKeyAddress h) = do- putWord8 (getAddrPrefix net)- serialize h+ putWord8 net.addrPrefix+ serialize h base58put net (ScriptAddress h) = do- putWord8 (getScriptPrefix net)- serialize h+ 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 :: PubKeyI -> Address-pubKeyAddr = PubKeyAddress . addressHash . runPutS . serialize+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 :: PubKeyI -> Address-pubKeyWitnessAddr = WitnessPubKeyAddress . addressHash . runPutS . serialize+-- | 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 :: PubKeyI -> Address-pubKeyCompatWitnessAddr =- p2shAddr- . addressHash- . encodeOutputBS- . PayWitnessPKHash- . addressHash- . runPutS- . serialize+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'.--}+-- | Obtain a SegWit pay-to-witness-public-key-hash (P2WPKH) address from a+-- 'Hash160'. p2wpkhAddr :: Hash160 -> Address p2wpkhAddr = WitnessPubKeyAddress @@ -305,66 +299,68 @@ p2wshAddr = WitnessScriptAddress -- | Compute a standard pay-to-script-hash (P2SH) address for an output script.-payToScriptAddress :: ScriptOutput -> Address-payToScriptAddress = p2shAddr . addressHash . encodeOutputBS+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 :: ScriptOutput -> Address-payToWitnessScriptAddress = p2wshAddr . sha256 . encodeOutputBS+-- | 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 :: ScriptOutput -> Address-payToNestedScriptAddress =- p2shAddr . addressHash . encodeOutputBS . toP2WSH . encodeOutput+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.--}+-- | 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+ \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 :: Address -> Script-addressToScript = encodeOutput . addressToOutput+addressToScript :: Ctx -> Address -> Script+addressToScript ctx = encodeOutput ctx . addressToOutput -- | Encode address as output script in 'ByteString' form.-addressToScriptBS :: Address -> ByteString-addressToScriptBS = runPutS . serialize . addressToScript+addressToScriptBS :: Ctx -> Address -> ByteString+addressToScriptBS ctx = runPutS . serialize . addressToScript ctx -- | 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+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 :: ByteString -> Either String Address-scriptToAddressBS =- maybeToEither "Could not decode address" . outputAddress <=< decodeOutputBS+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 :: ScriptOutput -> Maybe Address-outputAddress =- \case- PayPKHash h -> Just $ PubKeyAddress h- PayScriptHash h -> Just $ ScriptAddress h- PayPK k -> Just $ pubKeyAddr k- PayWitnessPKHash h -> Just $ WitnessPubKeyAddress h- PayWitnessScriptHash h -> Just $ WitnessScriptAddress h- PayWitness v d -> Just $ WitnessAddress v d- _ -> Nothing+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 :: ScriptInput -> Maybe Address-inputAddress =- \case- (RegularInput (SpendPKHash _ key)) -> Just $ pubKeyAddr key- (ScriptHashInput _ rdm) -> Just $ payToScriptAddress rdm- _ -> Nothing+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
@@ -1,41 +1,43 @@+{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE OverloadedStrings #-}--{- |-Module : Haskoin.Address.Base58-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE NoFieldSelectors #-} -Support for legacy 'Base58' addresses. Superseded by Bech32 for Bitcoin SegWit-(BTC) and CashAddr for Bitcoin Cash (BCH).--}-module Haskoin.Address.Base58 (- -- * Base58+-- |+-- 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+ )+where -import Control.Monad-import Data.Array+import Control.Monad (guard)+import Data.Array (Array, assocs, listArray, (!), (//)) import Data.ByteString (ByteString)-import qualified Data.ByteString as BS-import qualified Data.ByteString.Char8 as C-import Data.Bytes.Get-import Data.Bytes.Put-import Data.Bytes.Serial-import Data.Char+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 qualified Data.Text as T-import Data.Word-import Haskoin.Crypto.Hash-import Haskoin.Util+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.@@ -46,16 +48,19 @@ b58Data = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" b58Array :: Array Int Word8-b58Array = listArray (0, 57) (BS.unpack b58Data)+b58Array = listArray (0, 57) (B.unpack b58Data) b58InvArray :: Array Word8 (Maybe Int)-b58InvArray = listArray (minBound, maxBound) (repeat Nothing) // map swap (assocs b58Array)+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.--}+-- | Convert a number less than or equal to provided integer into a 'Base58'+-- character. b58 :: Int -> Word8 b58 = (b58Array !) @@ -63,62 +68,58 @@ 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.--}+-- | 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+ 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.--}+-- | Encode an arbitrary 'ByteString' into a its 'Base58' representation,+-- preserving leading zeroes. encodeBase58 :: ByteString -> Base58 encodeBase58 bs =- l <> r+ l <> r where- (z, b) = BS.span (== 0) bs- l = cs $ BS.replicate (BS.length z) (b58 0) -- preserve leading 0's+ (z, b) = B.span (== 0) bs+ l = cs $ B.replicate (B.length z) (b58 0) -- preserve leading 0's r- | BS.null b = T.empty- | otherwise = encodeBase58I $ bsToInteger b+ | B.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+ B.append prefix <$> r where- (z, b) = BS.span (== b58 0) (cs t)- prefix = BS.replicate (BS.length z) 0 -- preserve leading 1's+ (z, b) = B.span (== b58 0) (cs t)+ prefix = B.replicate (B.length z) 0 -- preserve leading 1's r- | BS.null b = Just BS.empty- | otherwise = integerToBS <$> decodeBase58I (cs b)+ | 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'.--}+-- | 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 $ runPutS $ serialize $ checkSum32 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.--}+-- | 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 == runPutS (serialize (checkSum32 res))- return res+ 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
@@ -1,19 +1,20 @@+{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE MultiWayIf #-} {-# LANGUAGE OverloadedStrings #-}--{- |-Module : Haskoin.Address.Base58-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE NoFieldSelectors #-} -Support for Bitcoin SegWit (BTC) Bech32 addresses. This module is a modified-version of Marko Bencun's reference implementation.--}-module Haskoin.Address.Bech32 (- -- * Bech32+-- |+-- 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 (..),@@ -28,38 +29,39 @@ Word5 (..), word5, fromWord5,-) where+ )+where import Control.Monad (guard)-import Data.Array (- Array,+import Data.Array+ ( Array, assocs, bounds, listArray, (!), (//),- )-import Data.Bits (- Bits,+ )+import Data.Bits+ ( Bits, testBit, unsafeShiftL, unsafeShiftR, xor, (.&.), (.|.),- )-import qualified Data.ByteString as B+ )+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 qualified Data.Text as T-import qualified Data.Text.Encoding as E+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)+ deriving (Eq, Show, Ord, Enum) -- | Bech32 human-readable string. type Bech32 = Text@@ -70,28 +72,28 @@ -- | Data part of 'Bech32' address. type Data = [Word8] -(.>>.), (.<<.) :: Bits a => a -> Int -> a+(.>>.), (.<<.) :: (Bits a) => a -> Int -> a (.>>.) = unsafeShiftR (.<<.) = unsafeShiftL -- | Five-bit word for Bech32. newtype Word5- = UnsafeWord5 Word8- deriving (Eq, Ord)+ = 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+ 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 :: (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 :: (Num a) => Word5 -> a fromWord5 (UnsafeWord5 x) = fromIntegral x {-# INLINE fromWord5 #-} {-# SPECIALIZE INLINE fromWord5 :: Word5 -> Word8 #-}@@ -99,13 +101,13 @@ -- | 'Bech32' character map as array of five-bit integers to character. charset :: Array Word5 Char charset =- listArray (UnsafeWord5 0, UnsafeWord5 31) "qpzry9x8gf2tvdw0s3jn54khce6mua7l"+ 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+ | inRange (bounds inv) upperC = inv ! upperC+ | otherwise = Nothing where upperC = toUpper c inv = listArray ('0', 'Z') (repeat Nothing) // map swap (assocs charset)@@ -116,19 +118,18 @@ bech32Polymod values = foldl' go 1 values .&. 0x3fffffff where go chk value =- foldl' xor chk' [g | (g, i) <- zip generator [25 ..], testBit chk i]+ 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.--}+-- | 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+ map (UnsafeWord5 . (.>>. 5)) hrpBytes+ ++ [UnsafeWord5 0]+ ++ map word5 hrpBytes where hrpBytes = B.unpack $ E.encodeUtf8 hrp @@ -147,49 +148,47 @@ -- | 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+ 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.--}+-- | 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+ 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+ 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.--}+-- | 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')+ 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 @@ -202,67 +201,65 @@ noPadding :: Pad Maybe noPadding frombits bits padValue result = do- guard $ bits < frombits && padValue == 0- return result+ 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]+-- | 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+ 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)+ go dat' acc' (bits' `rem` tobits) (result' : result) where acc' = (acc .<<. frombits) .|. fromIntegral value bits' = bits + frombits result' =- [ (acc' .>>. b) .&. maxv+ [ (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+ 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+ 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)+ 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)+ (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.--}+-- | 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+ 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
@@ -1,17 +1,20 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-}--{- |-Module : Haskoin.Address.CashAddr-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE NoFieldSelectors #-} -Support for Bitcoin Cash (BCH) CashAddr format.--}-module Haskoin.Address.CashAddr (- -- * CashAddr+-- |+-- 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,@@ -22,26 +25,35 @@ cash32encodeType, cash32decode, cash32encode,-) where+ )+where -import Control.Monad+import Control.Monad (guard) import Data.Bits+ ( Bits+ ( shiftL,+ shiftR,+ testBit,+ xor,+ (.&.),+ (.|.)+ ),+ ) 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.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 qualified Data.Text as T-import qualified Data.Text.Encoding as E-import Data.Word-import Haskoin.Data-import Haskoin.Util+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.--}+-- | '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.@@ -50,9 +62,8 @@ -- | 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.--}+-- | 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.@@ -63,156 +74,147 @@ 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.--}+-- | 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)+ 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.--}+-- | 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+ 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.--}+-- | 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)+ 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.--}+-- | 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)+ 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.--}+-- | 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)+ 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' = E.encodeUtf8 text+ bs' = encodeUtf8 text -{- | Low-Level: encode 'Cash32' string for 'CashPrefix' provided. Can encode- arbitrary data. No prefix or length validation is performed.--}+-- | 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+ 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.--}+-- | 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+ 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.--}+-- | 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+ 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)+ 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.--}+-- | 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)+ 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]]+ 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)+ 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]+ [0x98f2bc8e61, 0x79b76d99e2, 0xf33e5fb3c4, 0xae2eabe2a8, 0x1e4f43e470] inner c0 c (b, g)- | c0 `testBit` b = c `xor` g- | otherwise = c+ | c0 `testBit` b = c `xor` g+ | otherwise = c -- | Validate that polymod 'ByteString' (eight bytes) is equal to zero. verifyCash32Polymod :: ByteString -> Bool
src/Haskoin/Block.hs view
@@ -1,18 +1,20 @@-{- |-Module : Haskoin.Block-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE DuplicateRecordFields #-} -Most functions relating to blocks are exported by this module.--}-module Haskoin.Block (- module Haskoin.Block.Common,+-- |+-- 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+ )+where import Haskoin.Block.Common import Haskoin.Block.Headers
src/Haskoin/Block/Common.hs view
@@ -1,19 +1,21 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE OverloadedStrings #-}--{- |-Module : Haskoin.Block.Common-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE NoFieldSelectors #-} -Common data types and functions to handle blocks from the block chain.--}-module Haskoin.Block.Common (- -- * Blocks+-- |+-- 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,@@ -29,53 +31,34 @@ Headers (..), decodeCompact, encodeCompact,-) where+ )+where import Control.DeepSeq-import Control.Monad (forM_, liftM2, mzero, replicateM, (<=<))-import Data.Aeson (- FromJSON (..),- ToJSON (..),- Value (..),- object,- toJSON,- withObject,- withText,- (.:),- (.=),- )-import Data.Aeson.Encoding (pairs, unsafeToEncoding)+import Control.Monad+import Data.Aeson+import Data.Aeson.Encoding import Data.Binary (Binary (..))-import Data.Bits (shiftL, shiftR, (.&.), (.|.))-import qualified Data.ByteString as B+import Data.Bits+import Data.ByteString qualified as B import Data.ByteString.Builder (char7)-import qualified Data.ByteString.Lazy as BL-import Data.Bytes.Get (- MonadGet,- getWord32le,- runGetL,- runGetS,- )-import Data.Bytes.Put (- MonadPut,- putWord32le,- runPutL,- runPutS,- )-import Data.Bytes.Serial (Serial (..))-import Data.Hashable (Hashable)-import Data.Maybe (fromMaybe)+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 (IsString, fromString)-import Data.String.Conversions (cs)+import Data.String+import Data.String.Conversions import Data.Text (Text)-import Data.Word (Word32)-import GHC.Generics (Generic)+import Data.Word+import GHC.Generics import Haskoin.Crypto.Hash import Haskoin.Network.Common import Haskoin.Transaction.Common-import Haskoin.Util-import qualified Text.Read as R+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@@ -85,311 +68,312 @@ -- | Block header and transactions. data Block = Block- { blockHeader :: !BlockHeader- , blockTxns :: ![Tx]- }- deriving (Eq, Show, Read, Generic, Hashable, NFData)+ { 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+ 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+ get = deserialize+ put = serialize instance Binary Block where- get = deserialize- put = serialize+ get = deserialize+ put = serialize instance ToJSON Block where- toJSON (Block h t) = object ["header" .= h, "transactions" .= t]- toEncoding (Block h t) = pairs $ "header" .= h <> "transactions" .= t+ 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"+ parseJSON =+ withObject "Block" $ \o ->+ Block <$> o .: "header" <*> o .: "transactions" -- | Block header hash. To be serialized reversed for display purposes.-newtype BlockHash = BlockHash- { getBlockHash :: Hash256- }- deriving (Eq, Ord, Generic, Hashable, Serial, NFData)+newtype BlockHash = BlockHash {get :: Hash256}+ deriving (Eq, Ord, Generic, Hashable, Serial, NFData) instance Serialize BlockHash where- put = serialize- get = deserialize+ put = serialize+ get = deserialize instance Binary BlockHash where- put = serialize- get = deserialize+ put = serialize+ get = deserialize instance Show BlockHash where- showsPrec _ = shows . blockHashToHex+ showsPrec _ = shows . blockHashToHex instance Read BlockHash where- readPrec = do- R.String str <- R.lexP- maybe R.pfail return $ hexToBlockHash $ cs str+ 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+ 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+ parseJSON =+ withText "BlockHash" $+ maybe mzero return . hexToBlockHash instance ToJSON BlockHash where- toJSON = String . blockHashToHex- toEncoding h =- unsafeToEncoding $- char7 '"'- <> hexBuilder (BL.reverse (runPutL (serialize h)))- <> char7 '"'+ 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.--}+-- | 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.--}+-- | 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+ 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 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)+ { 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- ( "version" .= v- <> "prevblock" .= p- <> "merkleroot" .= encodeHex (runPutS (serialize m))- <> "timestamp" .= t- <> "bits" .= b- <> "nonce" .= n- )+ 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)+ 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- { blockVersion = v- , prevBlock = p- , merkleRoot = m- , blockTimestamp = t- , blockBits = b- , bhNonce = n- }- serialize (BlockHeader v p m bt bb n) = do- putWord32le v- serialize p- serialize m- putWord32le bt- putWord32le bb- putWord32le n+ 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+ put = serialize+ get = deserialize instance Serialize BlockHeader where- put = serialize- get = deserialize+ 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.--}+-- | 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 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- { getBlocksVersion :: !Word32- , -- | block locator object- getBlocksLocator :: !BlockLocator- , -- | hash of the last desired block- getBlocksHashStop :: !BlockHash- }- deriving (Eq, Show, Read, Generic, NFData)+ { 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) = putGetBlockMsg v xs h--instance Serialize GetBlocks where- put = serialize- get = deserialize--putGetBlockMsg :: MonadPut m => Word32 -> BlockLocator -> BlockHash -> m ()-putGetBlockMsg v xs h = do+ 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 -{- | 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.--}+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- { getHeadersVersion :: !Word32- , -- | block locator object- getHeadersBL :: !BlockLocator- , -- | hash of the last desired block header- getHeadersHashStop :: !BlockHash- }- deriving (Eq, Show, Read, Generic, NFData)+ { 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) = putGetBlockMsg v xs h+ 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+ put = serialize+ get = deserialize instance Binary GetHeaders where- put = serialize- get = deserialize+ 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.--}+-- | 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, Read, Generic, NFData)+ { -- | 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+ 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+ 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\).+ put = serialize+ get = deserialize - \[- N = -1^{sign} \times mantissa \times 256^{exponent-3}- \]--}+-- | 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)+ Word32 ->+ -- | true means overflow+ (Integer, Bool) decodeCompact nCompact = (if neg then res * (-1) else res, over) where nSize :: Int@@ -398,23 +382,22 @@ nWord' = nCompact .&. 0x007fffff nWord :: Word32 nWord- | nSize <= 3 = nWord' `shiftR` (8 * (3 - nSize))- | otherwise = nWord'+ | nSize <= 3 = nWord' `shiftR` (8 * (3 - nSize))+ | otherwise = nWord' res :: Integer res- | nSize <= 3 = fromIntegral nWord- | otherwise = fromIntegral nWord `shiftL` (8 * (nSize - 3))+ | 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- )+ 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.--}+-- | Encode an 'Integer' to the compact number format used in the difficulty+-- target of a block. encodeCompact :: Integer -> Word32 encodeCompact i = nCompact where@@ -422,23 +405,23 @@ neg = i < 0 nSize' :: Int nSize' =- let f 0 = 0- f n = 1 + f (n `shiftR` 8)- in f i'+ 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)))+ | 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''' .&. 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'+ | neg && (nCompact' .&. 0x007fffff /= 0) = nCompact' .|. 0x00800000+ | otherwise = nCompact' low64 :: Integer low64 = 0xffffffffffffffff
src/Haskoin/Block/Headers.hs view
@@ -1,1008 +1,974 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE RecordWildCards #-}--{- |-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 qualified Data.ByteString 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 qualified Data.HashMap.Strict 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.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- { nodeHeader :: !BlockHeader- , nodeHeight :: !BlockHeight- , -- | accumulated work so far- nodeWork :: !BlockWork- , -- | skip magic block hash- nodeSkip :: !BlockHash- }- deriving (Show, Read, Generic, Hashable, NFData)--instance Serial BlockNode where- deserialize = do- nodeHeader <- deserialize- nodeHeight <- getWord32le- nodeWork <- getInteger- if nodeHeight == 0- then do- let nodeSkip = headerHash nodeHeader- return BlockNode{..}- else do- nodeSkip <- deserialize- return BlockNode{..}- serialize bn = do- serialize $ nodeHeader bn- putWord32le $ nodeHeight bn- putInteger $ nodeWork bn- case nodeHeight bn of- 0 -> return ()- _ -> serialize $ nodeSkip bn--instance Serialize BlockNode where- put = serialize- get = deserialize--instance Binary BlockNode where- put = serialize- get = deserialize--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 (runPutS (serialize (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 . 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 $ nodeHeader node)- (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 > 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 BlockNode{nodeHeight = 0} = True-isGenesis _ = False---- | Build the genesis 'BlockNode' for the supplied 'Network'.-genesisNode :: Network -> BlockNode-genesisNode net =- BlockNode- { nodeHeader = getGenesisHeader net- , nodeHeight = 0- , nodeWork = headerWork (getGenesisHeader net)- , nodeSkip = headerHash (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 ->- -- | 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 == 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 ->- -- | 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 (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 ->- -- | 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 (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 ->- -- | starts from immediate parent- m [BlockNode]-getParents = getpars []- where- getpars acc 0 _ = return $ reverse acc- getpars acc n BlockNode{..}- | nodeHeight == 0 = return $ reverse acc- | otherwise = 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 ->- -- | new child height- BlockHeight ->- -- | new child hash- BlockHash ->- 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 ->- -- | 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, _) <- 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 ->- -- | new child height- BlockHeight ->- -- | new child hash- BlockHash ->- 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 ->- -- | new child height- BlockHeight ->- -- | new child version- Word32 ->- 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 _ bn@BlockNode{nodeHeight = 0} = return bn-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--{- | 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 (nodeHeight par > nodeHeight anchor)- return $ nextAsertWorkRequired net anchor- daa = do- daa_height <- getDaaBlockHeight net- guard (nodeHeight par + 1 >= daa_height)- return $ nextDaaWorkRequired net- eda = do- eda_height <- getEdaBlockHeight net- guard (nodeHeight par + 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- | 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- 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- height = nodeHeight par- e1 = error "Cannot get ancestor at parent - 144 height"- minDifficulty =- blockTimestamp bh- > blockTimestamp (nodeHeader par) + getTargetSpacing net * 2--mtp :: BlockHeaders m => BlockNode -> m Timestamp-mtp bn- | nodeHeight bn == 0 = return 0- | otherwise = do- pars <- getParents 11 bn- return $ medianTime (map (blockTimestamp . nodeHeader) 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 = nodeHeight b - nodeHeight a <= 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 (nodeHeight a) (nodeHeight b)--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 getAsertActivationTime net 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 (prevBlock (nodeHeader anchor))- let anchor_parent_time = toInteger $ blockTimestamp $ nodeHeader anchor_parent- time_diff = current_time - anchor_parent_time- return $ computeAsertBits halflife anchor_bits time_diff height_diff- where- halflife = getAsertHalfLife net- anchor_height = toInteger $ nodeHeight anchor- anchor_bits = blockBits $ nodeHeader anchor- current_height = toInteger (nodeHeight par) + 1- height_diff = current_height - anchor_height- current_time = toInteger $ blockTimestamp bh- 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 = (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 ->- -- | last block in previous retarget (parent)- BlockHeader ->- -- | timestamp of first block in previous retarget- Timestamp ->- 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 . 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 $ 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 ->- -- | 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- { prevBlock = headerHash bh- , -- Just to make it different in every header- merkleRoot = 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 (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+{-# 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
@@ -1,18 +1,20 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}--{- |-Module : Haskoin.Block.Merkle-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE NoFieldSelectors #-} -Function to deal with Merkle trees inside blocks.--}-module Haskoin.Block.Merkle (- -- * Merkle Blocks+-- |+-- 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,@@ -32,7 +34,8 @@ extractMatches, splitIn, boolsToWord8,-) where+ )+where import Control.DeepSeq import Control.Monad (forM_, replicateM, when)@@ -50,8 +53,8 @@ import GHC.Generics import Haskoin.Block.Common import Haskoin.Crypto.Hash-import Haskoin.Data import Haskoin.Network.Common+import Haskoin.Network.Data import Haskoin.Transaction.Common -- | Hash of the block's Merkle root.@@ -63,53 +66,52 @@ -- | 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.--}+-- | 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)+ { -- | 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)+ 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+ 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+ put = serialize+ get = deserialize instance Serialize MerkleBlock where- put = serialize- get = deserialize+ 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)]- ]+ [ b | p <- [0 .. length ws * 8 - 1], b <- [testBit (ws !! (p `div` 8)) (p `mod` 8)]+ ] -- | Pack Merkle flags from 'FlagBits'. encodeMerkleFlags :: FlagBits -> [Word8]@@ -117,33 +119,32 @@ -- | Computes the height of a Merkle tree. calcTreeHeight ::- -- | number of transactions (leaf nodes)- Int ->- -- | height of the merkle tree- Int+ -- | 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+ | 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.--}+-- | 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+ -- | 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+ -- | transaction hashes (leaf nodes)+ [TxHash] ->+ -- | root of the Merkle tree+ MerkleRoot buildMerkleRoot txs = calcHash (calcTreeHeight $ length txs) 0 txs -- | Concatenate and compute double SHA256.@@ -152,46 +153,44 @@ -- | 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+ -- | 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 = getTxHash $ txs !! pos- | otherwise = hash2 left right+ | 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+ | 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.--}+-- | 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)+ -- | 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.--}+-- | Helper function to build partial Merkle tree. Used by 'buildPartialMerkle'+-- above. traverseAndBuild ::- Int -> Int -> [(TxHash, Bool)] -> (FlagBits, PartialMerkleTree)+ 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)+ | 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@@ -199,88 +198,86 @@ 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 = ([], [])+ | (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)+ 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)+ | 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)+ | 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)+ 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'.--}+-- | 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])+ 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 > 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)+ | 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.--}+-- | 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@@ -294,15 +291,11 @@ -- | 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+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
− src/Haskoin/Constants.hs
@@ -1,615 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}--{- |-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.Constants (- Network (..),-- -- * 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.Data-import Haskoin.Network.Common-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) . getNetworkName) allNets---- | Bitcoin SegWit network. Symbol: BTC.-btc :: Network-btc =- Network- { getNetworkName = "btc"- , getAddrPrefix = 0- , getScriptPrefix = 5- , getSecretPrefix = 128- , getExtPubKeyPrefix = 0x0488b21e- , getExtSecretPrefix = 0x0488ade4- , getNetworkMagic = 0xf9beb4d9- , getGenesisHeader =- BlockHeader- 0x01- "0000000000000000000000000000000000000000000000000000000000000000"- (buildMerkleRoot [txHash genesisTx])- 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- , "dnsseed.emzy.de" -- Stephan Oeste- , "seed.bitcoin.wiz.biz" -- Jason Maurice- ]- , getBip44Coin = 0- , getSigHashForkId = Nothing- , getEdaBlockHeight = Nothing- , getDaaBlockHeight = Nothing- , getAsertActivationTime = Nothing- , getAsertHalfLife = 0- , getSegWit = True- , getCashAddrPrefix = Nothing- , getBech32Prefix = Just "bc"- , getReplaceByFee = True- , getHalvingInterval = 210000- }---- | Testnet for Bitcoin SegWit network.-btcTest :: Network-btcTest =- Network- { getNetworkName = "btctest"- , getAddrPrefix = 111- , getScriptPrefix = 196- , getSecretPrefix = 239- , getExtPubKeyPrefix = 0x043587cf- , getExtSecretPrefix = 0x04358394- , getNetworkMagic = 0x0b110907- , getGenesisHeader =- BlockHeader- 0x01- "0000000000000000000000000000000000000000000000000000000000000000"- (buildMerkleRoot [txHash genesisTx])- 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- , getAsertActivationTime = Nothing- , getAsertHalfLife = 0- , getSegWit = True- , getCashAddrPrefix = Nothing- , getBech32Prefix = Just "tb"- , getReplaceByFee = True- , getHalvingInterval = 210000- }---- | RegTest for Bitcoin SegWit network.-btcRegTest :: Network-btcRegTest =- Network- { getNetworkName = "btcreg"- , getAddrPrefix = 111- , getScriptPrefix = 196- , getSecretPrefix = 239- , getExtPubKeyPrefix = 0x043587cf- , getExtSecretPrefix = 0x04358394- , getNetworkMagic = 0xfabfb5da- , getGenesisHeader =- BlockHeader- -- 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206- 0x01- "0000000000000000000000000000000000000000000000000000000000000000"- (buildMerkleRoot [txHash genesisTx])- 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- , getAsertActivationTime = Nothing- , getAsertHalfLife = 0- , getSegWit = True- , getCashAddrPrefix = Nothing- , getBech32Prefix = Just "bcrt"- , getReplaceByFee = True- , getHalvingInterval = 150- }---- | Bitcoin Cash network. Symbol: BCH.-bch :: Network-bch =- Network- { getNetworkName = "bch"- , getAddrPrefix = 0- , getScriptPrefix = 5- , getSecretPrefix = 128- , getExtPubKeyPrefix = 0x0488b21e- , getExtSecretPrefix = 0x0488ade4- , getNetworkMagic = 0xe3e1f3e8- , getGenesisHeader =- BlockHeader- 0x01- "0000000000000000000000000000000000000000000000000000000000000000"- (buildMerkleRoot [txHash genesisTx])- 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-bch.bitcoinforks.org"- , "btccash-seeder.bitcoinunlimited.info"- , "seed.bchd.cash"- , "seed.bch.loping.net"- , "dnsseed.electroncash.de"- ]- , getBip44Coin = 145- , getSigHashForkId = Just 0- , getEdaBlockHeight = Just 478559- , getDaaBlockHeight = Just 404031- , getAsertActivationTime = Just 1605441600- , getAsertHalfLife = 60 * 60 * 10- , getSegWit = False- , getCashAddrPrefix = Just "bitcoincash"- , getBech32Prefix = Nothing- , getReplaceByFee = False- , getHalvingInterval = 210000- }---- | Testnet for Bitcoin Cash network.-bchTest4 :: Network-bchTest4 =- Network- { getNetworkName = "bchtest4"- , getAddrPrefix = 111- , getScriptPrefix = 196- , getSecretPrefix = 239- , getExtPubKeyPrefix = 0x043587cf- , getExtSecretPrefix = 0x04358394- , getNetworkMagic = 0xe2b7daaf- , getGenesisHeader =- BlockHeader- 0x01- "0000000000000000000000000000000000000000000000000000000000000000"- (buildMerkleRoot [txHash genesisTx])- 1597811185- 0x1d00ffff- 114152193- , -- Hash 000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943- getMaxBlockSize = 2000000- , getMaxSatoshi = 2100000000000000- , getHaskoinUserAgent = "/haskoin-bch-test4:" <> versionString <> "/"- , getDefaultPort = 28333- , getAllowMinDifficultyBlocks = True- , getPowNoRetargetting = False- , getPowLimit =- 0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff- , getBip34Block =- ( 2- , "00000000b0c65b1e03baace7d5c093db0d6aac224df01484985ffd5e86a1a20c"- )- , getBip65Height = 3- , getBip66Height = 4- , getTargetTimespan = 14 * 24 * 60 * 60- , getTargetSpacing = 10 * 60- , getCheckpoints =- [- ( 5000- , "000000009f092d074574a216faec682040a853c4f079c33dfd2c3ef1fd8108c4"- )- , -- Axion activation-- ( 16845- , "00000000fb325b8f34fe80c96a5f708a08699a68bbab82dba4474d86bd743077"- )- ,- ( 38000- , "000000000015197537e59f339e3b1bbf81a66f691bd3d7aa08560fc7bf5113fb"- )- ,- ( 54700- , "00000000009af4379d87f17d0f172ee4769b48839a5a3a3e81d69da4322518b8"- )- ]- , getSeeds =- [ "testnet4-seed-bch.bitcoinforks.org"- , "testnet4-seed-bch.toom.im"- , "seed.tbch4.loping.net"- , "testnet4-seed.flowee.cash"- ]- , getBip44Coin = 1- , getSigHashForkId = Just 0- , getEdaBlockHeight = Just 7- , getDaaBlockHeight = Just 3000- , getAsertActivationTime = Just 1605441600- , getAsertHalfLife = 60 * 60- , getSegWit = False- , getCashAddrPrefix = Just "bchtest"- , getBech32Prefix = Nothing- , getReplaceByFee = False- , getHalvingInterval = 210000- }---- | Testnet for Bitcoin Cash network.-bchTest :: Network-bchTest =- Network- { getNetworkName = "bchtest"- , getAddrPrefix = 111- , getScriptPrefix = 196- , getSecretPrefix = 239- , getExtPubKeyPrefix = 0x043587cf- , getExtSecretPrefix = 0x04358394- , getNetworkMagic = 0xf4e5f3f4- , getGenesisHeader =- BlockHeader- 0x01- "0000000000000000000000000000000000000000000000000000000000000000"- (buildMerkleRoot [txHash genesisTx])- 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-bch.bitcoinforks.org"- , "testnet-seed.bchd.cash"- , "seed.tbch.loping.net"- ]- , getBip44Coin = 1- , getSigHashForkId = Just 0- , getEdaBlockHeight = Just 1155876- , getDaaBlockHeight = Just 1188697- , getAsertActivationTime = Just 1605441600- , getAsertHalfLife = 60 * 60- , getSegWit = False- , getCashAddrPrefix = Just "bchtest"- , getBech32Prefix = Nothing- , getReplaceByFee = False- , getHalvingInterval = 210000- }---- | RegTest for Bitcoin Cash network.-bchRegTest :: Network-bchRegTest =- Network- { getNetworkName = "bchreg"- , getAddrPrefix = 111- , getScriptPrefix = 196- , getSecretPrefix = 239- , getExtPubKeyPrefix = 0x043587cf- , getExtSecretPrefix = 0x04358394- , getNetworkMagic = 0xdab5bffa- , getGenesisHeader =- BlockHeader- -- 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206- 0x01- "0000000000000000000000000000000000000000000000000000000000000000"- (buildMerkleRoot [txHash genesisTx])- 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- , getAsertActivationTime = Just 1605441600- , getAsertHalfLife = 2 * 24 * 60 * 60- , 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, bchTest4, bchTest, btcRegTest, bchRegTest]
src/Haskoin/Crypto.hs view
@@ -1,19 +1,23 @@-{- |-Module : Haskoin.Crypto-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE DuplicateRecordFields #-} -Hashing functions and ECDSA signatures.--}-module Haskoin.Crypto (+-- |+-- 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,- module Secp256k1,-) where+ )+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
@@ -1,24 +1,29 @@-{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE TypeApplications #-}--{- |-Module : Haskoin.Crypto.Hash-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE NoFieldSelectors #-} -Hashing functions and corresponding data types. Uses functions from the-cryptonite library.--}-module Haskoin.Crypto.Hash (- -- * Hashes- Hash512 (getHash512),- Hash256 (getHash256),- Hash160 (getHash160),- CheckSum32 (getCheckSum32),+-- |+-- 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,@@ -31,233 +36,232 @@ split512, join512, initTaggedHash,-) where+ )+where import Control.DeepSeq-import Crypto.Hash (- Context,- RIPEMD160 (..),- SHA1 (..),- SHA256 (..),- SHA512 (..),- hashInit,- hashUpdates,- hashWith,- )+import Crypto.Hash import Crypto.MAC.HMAC (HMAC, hmac) import Data.Binary (Binary (..))-import Data.ByteArray (ByteArrayAccess)-import qualified Data.ByteArray as BA+import Data.ByteArray (ByteArrayAccess, convert) import Data.ByteString (ByteString)-import qualified Data.ByteString as BS-import Data.ByteString.Short (ShortByteString)-import qualified Data.ByteString.Short as BSS-import qualified Data.Bytes.Get as Get-import qualified Data.Bytes.Put as Put+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+import Haskoin.Util.Helpers+import Haskoin.Util.Marshal import Text.Read as R -- | 'Word32' wrapped for type-safe 32-bit checksums. newtype CheckSum32 = CheckSum32- { getCheckSum32 :: Word32- }- deriving (Eq, Ord, Serial, Show, Read, Hashable, Generic, NFData)+ { 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+ put = serialize+ get = deserialize instance Binary CheckSum32 where- put = serialize- get = deserialize+ put = serialize+ get = deserialize -- | Type for 512-bit hashes.-newtype Hash512 = Hash512 {getHash512 :: ShortByteString}- deriving (Eq, Ord, Hashable, Generic, NFData)+newtype Hash512 = Hash512 {get :: ShortByteString}+ deriving (Eq, Ord, Generic)+ deriving newtype (Hashable, NFData) -- | Type for 256-bit hashes.-newtype Hash256 = Hash256 {getHash256 :: ShortByteString}- deriving (Eq, Ord, Hashable, Generic, NFData)+newtype Hash256 = Hash256 {get :: ShortByteString}+ deriving (Eq, Ord, Generic)+ deriving newtype (Hashable, NFData) -- | Type for 160-bit hashes.-newtype Hash160 = Hash160 {getHash160 :: ShortByteString}- deriving (Eq, Ord, Hashable, Generic, NFData)+newtype Hash160 = Hash160 {get :: ShortByteString}+ deriving (Eq, Ord, Generic)+ deriving newtype (Hashable, NFData) instance Show Hash512 where- showsPrec _ = shows . encodeHex . BSS.fromShort . getHash512+ showsPrec _ = shows . encodeHex . fromShort . (.get) instance Read Hash512 where- readPrec = do- R.String str <- lexP- maybe pfail return $ Hash512 . BSS.toShort <$> decodeHex (cs str)+ readPrec = do+ R.String str <- lexP+ maybe pfail (return . Hash512 . toShort) (decodeHex (cs str)) instance Show Hash256 where- showsPrec _ = shows . encodeHex . BSS.fromShort . getHash256+ showsPrec _ = shows . encodeHex . fromShort . (.get) instance Read Hash256 where- readPrec = do- R.String str <- lexP- maybe pfail return $ Hash256 . BSS.toShort <$> decodeHex (cs str)+ readPrec = do+ R.String str <- lexP+ maybe pfail (return . Hash256 . toShort) (decodeHex (cs str)) instance Show Hash160 where- showsPrec _ = shows . encodeHex . BSS.fromShort . getHash160+ showsPrec _ = shows . encodeHex . fromShort . (.get) instance Read Hash160 where- readPrec = do- R.String str <- lexP- maybe pfail return $ Hash160 . BSS.toShort <$> decodeHex (cs str)+ 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 BS.length bs of- 64 -> Hash512 (BSS.toShort bs)- _ -> e- where- e = error "Could not decode hash from hex string"+ 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 . BSS.toShort <$> Get.getByteString 64- serialize = Put.putByteString . BSS.fromShort . getHash512+ deserialize = Hash512 . toShort <$> getByteString 64+ serialize = putByteString . fromShort . (.get) instance Serialize Hash512 where- put = serialize- get = deserialize+ put = serialize+ get = deserialize instance Binary Hash512 where- put = serialize- get = deserialize+ put = serialize+ get = deserialize 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"+ 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 . BSS.toShort <$> Get.getByteString 32- serialize = Put.putByteString . BSS.fromShort . getHash256+ deserialize = Hash256 . toShort <$> getByteString 32+ serialize = putByteString . fromShort . (.get) instance Serialize Hash256 where- put = serialize- get = deserialize+ put = serialize+ get = deserialize instance Binary Hash256 where- put = serialize- get = deserialize+ put = serialize+ get = deserialize 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"+ 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 . BSS.toShort <$> Get.getByteString 20- serialize = Put.putByteString . BSS.fromShort . getHash160+ deserialize = Hash160 . toShort <$> getByteString 20+ serialize = putByteString . fromShort . (.get) instance Serialize Hash160 where- put = serialize- get = deserialize+ put = serialize+ get = deserialize instance Binary Hash160 where- put = serialize- get = deserialize+ put = serialize+ get = deserialize -- | Calculate SHA512 hash.-sha512 :: ByteArrayAccess b => b -> Hash512-sha512 = Hash512 . BSS.toShort . BA.convert . hashWith SHA512+sha512 :: (ByteArrayAccess b) => b -> Hash512+sha512 = Hash512 . toShort . convert . hashWith SHA512 -- | Calculate SHA256 hash.-sha256 :: ByteArrayAccess b => b -> Hash256-sha256 = Hash256 . BSS.toShort . BA.convert . hashWith SHA256+sha256 :: (ByteArrayAccess b) => b -> Hash256+sha256 = Hash256 . toShort . convert . hashWith SHA256 -- | Calculate RIPEMD160 hash.-ripemd160 :: ByteArrayAccess b => b -> Hash160-ripemd160 = Hash160 . BSS.toShort . BA.convert . hashWith RIPEMD160+ripemd160 :: (ByteArrayAccess b) => b -> Hash160+ripemd160 = Hash160 . toShort . convert . hashWith RIPEMD160 -- | Claculate SHA1 hash.-sha1 :: ByteArrayAccess b => b -> Hash160-sha1 = Hash160 . BSS.toShort . BA.convert . hashWith SHA1+sha1 :: (ByteArrayAccess b) => b -> Hash160+sha1 = Hash160 . toShort . convert . hashWith SHA1 -- | Compute two rounds of SHA-256.-doubleSHA256 :: ByteArrayAccess b => b -> Hash256+doubleSHA256 :: (ByteArrayAccess b) => b -> Hash256 doubleSHA256 =- Hash256 . BSS.toShort . BA.convert . hashWith SHA256 . hashWith SHA256+ Hash256 . toShort . convert . hashWith SHA256 . hashWith SHA256 -- | Compute SHA-256 followed by RIPMED-160.-addressHash :: ByteArrayAccess b => b -> Hash160+addressHash :: (ByteArrayAccess b) => b -> Hash160 addressHash =- Hash160 . BSS.toShort . BA.convert . hashWith RIPEMD160 . hashWith SHA256+ Hash160 . toShort . convert . hashWith RIPEMD160 . hashWith SHA256 {- CheckSum -} -- | Computes a 32 bit checksum.-checkSum32 :: ByteArrayAccess b => b -> CheckSum32+checkSum32 :: (ByteArrayAccess b) => b -> CheckSum32 checkSum32 =- fromRight (error "Could not decode bytes as CheckSum32")- . Get.runGetS deserialize- . BS.take 4- . BA.convert- . hashWith SHA256- . hashWith SHA256+ 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 $ BSS.toShort $ BA.convert (hmac key msg :: HMAC SHA512)+ 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 $ BSS.toShort $ BA.convert (hmac key msg :: HMAC SHA256)+ Hash256 $ toShort $ 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))+ (Hash256 (toShort a), Hash256 (toShort b)) where- (a, b) = BS.splitAt 32 . BSS.fromShort $ getHash512 h+ (a, b) = B.splitAt 32 $ fromShort h.get -- | 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)--{- | Initialize tagged hash specified in BIP340+join512 (a, b) = Hash512 (toShort (a.get `app` b.get))+ where+ app = B.append `on` fromShort -@since 0.21.0--}+-- | Initialize tagged hash specified in BIP340+--+-- @since 0.21.0 initTaggedHash ::- -- | Hash tag- ByteString ->- Context SHA256+ -- | Hash tag+ ByteString ->+ Context SHA256 initTaggedHash tag =- (`hashUpdates` [hashedTag, hashedTag]) $- hashInit @SHA256+ (`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
@@ -1,93 +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- putSig,- getSig,+-- |+-- 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+ )+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 qualified Data.ByteString as BS+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+ 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 :: SecKey -> Hash256 -> Sig-signHash k = signMsg k . hashToMsg+signHash :: Ctx -> SecKey -> Hash256 -> Sig+signHash ctx k = signMsg ctx k . hashToMsg -- | Verify an ECDSA signature for a 256-bit hash.-verifyHashSig :: Hash256 -> Sig -> PubKey -> Bool-verifyHashSig h s p = verifySig p norm (hashToMsg h)+verifyHashSig :: Ctx -> Hash256 -> Sig -> PubKey -> Bool+verifyHashSig ctx h s p = verifySig ctx p norm (hashToMsg h) where- norm = fromMaybe s (normalizeSig s)+ norm = fromMaybe s (normalizeSig ctx s) --- | Deserialize an ECDSA signature as commonly encoded in Bitcoin.-getSig :: MonadGet m => m 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+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 bs of- Just s -> return s- Nothing -> fail "Invalid signature"+ case decodeStrictSig ctx bs of+ Just s -> return s+ Nothing -> fail "Invalid signature" --- | Serialize an ECDSA signature for Bitcoin use.-putSig :: MonadPut m => Sig -> m ()-putSig s = putByteString $ exportSig s+ 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 :: Sig -> Bool-isCanonicalHalfOrder = isNothing . normalizeSig+isCanonicalHalfOrder :: Ctx -> Sig -> Bool+isCanonicalHalfOrder ctx = isNothing . normalizeSig ctx -- | Decode signature strictly.-decodeStrictSig :: ByteString -> Maybe Sig-decodeStrictSig bs = do- g <- importSig 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 g- let zero = BS.replicate 32 0- guard $ BS.take 32 (getCompactSig compact) /= zero- guard $ BS.take 32 (BS.drop 32 (getCompactSig compact)) /= zero- guard $ isCanonicalHalfOrder g- return g+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/Data.hs
@@ -1,93 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}--module Haskoin.Data (- Network (..),-) 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.Serialize (Serialize (..))-import Data.String-import Data.Text (Text)-import Data.Word (Word32, Word64, Word8)-import GHC.Generics (Generic)-import Haskoin.Block.Common-import Text.Read---- | Network definition.-data Network = Network- { -- | lowercase alphanumeric and dashes- getNetworkName :: !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)- , -- | asert3-2d algorithm activation time- -- TODO: Replace with block height after fork- getAsertActivationTime :: !(Maybe Word32)- , -- | asert3-2d algorithm halflife (not used for non-BCH networks)- getAsertHalfLife :: !Integer- , -- | 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, Show, Read, Generic, NFData)
− src/Haskoin/Keys.hs
@@ -1,20 +0,0 @@-{- |-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.Keys (- module Haskoin.Keys.Common,- module Haskoin.Keys.Extended,- module Haskoin.Keys.Mnemonic,-) where--import Haskoin.Keys.Common-import Haskoin.Keys.Extended-import Haskoin.Keys.Mnemonic
− src/Haskoin/Keys/Common.hs
@@ -1,190 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE OverloadedStrings #-}-{-# 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.Keys.Common (- -- * Public & Private Keys- PubKeyI (..),- SecKeyI (..),- exportPubKey,- importPubKey,- wrapPubKey,- derivePubKeyI,- wrapSecKey,- fromMiniKey,- tweakPubKey,- tweakSecKey,- getSecKey,- secKey,-- -- ** Private Key Wallet Import Format (WIF)- fromWif,- toWif,-) where--import Control.DeepSeq-import Control.Monad (guard, mzero, (<=<))-import Crypto.Secp256k1-import Data.Aeson (- FromJSON,- ToJSON (..),- Value (String),- parseJSON,- withText,- )-import Data.Aeson.Encoding (unsafeToEncoding)-import Data.Binary (Binary (..))-import Data.ByteString (ByteString)-import qualified Data.ByteString 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.Data-import 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 . runGetS deserialize <=< decodeHex $ cs str- where- e = error "Could not decode public key"--instance ToJSON PubKeyI where- toJSON = String . encodeHex . runPutS . serialize- toEncoding s =- unsafeToEncoding $- char7 '"'- <> hexBuilder (runPutL (serialize s))- <> char7 '"'--instance FromJSON PubKeyI where- parseJSON =- withText "PubKeyI" $- maybe mzero return . ((eitherToMaybe . runGetS deserialize) <=< decodeHex)--instance Serial PubKeyI where- deserialize =- s >>= \case- True -> c- False -> u- where- s =- lookAhead $- getWord8 >>= \case- 0x02 -> return True- 0x03 -> return True- 0x04 -> return False- _ -> fail "Not a public key"- c = do- bs <- getByteString 33- maybe (fail "Could not decode public key") return $- PubKeyI <$> importPubKey bs <*> pure True- u = do- bs <- getByteString 65- maybe (fail "Could not decode public key") return $- PubKeyI <$> importPubKey bs <*> pure False-- serialize pk = putByteString $ exportPubKey (pubKeyCompressed pk) (pubKeyPoint pk)--instance Serialize PubKeyI where- put = serialize- get = deserialize--instance Binary PubKeyI where- put = serialize- get = deserialize---- | 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 (runPutS (serialize 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---- | Tweak a private key.-tweakSecKey :: SecKey -> Hash256 -> Maybe SecKey-tweakSecKey key h = tweakAddSecKey key =<< tweak (runPutS (serialize h))---- | Decode Casascius mini private keys (22 or 30 characters).-fromMiniKey :: ByteString -> Maybe SecKeyI-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 SecKeyI-fromWif net wif = do- bs <- decodeBase58Check wif- -- Check that this is a private key- guard (BS.head bs == getSecretPrefix net)- 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 -> SecKeyI -> Base58-toWif net (SecKeyI k c) =- encodeBase58Check . BS.cons (getSecretPrefix net) $- if c- then getSecKey k `BS.snoc` 0x01- else getSecKey k
− src/Haskoin/Keys/Extended.hs
@@ -1,1084 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE OverloadedStrings #-}--{- |-Module : Haskoin.Keys.Extended-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX--BIP-32 extended keys.--}-module Haskoin.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,- xPubToJSON,- xPubToEncoding,- xPubFromJSON,- xPrvExport,- xPrvToJSON,- xPrvToEncoding,- 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.Encoding (Encoding, text)-import Data.Aeson.Types (Parser)-import Data.Binary (Binary (get, put))-import Data.Bits (clearBit, setBit, testBit)-import Data.ByteString (ByteString)-import qualified Data.ByteString as B-import Data.Bytes.Get-import Data.Bytes.Put-import Data.Bytes.Serial-import Data.Either (fromRight)-import Data.Hashable-import Data.List (foldl')-import Data.List.Split (splitOn)-import Data.Maybe (fromMaybe)-import Data.Serialize (Serialize (..))-import qualified Data.Serialize as S-import Data.String (IsString, fromString)-import Data.String.Conversions (cs)-import qualified Data.Text as Text-import Data.Typeable (Typeable)-import Data.Word (Word32, Word8)-import GHC.Generics (Generic)-import Haskoin.Address-import Haskoin.Crypto.Hash-import Haskoin.Data-import Haskoin.Keys.Common-import Haskoin.Keys.Extended.Internal (- Fingerprint (..),- fingerprintToText,- textToFingerprint,- )-import Haskoin.Script-import 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--{- | 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- xPrvDepth :: !Word8- , -- | fingerprint of parent- xPrvParent :: !Fingerprint- , -- | derivation index- xPrvIndex :: !KeyIndex- , -- | chain code- xPrvChain :: !ChainCode- , -- | private key of this node- xPrvKey :: !SecKey- }- deriving (Generic, Eq, Show, Read, NFData, Hashable)--instance Serial XPrvKey where- serialize k = do- putWord8 $ xPrvDepth k- serialize $ xPrvParent k- putWord32be $ xPrvIndex k- serialize $ xPrvChain k- putPadPrvKey $ xPrvKey k- deserialize =- XPrvKey <$> getWord8- <*> deserialize- <*> getWord32be- <*> deserialize- <*> getPadPrvKey--instance Binary XPrvKey where- put = serialize- get = deserialize--instance Serialize XPrvKey where- put = serialize- get = deserialize--xPrvToJSON :: Network -> XPrvKey -> Value-xPrvToJSON net = A.String . xPrvExport net--xPrvToEncoding :: Network -> XPrvKey -> Encoding-xPrvToEncoding net = text . xPrvExport 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---- | Data type representing an extended BIP32 public key.-data XPubKey = XPubKey- { -- | depth in the tree- xPubDepth :: !Word8- , -- | fingerprint of parent- xPubParent :: !Fingerprint- , -- | derivation index- xPubIndex :: !KeyIndex- , -- | chain code- xPubChain :: !ChainCode- , -- | public key of this node- xPubKey :: !PubKey- }- deriving (Generic, Eq, Show, Read, NFData, Hashable)--instance Serial XPubKey where- serialize k = do- putWord8 $ xPubDepth k- serialize $ xPubParent k- putWord32be $ xPubIndex k- serialize $ xPubChain k- serialize $ wrapPubKey True (xPubKey k)- deserialize =- XPubKey <$> getWord8- <*> deserialize- <*> getWord32be- <*> deserialize- <*> (pubKeyPoint <$> deserialize)--instance Serialize XPubKey where- put = serialize- get = deserialize--instance Binary XPubKey where- put = serialize- get = deserialize---- | 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--xPubToEncoding :: Network -> XPubKey -> Encoding-xPubToEncoding net = text . xPubExport net--{- | 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 :: 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 ::- -- | extended parent private key- XPrvKey ->- -- | child derivation index- KeyIndex ->- -- | extended child private key- XPrvKey-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) (runPutS (serialize child))- (a, c) = split512 $ hmac512 (runPutS $ serialize $ 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 ::- -- | extended parent public key- XPubKey ->- -- | child derivation index- KeyIndex ->- -- | extended child public key- XPubKey-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)) (runPutS $ serialize child)- (a, c) = split512 $ hmac512 (runPutS $ serialize $ 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 ::- -- | extended parent private key- XPrvKey ->- -- | child derivation index- KeyIndex ->- -- | extended child private key- XPrvKey-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) (runPutS $ serialize i)- (a, c) = split512 $ hmac512 (runPutS $ serialize $ 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 . runPutS . serialize . sha256 . exportPubKey True . xPubKey---- | Computes the key fingerprint of an extended private key.-xPrvFP :: XPrvKey -> Fingerprint-xPrvFP =- fromRight err . runGetS deserialize . B.take 4 . runPutS . serialize . xPrvID- where- err = error "Could not decode xPrvFP"---- | Computes the key fingerprint of an extended public key.-xPubFP :: XPubKey -> Fingerprint-xPubFP =- fromRight err . runGetS deserialize . B.take 4 . runPutS . serialize . 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 . runPutS . putXPrvKey net---- | Exports an extended public key to the BIP32 key export format ('Base58').-xPubExport :: Network -> XPubKey -> Base58-xPubExport net = encodeBase58Check . runPutS . 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 . runGetS (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 . runGetS (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 :: MonadGet m => Network -> m XPrvKey-getXPrvKey net = do- ver <- getWord32be- unless (ver == getExtSecretPrefix net) $- fail- "Get: Invalid version for extended private key"- deserialize---- | Serialize an extended private key.-putXPrvKey :: MonadPut m => Network -> XPrvKey -> m ()-putXPrvKey net k = do- putWord32be $ getExtSecretPrefix net- serialize k---- | Parse a binary extended public key.-getXPubKey :: MonadGet m => Network -> m XPubKey-getXPubKey net = do- ver <- getWord32be- unless (ver == getExtPubKeyPrefix net) $- fail- "Get: Invalid version for extended public key"- deserialize---- | Serialize an extended public key.-putXPubKey :: MonadPut m => Network -> XPubKey -> m ()-putXPubKey net k = do- putWord32be $ getExtPubKeyPrefix net- serialize 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- 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 :: 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- toEncoding = text . 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- 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 {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 :: MonadGet m => m SecKey-getPadPrvKey = do- pad <- getWord8- unless (pad == 0x00) $ fail "Private key must be padded with 0x00"- bs <- getByteString 32- case runGetS S.get bs of- Left e -> fail e- Right x -> return x---- | Serialize HDW-specific private key.-putPadPrvKey :: MonadPut m => SecKey -> m ()-putPadPrvKey p = putWord8 0x00 >> putByteString (runPutS (S.put p))--bsPadPrvKey :: SecKey -> ByteString-bsPadPrvKey = runPutS . putPadPrvKey
− src/Haskoin/Keys/Extended/Internal.hs
@@ -1,83 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}--module Haskoin.Keys.Extended.Internal (- Fingerprint (..),- fingerprintToText,- textToFingerprint,-) where--import Control.DeepSeq (NFData)-import Control.Monad ((>=>))-import Data.Aeson (- FromJSON,- ToJSON,- parseJSON,- toJSON,- withText,- )-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 qualified Data.Serialize as S-import Data.String (IsString (..))-import Data.Text (Text)-import qualified Data.Text as Text-import Data.Typeable (Typeable)-import Data.Word (Word32)-import GHC.Generics (Generic)-import Haskoin.Util (decodeHex, encodeHex)-import Text.Read (readEither, readPrec)---- | Fingerprint of parent-newtype Fingerprint = Fingerprint {unFingerprint :: 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 . unFingerprint- 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
− src/Haskoin/Keys/Mnemonic.hs
@@ -1,2233 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--{- |-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.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 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 (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` 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` 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' (flip M.insert) 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/Haskoin/Network.hs view
@@ -1,21 +1,27 @@-{- |-Module : Haskoin.Network-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE DuplicateRecordFields #-} -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 : 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+ )+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
@@ -1,21 +1,27 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}--{- |-Module : Haskoin.Network.Bloom-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoFieldSelectors #-} -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+-- |+-- 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 (..),@@ -28,27 +34,30 @@ isBloomFull, acceptsFilters, bloomRelevantUpdate,-) where+ )+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 qualified Data.ByteString as BS+import Data.ByteString qualified as BS import Data.Bytes.Get import Data.Bytes.Put import Data.Bytes.Serial-import qualified Data.Foldable as F+import Data.Foldable qualified as F import Data.Hash.Murmur (murmur3) import Data.List (foldl')-import qualified Data.Sequence as S+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@@ -66,139 +75,137 @@ 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.--}+-- | 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)+ = -- | 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"+ 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+ serialize f = putWord8 $ case f of+ BloomUpdateNone -> 0+ BloomUpdateAll -> 1+ BloomUpdateP2PubKeyOnly -> 2 instance Binary BloomFlags where- get = deserialize- put = serialize+ get = deserialize+ put = serialize instance Serialize BloomFlags where- get = deserialize- put = serialize+ 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.--}+-- | 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- bloomData :: !(S.Seq Word8)- , -- | number of hash functions for this filter- bloomHashFuncs :: !Word32- , -- | hash function random nonce- bloomTweak :: !Word32- , -- | bloom filter auto-update flags- bloomFlags :: !BloomFlags- }- deriving (Eq, Show, Read, Generic, NFData)+ { -- | 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+ deserialize =+ BloomFilter+ <$> (S.fromList <$> (readDat =<< deserialize))+ <*> getWord32le+ <*> getWord32le+ <*> deserialize+ where+ readDat (VarInt len) = replicateM (fromIntegral len) getWord8 - serialize (BloomFilter dat hashFuncs tweak flags) = do- putVarInt $ S.length dat- forM_ (F.toList dat) putWord8- putWord32le hashFuncs- putWord32le tweak- serialize flags+ 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+ put = serialize+ get = deserialize instance Serialize BloomFilter where- put = serialize- get = deserialize+ put = serialize+ get = deserialize -- | Set a new bloom filter on the peer connection.-newtype FilterLoad = FilterLoad {filterLoadBloomFilter :: BloomFilter}- deriving (Eq, Show, Read, Generic, NFData)+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+ deserialize = FilterLoad <$> deserialize+ serialize (FilterLoad f) = serialize f instance Binary FilterLoad where- put = serialize- get = deserialize+ put = serialize+ get = deserialize instance Serialize FilterLoad where- put = serialize- get = deserialize+ 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 {getFilterData :: ByteString}- deriving (Eq, Show, Read, Generic, NFData)+-- | 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+ deserialize = do+ (VarInt len) <- deserialize+ dat <- getByteString $ fromIntegral len+ return $ FilterAdd dat - serialize (FilterAdd bs) = do- putVarInt $ BS.length bs- putByteString bs+ serialize (FilterAdd bs) = do+ putVarInt $ BS.length bs+ putByteString bs instance Binary FilterAdd where- put = serialize- get = deserialize+ put = serialize+ get = deserialize instance Serialize FilterAdd where- put = serialize- get = deserialize+ 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.--}+-- | 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+ -- | 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+ BloomFilter (S.replicate bloomSize 0) numHashF where -- Bloom filter size in bytes bloomSize = truncate $ min a b / 8@@ -211,117 +218,127 @@ 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)+bloomHash b hashNum bs =+ murmur3 seed bs `mod` (fromIntegral (S.length (b.array)) * 8) where- seed = hashNum * 0xfba4c795 + bloomTweak bfilter+ seed = hashNum * 0xfba4c795 + b.tweak -{- | Insert arbitrary data into a bloom filter. Returns the new bloom filter- containing the new data.--}+-- | 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 bfilter bs- | isBloomFull bfilter = bfilter- | otherwise = bfilter{bloomData = newData}+ -- | 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 bfilter i bs) [0 .. bloomHashFuncs bfilter - 1]+ 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- newData = foldl upd (bloomData bfilter) idxs+ 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.--}+-- | 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 bfilter bs- | isBloomFull bfilter = True- | isBloomEmpty bfilter = False- | otherwise = all isSet idxs+ -- | 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 = bloomData bfilter- idxs = map (\i -> bloomHash bfilter i bs) [0 .. bloomHashFuncs bfilter - 1]+ 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+ 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).--}+-- | 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 ::- -- | Bloom filter- BloomFilter ->- -- | Tx that may (or may not) have relevant outputs- Tx ->- -- | Returns an updated bloom filter adding relevant output- Maybe BloomFilter-bloomRelevantUpdate bfilter tx- | isBloomFull bfilter || isBloomEmpty bfilter = Nothing- | bloomFlags bfilter == BloomUpdateNone = Nothing- | not (null matchOuts) = Just $ foldl' addRelevant bfilter matchOuts- | otherwise = Nothing+ 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 (decodeOutputBS . scriptOutput) $ txOut tx+ 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) -> bloomContains bfilter $ encodeScriptOut op)+ filter (\(_, op) -> any (bloomContains b) (encodeScriptOut op)) matchOuts = matchFilter idxOutputScripts addRelevant :: BloomFilter -> (Word32, ScriptOutput) -> BloomFilter addRelevant bf (id', scriptOut) =- case (bloomFlags bfilter, scriptType) of- -- We filtered out BloomUpdateNone so we insert any PayPk or PayMulSig+ 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"+ (_, True) -> bloomInsert bf outpoint+ (BloomUpdateAll, _) -> bloomInsert bf outpoint+ _ -> error "Error Updating Bloom Filter with relevant outpoint" where- outpoint = runPutS $ serialize $ OutPoint{outPointHash = h, outPointIndex = id'}+ 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 (PayMulSig outputMuSig _) = runPutS $ serialize outputMuSig- encodeScriptOut (PayWitnessScriptHash scriptHash) = runPutS $ serialize scriptHash- encodeScriptOut (DataCarrier getOutputDat) = runPutS $ serialize getOutputDat- encodeScriptOut outputHash = (runPutS . serialize . getOutputHash) outputHash+ 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 bfilter = all (== 0x00) $ F.toList $ bloomData bfilter+isBloomEmpty b = all (== 0x00) $ F.toList b.array -- | Returns True if the filter is full (all bytes set to 0xff) isBloomFull :: BloomFilter -> Bool-isBloomFull bfilter = all (== 0xff) $ F.toList $ bloomData bfilter+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 bfilter =- S.length (bloomData bfilter) <= maxBloomSize- && bloomHashFuncs bfilter <= maxHashFuncs+ -- | 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
src/Haskoin/Network/Common.hs view
@@ -1,751 +1,754 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}--{- |-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 qualified Data.ByteString 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.- addrList :: [NetworkAddressTime]- }- deriving (Eq, Show, Generic, 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.- alertPayload :: !VarString- , -- | ECDSA signature of the payload- alertSignature :: !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- getDataList :: [InvVector]- }- deriving (Eq, Show, Generic, 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- invList :: [InvVector]- }- deriving (Eq, Show, Generic, 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, 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- naServices :: !Word64- , -- | address and port information- naAddress :: !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- notFoundList :: [InvVector]- }- deriving (Eq, Show, Generic, 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.- 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 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- 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 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 {getVarInt :: Word64}- deriving (Eq, Show, Read, Generic, 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 {getVarString :: ByteString}- deriving (Eq, Show, Read, Generic, 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- 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 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'+{-# 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
+ 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
@@ -1,30 +1,34 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}--{- |-Module : Haskoin.Network.Message-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-}+{-# LANGUAGE NoFieldSelectors #-} -Peer-to-peer network message serialization.--}-module Haskoin.Network.Message (- -- * Network Message+-- |+-- 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+ )+where import Control.DeepSeq import Control.Monad (unless) import Data.Binary (Binary (..)) import Data.ByteString (ByteString)-import qualified Data.ByteString as BS+import Data.ByteString qualified as B import Data.Bytes.Get import Data.Bytes.Put import Data.Bytes.Serial@@ -34,80 +38,78 @@ import Haskoin.Block.Common import Haskoin.Block.Merkle import Haskoin.Crypto.Hash-import Haskoin.Data 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 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)+ { -- | 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+ deserialize =+ MessageHeader+ <$> getWord32be+ <*> deserialize+ <*> getWord32le+ <*> deserialize - serialize (MessageHeader m c l chk) = do- putWord32be m- serialize c- putWord32le l- serialize chk+ serialize (MessageHeader m c l chk) = do+ putWord32be m+ serialize c+ putWord32le l+ serialize chk instance Binary MessageHeader where- put = serialize- get = deserialize+ put = serialize+ get = deserialize instance Serialize MessageHeader where- put = serialize- get = deserialize+ 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.--}+-- | 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)+ = 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@@ -136,85 +138,87 @@ msgType (MOther c _) = MCOther c -- | Deserializer for network messages.-getMessage :: MonadGet m => Network -> m Message+getMessage :: (MonadGet m) => Network -> m Message getMessage net = do- (MessageHeader mgc cmd len chk) <- deserialize- 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 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 BS.empty)+ (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- ++ " is expected to carry a payload"+ 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 :: (MonadPut m) => Network -> Message -> m () putMessage net msg = do- let (cmd, payload) =- case msg of- MVersion m -> (MCVersion, runPutS $ serialize m)- MVerAck -> (MCVerAck, BS.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, BS.empty)- MFilterLoad m -> (MCFilterLoad, runPutS $ serialize m)- MFilterAdd m -> (MCFilterAdd, runPutS $ serialize m)- MFilterClear -> (MCFilterClear, BS.empty)- MPing m -> (MCPing, runPutS $ serialize m)- MPong m -> (MCPong, runPutS $ serialize m)- MAlert m -> (MCAlert, runPutS $ serialize m)- MMempool -> (MCMempool, BS.empty)- MReject m -> (MCReject, runPutS $ serialize 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- serialize header- putByteString payload+ 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
@@ -1,20 +1,20 @@-{- |-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 : 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+ )+where import Haskoin.Script.Common as Common import Haskoin.Script.SigHash as SigHash
src/Haskoin/Script/Common.hs view
@@ -1,581 +1,598 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}--{- |-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.Binary (Binary (..))-import Data.ByteString (ByteString)-import qualified Data.ByteString as B-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)--{- | 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 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+{-# 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
@@ -1,27 +1,35 @@+{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-}--{- |-Module : Haskoin.Script.SigHash-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoFieldSelectors #-} -Transaction signatures and related functions.--}-module Haskoin.Script.SigHash (- -- * Script Signatures+-- |+-- 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,- hasAnyoneCanPayFlag,+ anyoneCanPay, hasForkIdFlag,- setAnyoneCanPayFlag,+ setAnyoneCanPay, setForkIdFlag, isSigHashAll, isSigHashNone,@@ -33,15 +41,19 @@ txSigHash, txSigHashForkId, TxSignature (..),- encodeTxSig, decodeTxSig,-) where+ encodeTxSig,+ )+where import Control.DeepSeq import Control.Monad-import qualified Data.Aeson as J+import Crypto.Secp256k1+import Data.Aeson import Data.Bits-import qualified Data.ByteString as BS+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@@ -50,82 +62,69 @@ import Data.Scientific import Data.Word import GHC.Generics (Generic)-import Haskoin.Crypto import Haskoin.Crypto.Hash-import Haskoin.Data+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)+ = -- | 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.+ 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" - 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).--}+-- | 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- )+ = SigHash Word32+ deriving (Eq, Ord, Enum, Show, Read, Generic)+ deriving newtype (Bits, Integral, Num, Real, Hashable, NFData) -instance J.FromJSON SigHash where- parseJSON =- J.withScientific "sighash" $- maybe mzero (return . SigHash) . toBoundedInteger+instance FromJSON SigHash where+ parseJSON =+ withScientific "sighash" $+ maybe mzero (return . SigHash) . toBoundedInteger -instance J.ToJSON SigHash where- toJSON = J.Number . fromIntegral- toEncoding (SigHash n) = J.toEncoding n+instance ToJSON SigHash where+ toJSON = Number . fromIntegral+ toEncoding (SigHash n) = toEncoding n -- | SIGHASH_NONE as a byte. sigHashNone :: SigHash@@ -152,16 +151,16 @@ setForkIdFlag = (.|. sigHashForkId) -- | Set SIGHASH_ANYONECANPAY flag.-setAnyoneCanPayFlag :: SigHash -> SigHash-setAnyoneCanPayFlag = (.|. sigHashAnyoneCanPay)+setAnyoneCanPay :: SigHash -> SigHash+setAnyoneCanPay = (.|. 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)+anyoneCanPay :: SigHash -> Bool+anyoneCanPay = (/= 0) . (.&. sigHashAnyoneCanPay) -- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_ALL'. isSigHashAll :: SigHash -> Bool@@ -178,7 +177,7 @@ -- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_UNKNOWN'. isSigHashUnknown :: SigHash -> Bool isSigHashUnknown =- (`notElem` [sigHashAll, sigHashNone, sigHashSingle]) . (.&. 0x1f)+ (`notElem` [sigHashAll, sigHashNone, sigHashSingle]) . (.&. 0x1f) -- | Add a fork id to a 'SigHash'. sigHashAddForkId :: SigHash -> Word32 -> SigHash@@ -187,7 +186,7 @@ -- | Add fork id of a particular network to a 'SigHash'. sigHashAddNetworkId :: Network -> SigHash -> SigHash sigHashAddNetworkId net =- (`sigHashAddForkId` fromMaybe 0 (getSigHashForkId net))+ (`sigHashAddForkId` fromMaybe 0 net.sigHashForkId) -- | Get fork id from 'SigHash'. sigHashGetForkId :: SigHash -> Word32@@ -195,146 +194,157 @@ -- | 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+ 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 (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 $- runPutS $ do- serialize newTx- putWord32le $ fromIntegral 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) $ scriptOps out+ 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- | hasAnyoneCanPayFlag sh =- [(txins !! i){scriptInput = runPutS $ serialize out}]- | isSigHashAll sh || isSigHashUnknown sh = single- | otherwise = zipWith noSeq single [0 ..]+ | anyoneCanPay sh = [serialOut (txins !! i)]+ | 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 = runPutS $ serialize out}- noSeq ti j =- if i == j- then ti- else ti{txInSequence = 0}+ 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]+ | 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+ 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.--}+-- | 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+ 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 $ txVersion tx- serialize hashPrevouts- serialize hashSequence- serialize $ prevOutput $ txIn tx !! i- putScript out- putWord64le v- putWord32le $ txInSequence $ txIn tx !! i- serialize hashOutputs- putWord32le $ txLockTime tx- putWord32le $ fromIntegral $ sigHashAddNetworkId net 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 $ hasAnyoneCanPayFlag sh =- doubleSHA256 $ runPutS $ mapM_ (serialize . prevOutput) $ txIn tx- | otherwise = zeros+ | not (anyoneCanPay sh) =+ doubleSHA256 . runPutS $ mapM_ (serialize . (.outpoint)) tx.inputs+ | otherwise = zeros hashSequence- | not (hasAnyoneCanPayFlag sh)- && not (isSigHashSingle sh)- && not (isSigHashNone sh) =- doubleSHA256 $ runPutS $ mapM_ (putWord32le . txInSequence) $ txIn tx- | otherwise = zeros+ | not (anyoneCanPay sh || isSigHashSingle sh || isSigHashNone sh) =+ doubleSHA256 . runPutS $ mapM_ (putWord32le . (.sequence)) tx.inputs+ | otherwise = zeros hashOutputs- | not (isSigHashSingle sh) && not (isSigHashNone sh) =- doubleSHA256 $ runPutS $ mapM_ serialize $ txOut tx- | isSigHashSingle sh && i < length (txOut tx) =- doubleSHA256 $ runPutS $ serialize $ txOut tx !! i- | otherwise = zeros+ | 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 $ BS.length encodedScript- putByteString encodedScript+ 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 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)+ = TxSignature+ { sig :: !Sig,+ hash :: !SigHash+ }+ | TxSignatureEmpty+ deriving (Eq, Show, Read, Generic, NFData) -instance NFData TxSignature+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) --- | Serialize a 'TxSignature'.-encodeTxSig :: TxSignature -> BS.ByteString-encodeTxSig TxSignatureEmpty = error "Can not encode an empty signature"-encodeTxSig (TxSignature sig (SigHash n)) =- runPutS $ putSig 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 --- | Deserialize a 'TxSignature'.-decodeTxSig :: Network -> BS.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"+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
@@ -1,20 +1,26 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-}--{- |-Module : Haskoin.Script.Standard-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE NoFieldSelectors #-} -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+-- |+-- 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,@@ -26,9 +32,7 @@ isPayWitnessScriptHash, isDataCarrier, encodeOutput,- encodeOutputBS, decodeOutput,- decodeOutputBS, toP2SH, toP2WSH, sortMulSig,@@ -37,22 +41,23 @@ ScriptInput (..), SimpleInput (..), encodeInput,- encodeInputBS, decodeInput,- decodeInputBS, isSpendPK, isSpendPKHash, isSpendMulSig, isScriptHashInput,-) where+ )+where import Control.Applicative ((<|>)) import Control.DeepSeq import Control.Monad (guard, liftM2, (<=<))-import qualified Data.Aeson as A-import qualified Data.Aeson.Encoding as A+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 qualified Data.ByteString as BS+import Data.ByteString qualified as B import Data.Bytes.Get import Data.Bytes.Put import Data.Bytes.Serial@@ -62,53 +67,52 @@ import Data.Maybe (fromJust, isJust) import Data.Word (Word8) import GHC.Generics (Generic)-import Haskoin.Crypto-import Haskoin.Data-import Haskoin.Keys.Common+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 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}- | -- | another pay to witness address- PayWitness- { getWitnessVersion :: !Word8- , getWitnessData :: !ByteString- }- | -- | provably unspendable data carrier- DataCarrier {getOutputData :: !ByteString}- deriving (Eq, Show, Read, Generic, Hashable, NFData)+ = -- | 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 A.FromJSON ScriptOutput where- parseJSON =- A.withText "scriptoutput" $ \t ->- either fail return $- maybeToEither "scriptoutput not hex" (decodeHex t)- >>= decodeOutputBS+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 -instance A.ToJSON ScriptOutput where- toJSON = A.String . encodeHex . encodeOutputBS- toEncoding = A.text . encodeHex . encodeOutputBS+ 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@@ -149,35 +153,41 @@ 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 <$> runGetS deserialize 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]- | BS.length bs == 20 -> PayWitnessPKHash <$> runGetS deserialize bs- | BS.length bs == 32 -> PayWitnessScriptHash <$> runGetS deserialize bs- | BS.length bs /= 20 && BS.length bs /= 32 ->- Left "Version 0 segwit program must be 20 or 32 bytes long"- -- Other Witness- [ver, OP_PUSHDATA bs _]- | isJust (opWitnessVersion ver)- && BS.length bs >= 2- && BS.length bs <= 40 ->- Right $ PayWitness (fromJust (opWitnessVersion ver)) bs- -- Provably unspendable data carrier output- [OP_RETURN, OP_PUSHDATA bs _] -> Right $ DataCarrier bs- -- Pay to MultiSig Keys- _ -> matchPayMulSig s+-- | 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@@ -219,51 +229,51 @@ opWitnessVersion OP_16 = Just 16 opWitnessVersion _ = Nothing --- | Similar to 'decodeOutput' but decodes from a 'ByteString'.-decodeOutputBS :: ByteString -> Either String ScriptOutput-decodeOutputBS = decodeOutput <=< runGetS deserialize- -- | Computes a 'Script' from a standard 'ScriptOutput'.-encodeOutput :: ScriptOutput -> Script-encodeOutput s = Script $ case s of- -- Pay to PubKey- (PayPK k) -> [opPushData $ runPutS $ serialize 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 . runPutS . serialize) 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]+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] --- | Similar to 'encodeOutput' but encodes to a ByteString-encodeOutputBS :: ScriptOutput -> ByteString-encodeOutputBS = runPutS . serialize . encodeOutput+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@@ -274,59 +284,61 @@ toP2WSH = PayWitnessScriptHash . sha256 . runPutS . serialize -- | 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"+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 (:) (runGetS deserialize bs) (go xs)- go [] = return []- go _ = Left "matchPayMulSig: invalid multisig opcode"+ 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 :: ScriptOutput -> ScriptOutput-sortMulSig out = case out of- PayMulSig keys r -> PayMulSig (sortBy (compare `on` (runPutS . serialize)) keys) r- _ -> error "Can only call orderMulSig on PayMulSig scripts"+-- | 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 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- getInputSig :: !TxSignature- }- | SpendPKHash- { -- | embedded signature- getInputSig :: !TxSignature- , -- | public key- getInputKey :: !PubKeyI- }- | SpendMulSig- { -- | list of signatures- getInputMulSigKeys :: ![TxSignature]- }- deriving (Eq, Show, Generic, NFData)+ = 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.--}+-- | 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.--}+-- | 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@@ -341,91 +353,83 @@ 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.--}+-- | 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- getRegularInput :: !SimpleInput- }- | ScriptHashInput- { -- | get simple input associated with redeem script- getScriptHashInput :: !SimpleInput- , -- | redeem script- getScriptHashRedeem :: !RedeemScript- }- deriving (Eq, Show, Generic, NFData)+ = 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 -> Script -> Either String SimpleInput-decodeSimpleInput net (Script ops) =- maybeToEither errMsg $ matchPK ops <|> matchPKHash ops <|> matchMulSig ops+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 (runGetS deserialize pub)+ SpendPKHash <$> f op <*> eitherToMaybe (unmarshal ctx pub) matchPKHash _ = Nothing matchMulSig (x : xs) = do- guard $ x == OP_0- SpendMulSig <$> mapM f xs+ 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 (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 -> Script -> Either String ScriptInput-decodeInput net s@(Script ops) =- maybeToEither errMsg $ matchSimpleInput <|> matchPayScriptHash+-- | 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 s)+ RegularInput <$> eitherToMaybe (decodeSimpleInput net ctx 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+ 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" -{- | Like 'decodeInput' but decodes directly from a serialized script- 'ByteString'.--}-decodeInputBS :: Network -> ByteString -> Either String ScriptInput-decodeInputBS net = decodeInput net <=< runGetS deserialize+instance Marshal (Network, Ctx) ScriptInput where+ marshalGet (net, ctx) =+ deserialize >>= either fail return . decodeInput net ctx --- | 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]+ marshalPut (net, ctx) =+ serialize . encodeInput net ctx -{- | Similar to 'encodeInput' but encodes directly to a serialized script- 'ByteString'.--}-encodeInputBS :: ScriptInput -> ByteString-encodeInputBS = runPutS . serialize . encodeInput+-- | 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 :: SimpleInput -> Script-encodeSimpleInput s =- Script $- case s of- SpendPK ts -> [f ts]- SpendPKHash ts p -> [f ts, opPushData $ runPutS $ serialize p]- SpendMulSig xs -> OP_0 : map f xs+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 ts+ f ts = opPushData $ encodeTxSig net ctx ts
src/Haskoin/Transaction.hs view
@@ -1,21 +1,23 @@-{- |-Module : Haskoin.Transaction-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE DuplicateRecordFields #-} -Transactions and related code.--}-module Haskoin.Transaction (- module Common,+-- |+-- 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+ )+where import Haskoin.Transaction.Builder as Builder import Haskoin.Transaction.Common as Common
src/Haskoin/Transaction/Builder.hs view
@@ -1,20 +1,24 @@+{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-}--{- |-Module : Haskoin.Transaction.Builder-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoFieldSelectors #-} -Code to simplify transaction creation, signing, fee calculation and coin-selection.--}-module Haskoin.Transaction.Builder (- -- * Transaction Builder+-- |+-- 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,@@ -43,26 +47,21 @@ guessMSTxFee, guessTxSize, guessMSSize,-) where+ )+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.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 (ConduitT, Void, await, runConduit, (.|)) import Data.Conduit.List (sourceList)-import Data.Either (fromRight)+import Data.Either (fromRight, rights) import Data.List (nub) import Data.Maybe (catMaybes, fromJust, isJust) import Data.String.Conversions (cs)@@ -70,508 +69,556 @@ import Data.Word (Word64) import Haskoin.Address import Haskoin.Crypto.Hash (Hash256, addressHash)+import Haskoin.Crypto.Keys.Common import Haskoin.Crypto.Signature-import Haskoin.Data-import Haskoin.Keys.Common import Haskoin.Network.Common+import Haskoin.Network.Data import Haskoin.Script-import Haskoin.Transaction.Builder.Sign (- SigInput (..),- buildInput,- makeSignature,- sigKeys,- )-import qualified Haskoin.Transaction.Builder.Sign as S+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 (- decodeWitnessInput,- isSegwit,- viewWitnessProgram,- )+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.--}+-- | 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+ 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.--}+-- | 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)+ (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+ 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.--}+-- | 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))+ (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"+ | 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.--}+-- | 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)+ (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+ 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.--}+-- | 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))+ (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"+ | 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.--}+-- | 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))+ (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+ 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+ 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 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+ 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))+ 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)+ 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)+ 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.--}+-- | 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+ -- | 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+ 8 + inpLen + inp + outLen + out where inpLen =- B.length- . runPutS- . serialize- . VarInt- . fromIntegral- $ length msi + pki+ B.length+ . runPutS+ . serialize+ . VarInt+ . fromIntegral+ $ length msi + pki outLen =- B.length- . runPutS- . serialize- . VarInt- . fromIntegral- $ pkout + msout+ 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) +- -- (1: script len) + (8: Word64)- msout * 32+ 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+ -- 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+ 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 -> [OutPoint] -> [(Text, Word64)] -> Either String Tx-buildAddrTx net ops rcps =- buildTx ops <$> mapM f rcps+-- | 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)+ 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 :: [OutPoint] -> [(ScriptOutput, Word64)] -> Tx-buildTx ops rcpts =- Tx 1 (toIn <$> ops) (toOut <$> rcpts) [] 0+-- | 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 $ encodeOutputBS 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+ toOut (o, v) = TxOut v $ marshal ctx o - > sigIn = SigInput (PayWitnessScriptHash h) 100000 op sigHashAll (Just $ PayMulSig [p1,p2,p3] 2)- > signedTx = signTx btc unsignedTx [sigIn] [k1,k3]--}+-- | 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 ->- -- | transaction to sign- Tx ->- -- | signing parameters- [SigInput] ->- -- | private keys to sign with- [SecKey] ->- -- | signed transaction- Either String Tx-signTx net tx si = S.signTx net tx $ notNested <$> si+ 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'.--}+-- | 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 ->- -- | transaction to sign- Tx ->- -- | signing parameters- [SigInput] ->- -- | private keys to sign with- [SecKey] ->- -- | signed transaction- Either String Tx-signNestedWitnessTx net tx si = S.signTx net tx $ nested <$> si+ 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 -> Tx -> Int -> SigInput -> SecKeyI -> Either String Tx-signInput net tx i si = S.signInput net tx i (si, False)+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 -> Tx -> Int -> SigInput -> SecKeyI -> Either String Tx-signNestedInput net tx i si = S.signInput net tx i (si, True)+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.--}+-- | 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+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.--}+-- | 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+ 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 (txIn $ head txs) f) [0 ..]+ 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 == prevOutput txin+ 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 (\ti -> ti{scriptInput = B.empty})+ ins is i = updateIndex i is (\TxIn {..} -> TxIn {script = B.empty, ..}) clearInput tx (_, i) =- Tx (txVersion tx) (ins (txIn tx) i) (txOut tx) [] (txLockTime tx)+ 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.--}+-- | 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) = do- -- Ignore transactions with empty inputs- let ins = map (scriptInput . (!! i) . txIn) 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 <- 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)+ 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 (getRegularInput si) rdm- _ -> Left "Invalid output script type"+ 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 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"+ 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- (txSigHash net tx (encodeOutput out) val i sh)- x- (pubKeyPoint 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 -> Tx -> [(ScriptOutput, Word64, OutPoint)] -> Bool-verifyStdTx net tx xs =- not (null (txIn tx)) && all go (zip (matchTemplate xs (txIn tx) f) [0 ..])+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 == prevOutput txin- go (Just (so, val, _), i) = verifyStdInput net tx i so val+ 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 -> 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)+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 = scriptInput $ txIn tx !! i- theTxSigHash so = S.makeSigHash net tx i so val+ inp = (tx.inputs !! i).script+ theTxSigHash so = Sign.makeSigHash net ctx tx i so val ws :: WitnessStack ws- | length (txWitness tx) > i = txWitness tx !! i- | otherwise = []+ | length tx.witness > i = tx.witness !! i+ | otherwise = [] wp :: ScriptOutput -> Either String (Maybe ScriptOutput, SimpleInput)- wp so = decodeWitnessInput net =<< viewWitnessProgram net so ws+ wp so = decodeWitnessInput net ctx =<< viewWitnessProgram net ctx so ws nestedScriptOutput :: Either String ScriptOutput nestedScriptOutput =- scriptOps <$> runGetS deserialize inp >>= \case- [OP_PUSHDATA bs _] -> decodeOutputBS bs- _ -> Left "nestedScriptOutput: not a nested output"+ 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 (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+ (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 so+ out = encodeOutput ctx so verifySegwitInput ::- ScriptOutput -> (Maybe ScriptOutput, SimpleInput) -> Bool+ ScriptOutput -> (Maybe ScriptOutput, SimpleInput) -> Bool 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 (runPutS (serialize 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+ ( 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+ ScriptOutput -> ScriptOutput -> (Maybe RedeemScript, SimpleInput) -> Bool verifyNestedInput so so' x = case so of- PayScriptHash h -> payToScriptAddress so' == p2shAddr h && verifySegwitInput so' x- _ -> False+ PayScriptHash h -> payToScriptAddress ctx so' == p2shAddr h && verifySegwitInput so' x+ _ -> False -- | 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+ 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' :: (SigHash -> Hash256) -> [PubKey] -> [TxSignature] -> Int-countMulSig' _ [] _ = 0-countMulSig' _ _ [] = 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+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
@@ -1,20 +1,25 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-}--{- |-Module : Haskoin.Transaction.Builder.Sign-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoFieldSelectors #-} -Types and logic for signing transactions.--}-module Haskoin.Transaction.Builder.Sign (- SigInput (..),+-- |+-- 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,@@ -22,298 +27,296 @@ signInput, buildInput, sigKeys,-) where+ )+where -import Control.DeepSeq (NFData)-import Control.Monad (foldM, when)-import Data.Aeson (- FromJSON,- ToJSON (..),- object,- pairs,- parseJSON,- withObject,- (.:),- (.:?),- (.=),- )+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 (rights)-import Data.Hashable (Hashable)-import Data.List (find, nub)-import Data.Maybe (- catMaybes,- fromMaybe,- mapMaybe,- maybeToList,- )-import Data.Word (Word64)-import GHC.Generics (Generic)-import Haskoin.Address (getAddrHash160, pubKeyAddr)-import Haskoin.Crypto (Hash256, SecKey)-import Haskoin.Crypto.Signature (signHash, verifyHashSig)-import Haskoin.Data (Network)-import Haskoin.Keys.Common (- PubKeyI (..),- SecKeyI (..),- derivePubKeyI,- wrapSecKey,- )+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 (matchTemplate, updateIndex)+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 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- sigInputScript :: !ScriptOutput- , -- | output script value- -- ^ outpoint to spend- sigInputValue :: !Word64- , -- | outpoint to spend- -- ^ signature type- sigInputOP :: !OutPoint- , -- | signature type- -- ^ redeem script- sigInputSH :: !SigHash- , -- | redeem script- sigInputRedeem :: !(Maybe RedeemScript)- }- deriving (Eq, Show, Read, Generic, Hashable, NFData)+ { -- | 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 ToJSON SigInput where- toJSON (SigInput so val op sh rdm) =- object $- [ "pkscript" .= so- , "value" .= val- , "outpoint" .= op- , "sighash" .= sh- ]- ++ ["redeem" .= r | r <- maybeToList rdm]- toEncoding (SigInput so val op sh rdm) =- pairs $- "pkscript" .= so- <> "value" .= val- <> "outpoint" .= op- <> "sighash" .= sh- <> maybe mempty ("redeem" .=) rdm+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+ ] -instance FromJSON SigInput where- parseJSON =- withObject "SigInput" $ \o ->- SigInput <$> o .: "pkscript"- <*> o .: "value"- <*> o .: "outpoint"- <*> o .: "sighash"- <*> o .:? "redeem"+ 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+ ] -{- | 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.--}+ 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 ->- -- | transaction to sign- Tx ->- -- | signing parameters, with nesting flag- [(SigInput, Bool)] ->- -- | private keys to sign with- [SecKey] ->- -- | signed transaction- Either String Tx-signTx net otx sigis allKeys- | null ti = Left "signTx: Transaction has no inputs"- | otherwise = foldM go otx $ findInputIndex (sigInputOP . fst) sigis ti+ 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 = txIn otx+ ti = otx.inputs go tx (sigi@(SigInput so _ _ _ rdmM, _), i) = do- keys <- sigKeys so rdmM allKeys- foldM (\t k -> signInput net t i sigi k) tx keys+ 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.--}+-- | Sign a single input in a transaction deterministically (RFC-6979). The+-- nesting flag only affects the behavior of segwit inputs. signInput ::- Network ->- Tx ->- Int ->- -- | boolean flag: nest input- (SigInput, Bool) ->- SecKeyI ->- Either String Tx-signInput net tx i (sigIn@(SigInput so val _ _ 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- }+ 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 x = x{scriptInput = encodeInputBS si}- g so' x = x{scriptInput = runPutS . serialize . opPushData $ encodeOutputBS so'}- txis = txIn tx+ 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 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.+ | isSegwit so' && nest = updateIndex i tx.inputs (g so')+ | isSegwit so' = tx.inputs+ | otherwise = updateIndex i tx.inputs (f si) - @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+-- | 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 $ 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+ | 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)]+ -- | 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 ..]+ mapMaybe g $ zip (matchTemplate as ti f) [0 ..] where- f s txin = getOutPoint s == prevOutput txin+ 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'.--}+-- | 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"+ 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 = derivePubKeyI prv- ]+ [ (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) . getAddrHash160 . pubKeyAddr . snd+ 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.--}+-- | Construct an input for a transaction given a signature, public key and data+-- about the previous output. buildInput ::- Network ->- -- | 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 ->- 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"+ 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 tx so i- return $ RegularInput $ SpendMulSig mSigs- _ -> Left "buildInput: Invalid output/redeem script combination"+ 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 (getRegularInput inp) rdm+ inp <- buildRegularInput rdm+ return $ ScriptHashInput inp.get rdm f (TxSignature x sh) p =- verifyHashSig (makeSigHash net tx i so val sh rdmM) x (pubKeyPoint 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 -> Tx -> ScriptOutput -> Int -> [TxSignature]-parseExistingSigs net tx so i = insSigs <> witSigs+-- | 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 decodeInputBS net scp of- Right (ScriptHashInput (SpendMulSig xs) _) -> xs- Right (RegularInput (SpendMulSig xs)) -> xs- _ -> []- scp = scriptInput $ txIn tx !! i+ 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 $ txWitness tx = []- | otherwise = rights $ decodeTxSig net <$> (txWitness tx !! i)+ | 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 -> Tx -> Int -> SigInput -> SecKeyI -> TxSignature-makeSignature net tx i (SigInput so val _ sh rdmM) key =- TxSignature (signHash (secKeyData key) m) sh+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 tx i so val sh rdmM--{- | A function which selects the digest algorithm and parameters as appropriate+ m = makeSigHash net ctx tx i so val sh rdmM - @since 0.11.0.0--}+-- | 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+ 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+ PayWitnessPKHash h' -> PayPKHash h'+ _ -> fromMaybe so rdmM h- | isSegwit so = txSigHashForkId- | otherwise = txSigHash+ | isSegwit so = txSigHashForkId+ | otherwise = txSigHash
src/Haskoin/Transaction/Common.hs view
@@ -1,19 +1,25 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-}--{- |-Module : Haskoin.Transaction.Common-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoFieldSelectors #-} -Code related to transactions parsing and serialization.--}-module Haskoin.Transaction.Common (- -- * Transactions+-- |+-- 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 (..),@@ -27,27 +33,20 @@ txHashToHex, nosigTxHash, nullOutPoint,-) where+ )+where import Control.Applicative ((<|>)) import Control.DeepSeq-import Control.Monad (- forM_,- guard,- liftM2,- mzero,- replicateM,- unless,- when,- (<=<),- )+import Control.Monad (forM_, guard, liftM2, mzero, replicateM, unless, when, (<=<)) import Data.Aeson as A-import Data.Aeson.Encoding (unsafeToEncoding)+import Data.Aeson.Encoding qualified as E import Data.Binary (Binary (..))+import Data.Bool (bool) import Data.ByteString (ByteString)-import qualified Data.ByteString as B+import Data.ByteString qualified as B import Data.ByteString.Builder (char7)-import qualified Data.ByteString.Lazy as BL+import Data.ByteString.Lazy qualified as BL import Data.Bytes.Get import Data.Bytes.Put import Data.Bytes.Serial@@ -61,67 +60,67 @@ import GHC.Generics (Generic) import Haskoin.Crypto.Hash import Haskoin.Network.Common-import Haskoin.Util+import Haskoin.Util.Helpers import Text.Read as R -- | Transaction id: hash of transaction excluding witness data.-newtype TxHash = TxHash {getTxHash :: Hash256}- deriving (Eq, Ord, Generic, Hashable, Serial, NFData)+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+ put = serialize+ get = deserialize instance Binary TxHash where- put = serialize- get = deserialize+ put = serialize+ get = deserialize instance Show TxHash where- showsPrec _ = shows . txHashToHex+ showsPrec _ = shows . txHashToHex instance Read TxHash where- readPrec = do- R.String str <- R.lexP- maybe R.pfail return $ hexToTxHash $ cs str+ 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+ 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+ parseJSON =+ withText "txid" $+ maybe mzero return . hexToTxHash instance ToJSON TxHash where- toJSON = A.String . txHashToHex- toEncoding h =- unsafeToEncoding $- char7 '"'- <> hexBuilder (BL.reverse (runPutL (serialize h)))- <> char7 '"'+ toJSON = A.String . txHashToHex+ toEncoding = hexEncoding . BL.reverse . runPutL . serialize -- | Transaction hash excluding signatures. nosigTxHash :: Tx -> TxHash-nosigTxHash tx =- TxHash $- doubleSHA256 $- runPutS $- serialize tx{txIn = map clearInput $ txIn tx}+nosigTxHash Tx {..} =+ TxHash . doubleSHA256 . runPutS $ serialize tx where- clearInput ti = ti{scriptInput = B.empty}+ 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)))+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+ bs <- B.reverse <$> decodeHex hex+ h <- either (const Nothing) Just (runGetS deserialize bs)+ return $ TxHash h -- | Witness stack for SegWit transactions. type WitnessData = [WitnessStack]@@ -134,289 +133,297 @@ -- | 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)+ { -- | 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{txWitness = []}+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"+ fromString =+ fromMaybe e . (eitherToMaybe . runGetS deserialize <=< decodeHex) . cs+ where+ e = error "Could not read transaction from hex string" instance Serial Tx where- deserialize =- isWitnessTx >>= \w -> if w then parseWitnessTx else parseLegacyTx- serialize tx- | null (txWitness tx) = putLegacyTx tx- | otherwise = putWitnessTx tx+ deserialize = isWitnessTx >>= bool parseLegacyTx parseWitnessTx+ serialize tx+ | null tx.witness = putLegacyTx tx+ | otherwise = putWitnessTx tx instance Binary Tx where- put = serialize- get = deserialize+ put = serialize+ get = deserialize instance Serialize Tx where- put = serialize- get = deserialize+ put = serialize+ get = deserialize -putInOut :: MonadPut m => Tx -> m ()+putInOut :: (MonadPut m) => Tx -> m () putInOut tx = do- putVarInt $ length (txIn tx)- forM_ (txIn tx) serialize- putVarInt $ length (txOut tx)- forM_ (txOut tx) serialize+ 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 :: (MonadPut m) => Tx -> m () putLegacyTx tx = do- putWord32le (txVersion tx)- putInOut tx- putWord32le (txLockTime tx)+ putWord32le tx.version+ putInOut tx+ putWord32le tx.locktime -- | Witness transaciton serializer.-putWitnessTx :: MonadPut m => Tx -> m ()+putWitnessTx :: (MonadPut m) => Tx -> m () putWitnessTx tx = do- putWord32le (txVersion tx)- putWord8 0x00- putWord8 0x01- putInOut tx- putWitnessData (txWitness tx)- putWord32le (txLockTime tx)+ putWord32le tx.version+ putWord8 0x00+ putWord8 0x01+ putInOut tx+ putWitnessData tx.witness+ putWord32le tx.locktime -isWitnessTx :: MonadGet m => m Bool+isWitnessTx :: (MonadGet m) => m Bool isWitnessTx = lookAhead $ do- _ <- getWord32le- m <- getWord8- f <- getWord8- return (m == 0x00 && f == 0x01)+ _ <- getWord32le+ m <- getWord8+ f <- getWord8+ return (m == 0x00 && f == 0x01) -- | Non-SegWit transaction deseralizer.-parseLegacyTx :: MonadGet m => m Tx+parseLegacyTx :: (MonadGet m) => m Tx parseLegacyTx = do- v <- getWord32le- is <- replicateList =<< deserialize- os <- replicateList =<< deserialize- when (length is == 0x00 && length os == 0x01) $ fail "Witness transaction"- l <- getWord32le- return- Tx- { txVersion = v- , txIn = is- , txOut = os- , txWitness = []- , txLockTime = l- }+ version <- getWord32le+ inputs <- rl =<< deserialize+ outputs <- rl =<< deserialize+ when (length inputs == 0x00 && length outputs == 0x01) $+ fail "Witness transaction"+ locktime <- getWord32le+ return Tx {witness = [], ..} where- replicateList (VarInt c) = replicateM (fromIntegral c) deserialize+ rl (VarInt c) = replicateM (fromIntegral c) deserialize -- | Witness transaction deserializer.-parseWitnessTx :: MonadGet m => m Tx+parseWitnessTx :: (MonadGet m) => m Tx parseWitnessTx = do- v <- getWord32le- m <- getWord8- f <- getWord8- unless (m == 0x00 && f == 0x01) $ fail "Not a witness transaction"- is <- replicateList =<< deserialize- os <- replicateList =<< deserialize- w <- parseWitnessData $ length is- l <- getWord32le- return- Tx{txVersion = v, txIn = is, txOut = os, txWitness = w, txLockTime = l}+ 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 :: (MonadGet m) => Int -> m WitnessData parseWitnessData n = replicateM n parseWitnessStack where parseWitnessStack = do- VarInt i <- deserialize- replicateM (fromIntegral i) parseWitnessStackItem+ VarInt i <- deserialize+ replicateM (fromIntegral i) parseWitnessStackItem parseWitnessStackItem = do- VarInt i <- deserialize- getByteString $ fromIntegral i+ VarInt i <- deserialize+ getByteString $ fromIntegral i -- | Witness data serializer.-putWitnessData :: MonadPut m => WitnessData -> m ()+putWitnessData :: (MonadPut m) => WitnessData -> m () putWitnessData = mapM_ putWitnessStack where putWitnessStack ws = do- putVarInt $ length ws- mapM_ putWitnessStackItem ws+ putVarInt $ length ws+ mapM_ putWitnessStackItem ws putWitnessStackItem bs = do- putVarInt $ B.length bs- putByteString bs+ 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+ 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- ( "version" .= v- <> "inputs" .= i- <> "outputs" .= o- <> "witnessdata" .= fmap (fmap encodeHex) w- <> "locktime" .= l- )+ 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- 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)+ { -- | 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+ 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+ serialize (TxIn o s q) = do+ serialize o+ putVarInt $ B.length s+ putByteString s+ putWord32le q instance Binary TxIn where- get = deserialize- put = serialize+ get = deserialize+ put = serialize instance Serialize TxIn where- get = deserialize- put = serialize+ get = deserialize+ put = serialize instance FromJSON TxIn where- parseJSON =- withObject "TxIn" $ \o ->- TxIn <$> o .: "prevoutput"- <*> (maybe mzero return . decodeHex =<< o .: "inputscript")- <*> o .: "sequence"+ 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- ( "prevoutput" .= o- <> "inputscript" .= encodeHex s- <> "sequence" .= q- )+ 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- outValue :: !Word64- , -- | pubkey script- scriptOutput :: !ByteString- }- deriving (Eq, Show, Read, Ord, Generic, Hashable, NFData)+ { -- | 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)+ 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+ serialize (TxOut o s) = do+ putWord64le o+ putVarInt $ B.length s+ putByteString s instance Binary TxOut where- put = serialize- get = deserialize+ put = serialize+ get = deserialize instance Serialize TxOut where- put = serialize- get = deserialize+ put = serialize+ get = deserialize instance FromJSON TxOut where- parseJSON =- withObject "TxOut" $ \o ->- TxOut <$> o .: "value"- <*> (maybe mzero return . decodeHex =<< o .: "outputscript")+ 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 ("value" .= o <> "outputscript" .= encodeHex s)+ 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- outPointHash :: !TxHash- , -- | position of output in previous transaction- outPointIndex :: !Word32- }- deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)+ { -- | 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+ 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+ put = serialize+ get = deserialize instance Serialize OutPoint where- put = serialize- get = deserialize+ put = serialize+ get = deserialize instance FromJSON OutPoint where- parseJSON =- withObject "OutPoint" $ \o ->- OutPoint <$> o .: "txid" <*> o .: "index"+ 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 ("txid" .= h <> "index" .= i)+ 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- { outPointHash =- "0000000000000000000000000000000000000000000000000000000000000000"- , outPointIndex = maxBound- }+ OutPoint+ { hash = "0000000000000000000000000000000000000000000000000000000000000000",+ index = maxBound+ }
src/Haskoin/Transaction/Genesis.hs view
@@ -1,42 +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+-- |+-- 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 Data.String (fromString)+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 :: Tx-genesisTx =- Tx 1 [txin] [txout] [] locktime+genesisTx :: Ctx -> Tx+genesisTx ctx =+ Tx 1 [txin] [txout] [] locktime where txin = TxIn outpoint inputBS maxBound- txout = TxOut 5000000000 (encodeOutputBS output)+ txout = TxOut 5000000000 (marshal ctx output) locktime = 0 outpoint = OutPoint z maxBound Just inputBS =- decodeHex $- fromString $- "04ffff001d0104455468652054696d65732030332f4a616e2f323030392043686"- ++ "16e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f"- ++ "757420666f722062616e6b73"- output =- PayPK $- fromString $- "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb"- ++ "649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"+ 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
@@ -1,925 +1,949 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}--{- |-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- PartiallySignedTransaction (..),- Input (..),- Output (..),- UnknownMap (..),- Key (..),- merge,- mergeMany,- mergeInput,- mergeOutput,- complete,- finalTransaction,- emptyPSBT,- emptyInput,- emptyOutput,-- -- ** Signing- PsbtSigner,- getSignerKey,- secKeySigner,- xPrvSigner,- signPSBT,-) where--import Control.Applicative ((<|>))-import Control.DeepSeq-import Control.Monad (foldM, guard, replicateM, void)-import Data.ByteString (ByteString)-import qualified Data.ByteString as B-import Data.Bytes.Get (runGetS)-import Data.Bytes.Put (runPutS)-import Data.Bytes.Serial (Serial (..))-import Data.Either (fromRight)-import Data.HashMap.Strict (HashMap)-import qualified Data.HashMap.Strict as HM-import qualified Data.HashMap.Strict as HashMap-import Data.Hashable (Hashable)-import Data.List (foldl')-import Data.Maybe (fromMaybe, isJust)-import Data.Serialize (Get, Put, Serialize)-import qualified Data.Serialize as S-import GHC.Generics (Generic)-import GHC.Word (Word32, Word8)-import Haskoin.Address (Address (..), pubKeyAddr)-import Haskoin.Crypto (SecKey, derivePubKey)-import Haskoin.Data (Network)-import Haskoin.Keys (- DerivPath,- DerivPathI (Deriv),- Fingerprint,- KeyIndex,- PubKeyI,- SecKeyI (SecKeyI),- XPrvKey,- derivePath,- deriveXPubKey,- listToPath,- pathToList,- pubKeyCompressed,- pubKeyPoint,- xPrvKey,- xPubFP,- )-import Haskoin.Network (- VarInt (..),- VarString (..),- putVarInt,- )-import Haskoin.Script (- Script (..),- ScriptOp (..),- ScriptOutput (..),- SigHash,- decodeOutput,- decodeOutputBS,- encodeOutputBS,- encodeTxSig,- isPayScriptHash,- opPushData,- sigHashAll,- toP2SH,- toP2WSH,- )-import Haskoin.Transaction.Builder (SigInput (..), makeSignature)-import Haskoin.Transaction.Common (- Tx (..),- TxOut,- WitnessStack,- outPointIndex,- outValue,- prevOutput,- scriptInput,- scriptOutput,- )-import Haskoin.Transaction.Segwit (isSegwit)-import 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--{- | A version of 'merge' for a collection of PSBTs.-- @since 0.21.0--}-mergeMany :: [PartiallySignedTransaction] -> Maybe PartiallySignedTransaction-mergeMany (psbt : psbts) = foldM merge psbt psbts-mergeMany _ = 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- }--{- | 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 ::- PubKeyI ->- 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 'PubKeyI' if possible.-- @since 0.21@--}-getSignerKey :: PsbtSigner -> PubKeyI -> Maybe (Fingerprint, DerivPath) -> Maybe SecKey-getSignerKey = unPsbtSigner--{- | This signer can sign for one key.-- @since 0.21@--}-secKeySigner :: SecKey -> PsbtSigner-secKeySigner theSecKey = PsbtSigner signer- where- signer requiredKey _- | pubKeyPoint requiredKey == derivePubKey theSecKey = Just theSecKey- | otherwise = Nothing--{- | This signer can sign with any child key, provided that derivation information is present.-- @since 0.21@--}-xPrvSigner ::- XPrvKey ->- -- | Origin data, if the input key is explicitly a child key- Maybe (Fingerprint, DerivPath) ->- PsbtSigner-xPrvSigner xprv origin = PsbtSigner signer- where- signer pubKey (Just hdData)- | result@(Just theSecKey) <- maybe noOrigin onOrigin origin hdData- , pubKeyPoint pubKey == derivePubKey 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 = xPrvKey $ derivePath path xprv-- thisFP = xPubFP $ deriveXPubKey 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 ->- PsbtSigner ->- PartiallySignedTransaction ->- PartiallySignedTransaction-signPSBT net signer psbt =- psbt- { inputs = addSigsForInput net signer tx <$> zip [0 :: Int ..] (inputs psbt)- }- where- tx = unsignedTransaction psbt--addSigsForInput :: Network -> PsbtSigner -> Tx -> (Int, Input) -> Input-addSigsForInput net signer tx (ix, input) =- maybe input (onPrevTxOut net signer tx ix input) $- Left <$> nonWitnessUtxo input <|> Right <$> witnessUtxo input--onPrevTxOut ::- Network ->- PsbtSigner ->- Tx ->- Int ->- Input ->- Either Tx TxOut ->- Input-onPrevTxOut net signer tx ix input prevTxData =- input- { partialSigs = newSigs <> partialSigs input- }- where- newSigs = HM.mapWithKey sigForInput sigKeys- sigForInput thePubKey theSecKey =- encodeTxSig . makeSignature net tx ix theSigInput $- SecKeyI theSecKey (pubKeyCompressed thePubKey)-- theSigInput =- SigInput- { -- Must be the segwit input script for segwit spends (even nested)- sigInputScript = fromMaybe theInputScript segwitInput- , sigInputValue = outValue prevTxOut- , sigInputOP = thePrevOutPoint- , sigInputSH = fromMaybe sigHashAll $ sigHashType input- , -- Must be the witness script for segwit spends (even nested)- sigInputRedeem = theWitnessScript <|> theRedeemScript- }-- prevTxOut = either ((!! (fromIntegral . outPointIndex) thePrevOutPoint) . txOut) id prevTxData- thePrevOutPoint = prevOutput $ txIn tx !! ix-- segwitInput = justWhen isSegwit theInputScript <|> (justWhen isSegwit =<< theRedeemScript)-- theInputScript = fromRight inputScriptErr $ (decodeOutputBS . scriptOutput) prevTxOut- inputScriptErr = error "addSigsForInput: Unable to decode input script"-- theRedeemScript = case decodeOutput <$> inputRedeemScript input of- Just (Right script) -> Just script- Just Left{} -> error "addSigsForInput: Unable to decode redeem script"- _ -> Nothing-- theWitnessScript = case decodeOutput <$> inputWitnessScript input of- Just (Right script) -> Just script- Just Left{} -> error "addSigsForInput: Unable to decode witness script"- _ -> Nothing-- sigKeys = HM.mapMaybeWithKey getSignerKey $ inputHDKeypaths input- 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 ::- 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) = pruneInputFields $ completeSig input script-- -- If we have final scripts, we can get rid of data for signing following- -- the Bitcoin Core implementation.- pruneInputFields input- | isJust (finalScriptSig input) || isJust (finalScriptWitness input) =- input- { partialSigs = mempty- , inputHDKeypaths = mempty- , inputRedeemScript = Nothing- , inputWitnessScript = Nothing- , sigHashType = Nothing- }- | otherwise = input-- indexed :: [a] -> [(Word32, a)]- indexed = zip [0 ..]--completeSig :: Input -> ScriptOutput -> Input-completeSig input (PayPK k) =- input- { finalScriptSig =- eitherToMaybe . runGetS deserialize- =<< 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 (runPutS (serialize k))- ]- }-completeSig 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 input (PayScriptHash h)- | Just rdmScript <- inputRedeemScript input- , PayScriptHash h == toP2SH rdmScript- , Right decodedScript <- decodeOutput rdmScript- , not (isPayScriptHash decodedScript) =- pushScript rdmScript $ completeSig input decodedScript- where- pushScript rdmScript updatedInput =- updatedInput- { finalScriptSig =- Just $- fromMaybe (Script mempty) (finalScriptSig updatedInput)- `scriptAppend` serializedRedeemScript rdmScript- }- scriptAppend (Script script1) (Script script2) = Script $ script1 <> script2-completeSig input (PayWitnessPKHash h)- | [(k, sig)] <- HashMap.toList (partialSigs input)- , PubKeyAddress h == pubKeyAddr k =- input{finalScriptWitness = Just [sig, runPutS $ serialize k]}-completeSig input (PayWitnessScriptHash h)- | Just witScript <- inputWitnessScript input- , PayWitnessScriptHash h == toP2WSH witScript- , Right decodedScript <- decodeOutput witScript =- completeWitnessSig input decodedScript-completeSig input _ = input--serializedRedeemScript :: Script -> Script-serializedRedeemScript = Script . pure . opPushData . runPutS . serialize--completeWitnessSig :: Input -> ScriptOutput -> Input-completeWitnessSig input script@(PayMulSig pubKeys m)- | length sigs >= m =- input{finalScriptWitness = Just finalWit}- where- sigs = collectSigs m pubKeys 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) =- ( txInput{scriptInput = maybe mempty (runPutS . serialize) $ finalScriptSig psbtInput} : 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 <- S.getBytes 4- guard $ magic == "psbt"- headerSep <- S.getWord8- guard $ headerSep == 0xff-- keySize <- S.getWord8- guard $ keySize == 1- globalUnsignedTxType <- S.getWord8- guard $ globalUnsignedTxType == 0x00- unsignedTransaction <- getSizedBytes deserialize- guard $ all (B.null . scriptInput) (txIn unsignedTransaction)- guard $ null (txWitness unsignedTransaction)-- globalUnknown <- S.get- globalEnd <- S.getWord8- guard $ globalEnd == 0x00-- inputs <-- replicateM- (length (txIn unsignedTransaction))- S.get- outputs <-- replicateM- (length (txOut unsignedTransaction))- S.get-- return- PartiallySignedTransaction- { unsignedTransaction- , globalUnknown- , inputs- , outputs- }-- put- PartiallySignedTransaction- { unsignedTransaction- , globalUnknown- , inputs- , outputs- } = do- S.putByteString "psbt"- S.putWord8 0xff -- Header separator- S.putWord8 0x01 -- Key size- S.putWord8 0x00 -- Unsigned Transaction type- putSizedBytes $ serialize unsignedTransaction- S.put globalUnknown- S.putWord8 0x00 -- Global end- mapM_ S.put inputs- mapM_ S.put outputs--instance Serialize Key where- get = do- VarInt keySize <- deserialize- guard $ keySize > 0- t <- S.getWord8- k <- S.getBytes (fromIntegral keySize - 1)- return (Key t k)-- put (Key t k) = do- putVarInt $ 1 + B.length k- S.putWord8 t- S.putByteString k--instance Serialize UnknownMap where- get = go HashMap.empty- where- getItem m = do- k <- S.get- VarString v <- deserialize- go $ HashMap.insert k v m- go m = do- isEnd <- S.lookAhead S.getWord8- if isEnd == 0x00- then return (UnknownMap m)- else getItem m-- put (UnknownMap m) =- void $- HashMap.traverseWithKey- (\k v -> S.put k >> serialize (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 . serialize)- nonWitnessUtxo- whenJust- (putKeyValue InWitnessUtxo . serialize)- witnessUtxo- putPartialSig partialSigs- whenJust- putSigHash- sigHashType- whenJust- (putKeyValue InRedeemScript . serialize)- inputRedeemScript- whenJust- (putKeyValue InWitnessScript . serialize)- inputWitnessScript- putHDPath InBIP32Derivation inputHDKeypaths- whenJust- (putKeyValue InFinalScriptSig . serialize)- finalScriptSig- whenJust- (putKeyValue InFinalScriptWitness . putFinalScriptWitness)- finalScriptWitness- S.put inputUnknown- S.putWord8 0x00- where- putPartialSig =- putPubKeyMap serialize InPartialSig . fmap VarString- putSigHash sigHash = do- putKey InSigHashType- S.putWord8 0x04- S.putWord32le (fromIntegral sigHash)- putFinalScriptWitness witnessStack = do- S.put $ (VarInt . fromIntegral . length) witnessStack- mapM_ (serialize . VarString) witnessStack--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 . serialize)- outputRedeemScript- whenJust- (putKeyValue OutWitnessScript . serialize)- outputWitnessScript- putHDPath- OutBIP32Derivation- outputHDKeypaths- S.put outputUnknown- S.putWord8 0x00--putSizedBytes :: Put -> Put-putSizedBytes f = do- putVarInt (B.length bs)- S.putByteString bs- where- bs = S.runPut f--getSizedBytes :: Get a -> Get a-getSizedBytes =- S.getNested- (fromIntegral . getVarInt <$> deserialize)--putKeyValue :: Enum t => t -> Put -> Put-putKeyValue t v = do- putKey t- putSizedBytes v--putKey :: Enum t => t -> Put-putKey t = do- putVarInt (1 :: Word8)- S.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 <- S.getBytes (fromIntegral keySize - 1)- VarString v <- deserialize- go $ setUnknown (HashMap.insert (Key t k) v) m- go m = do- keySize <- getVarInt <$> deserialize- if keySize == 0- then return m- else getItem keySize m . word8Enum =<< S.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 deserialize- return input{nonWitnessUtxo = Just utxo}-getInputItem 0 input@Input{witnessUtxo = Nothing} InWitnessUtxo = do- utxo <- getSizedBytes deserialize- return input{witnessUtxo = Just utxo}-getInputItem keySize input InPartialSig = do- (k, v) <- getPartialSig- return- input- { partialSigs = HashMap.insert k v (partialSigs input)- }- where- getPartialSig =- (,)- <$> S.isolate keySize deserialize- <*> (getVarString <$> deserialize)-getInputItem 0 input@Input{sigHashType = Nothing} InSigHashType = do- VarInt size <- deserialize- guard $ size == 0x04- sigHash <- fromIntegral <$> S.getWord32le- return $ input{sigHashType = Just sigHash}-getInputItem 0 input@Input{inputRedeemScript = Nothing} InRedeemScript = do- script <- getSizedBytes deserialize- return $ input{inputRedeemScript = Just script}-getInputItem 0 input@Input{inputWitnessScript = Nothing} InWitnessScript = do- script <- getSizedBytes deserialize- 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 deserialize- return $ input{finalScriptSig = Just script}-getInputItem 0 input@Input{finalScriptWitness = Nothing} InFinalScriptWitness = do- scripts <- map getVarString <$> getVarIntList- return $ input{finalScriptWitness = Just scripts}- where- getVarIntList = getSizedBytes $ do- VarInt n <- deserialize -- Item count- replicateM (fromIntegral n) deserialize-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 deserialize- return $ output{outputRedeemScript = Just script}-getOutputItem 0 output@Output{outputWitnessScript = Nothing} OutWitnessScript = do- script <- getSizedBytes deserialize- 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 =- (,)- <$> S.isolate keySize deserialize- <*> (unPSBTHDPath <$> S.get)--putHDPath :: Enum t => t -> HashMap PubKeyI (Fingerprint, [KeyIndex]) -> Put-putHDPath t = putPubKeyMap S.put t . fmap PSBTHDPath--newtype PSBTHDPath = PSBTHDPath {unPSBTHDPath :: (Fingerprint, [KeyIndex])}- deriving (Show, Eq, Generic)--instance NFData PSBTHDPath--instance Serialize PSBTHDPath where- get = do- VarInt valueSize <- deserialize- guard $ valueSize `mod` 4 == 0- let numIndices = (fromIntegral valueSize - 4) `div` 4- PSBTHDPath- <$> S.isolate- (fromIntegral valueSize)- ((,) <$> S.get <*> getKeyIndexList numIndices)- where- getKeyIndexList n = replicateM n S.getWord32le-- put (PSBTHDPath (fp, kis)) = do- putVarInt (B.length bs)- S.putByteString bs- where- bs = S.runPut $ S.put fp >> mapM_ S.putWord32le kis--putPubKeyMap :: Enum t => (a -> Put) -> t -> HashMap PubKeyI a -> Put-putPubKeyMap f t =- void . HashMap.traverseWithKey putItem- where- putItem k v = do- S.put $ Key (enumWord8 t) (runPutS (serialize 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+{-# 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
@@ -1,22 +1,23 @@+{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-}--{- |-Module : Haskoin.Transaction.Segwit-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE NoFieldSelectors #-} -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+-- |+-- 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 (..),@@ -26,131 +27,149 @@ calcWitnessProgram, simpleInputStack, toWitnessStack,-) where+ )+where +import Crypto.Secp256k1 import Data.ByteString (ByteString)-import Data.Bytes.Get-import Data.Bytes.Put-import Data.Bytes.Serial-import Haskoin.Data-import Haskoin.Keys.Common-import Haskoin.Script+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--{- | Test if a 'ScriptOutput' is P2WPKH or P2WSH+import Haskoin.Util.Marshal - @since 0.11.0.0--}+-- | 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+ PayWitnessPKHash {} -> True+ PayWitnessScriptHash {} -> True+ _ -> False - @since 0.11.0.0--}+-- | 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, runPutS (serialize key)]- P2WSH (WitnessProgramSH stack scr) -> stack <> [runPutS (serialize scr)]- EmptyWitnessProgram -> mempty+ = P2WPKH WitnessProgramPKH+ | P2WSH WitnessProgramSH+ | EmptyWitnessProgram+ deriving (Eq) -{- | High level representation of a P2WPKH witness+-- | 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 - @since 0.11.0.0--}+-- | 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+ { signature :: !TxSignature,+ key :: !PublicKey+ }+ deriving (Eq) - @since 0.11.0.0--}+-- | 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+ { stack :: ![ByteString],+ script :: !Script+ }+ deriving (Eq, Show) - @since 0.11.0.0--}+-- | 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 <- runGetS deserialize $ 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+ 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" - @since 0.11.0.0--}+-- | 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 _, [sigBS]) ->- SpendPK <$> decodeTxSig net sigBS- (PayPKHash _, [sigBS, keyBS]) ->- SpendPKHash <$> decodeTxSig net sigBS <*> runGetS deserialize keyBS- (PayMulSig _ _, "" : 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+ 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" - @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"+-- | 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 = 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+ p2wpkh sig =+ P2WPKH . WitnessProgramPKH sig+ p2wsh i =+ P2WSH . WitnessProgramSH (simpleInputStack net ctx i) . encodeOutput ctx - @since 0.11.0.0--}-simpleInputStack :: SimpleInput -> [ByteString]-simpleInputStack = \case- SpendPK sig -> [f sig]- SpendPKHash sig k -> [f sig, runPutS (serialize k)]- SpendMulSig sigs -> "" : fmap f sigs+-- | 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 sig+ f sig = encodeTxSig net ctx sig
src/Haskoin/Transaction/Taproot.hs view
@@ -1,21 +1,26 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeApplications #-}--{- |-Module : Haskoin.Transaction.Taproot-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX+{-# LANGUAGE NoFieldSelectors #-} -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 (..),+-- |+-- 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,@@ -28,283 +33,283 @@ viewTaprootWitness, encodeTaprootWitness, verifyScriptPathData,-) where+ )+where import Control.Applicative (many) import Control.Monad ((<=<))-import Crypto.Hash (- Digest,+import Crypto.Hash+ ( Digest, SHA256, digestFromByteString, hashFinalize, hashUpdate, hashUpdates,- )-import Data.Aeson (FromJSON (parseJSON), ToJSON (toJSON), withText)+ )+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 qualified Data.ByteArray as BA+import Data.ByteArray qualified as BA import Data.ByteString (ByteString)-import qualified Data.ByteString as BS-import Data.Bytes.Get (getBytes, runGetS)-import Data.Bytes.Put (putByteString, runPutS)+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 (PubKey, initTaggedHash, tweak, tweakAddPubKey)-import Haskoin.Keys.Common (PubKeyI (PubKeyI), pubKeyPoint)-import Haskoin.Script.Common (Script)-import Haskoin.Script.Standard (ScriptOutput (PayWitness))-import Haskoin.Transaction.Common (WitnessStack)-import Haskoin.Util (decodeHex, eitherToMaybe, encodeHex)--{- | 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.+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 -@since 0.21.0--}-newtype XOnlyPubKey = XOnlyPubKey {xOnlyPubKey :: PubKey}- deriving (Show)+-- | 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- k1 == k2 = runPutS (serialize k1) == runPutS (serialize k2)+ XOnlyPubKey k1 == XOnlyPubKey k2 = f k1 == f k2+ where+ f = BS.take 32 . (.get) -instance Serial XOnlyPubKey where- serialize (XOnlyPubKey pk) =- putByteString- . BS.drop 1- . runPutS- . serialize- $ PubKeyI pk True- deserialize =- either fail (pure . XOnlyPubKey . pubKeyPoint)- . runGetS deserialize- . BS.cons 0x02- =<< getBytes 32+instance Marshal Ctx XOnlyPubKey where+ marshalPut ctx (XOnlyPubKey pk) =+ putByteString+ . BS.drop 1+ . marshal ctx+ $ PublicKey pk True -instance Serialize XOnlyPubKey where- put = serialize- get = deserialize+ marshalGet ctx =+ either fail (pure . XOnlyPubKey . (\PublicKey {point} -> point))+ . unmarshal ctx+ . BS.cons 0x02+ =<< getBytes 32 -instance Binary XOnlyPubKey where- put = serialize- get = deserialize+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 --- | Hex encoding-instance FromJSON XOnlyPubKey where- parseJSON =- withText "XOnlyPubKey" $- either fail pure- . (runGetS deserialize <=< maybe (Left "Unable to decode hex") Right . decodeHex)+ marshalValue ctx =+ String . encodeHex . marshal ctx --- | Hex encoding-instance ToJSON XOnlyPubKey where- toJSON = toJSON . encodeHex . runPutS . serialize+ 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--}+-- | 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.+ = MASTBranch MAST MAST+ | MASTLeaf TapLeafVersion Script+ | MASTCommitment (Digest SHA256)+ deriving (Show) -@since 0.21.0--}+-- | 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+ 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.+ (v, s, reverse $ proofInit <> (branchCommitment : proofTail)) -@since 0.21.0--}+-- | 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+ 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- ]+ 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+ 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--}+-- | Representation of a full taproot output.+--+-- @since 0.21.0 data TaprootOutput = TaprootOutput- { taprootInternalKey :: PubKey- , taprootMAST :: Maybe MAST- }- deriving (Show)+ { internalKey :: PubKey,+ mast :: Maybe MAST+ } -- | @since 0.21.0-taprootOutputKey :: TaprootOutput -> PubKey-taprootOutputKey TaprootOutput{taprootInternalKey, taprootMAST} =- fromMaybe keyFail $ tweak commitment >>= tweakAddPubKey taprootInternalKey+taprootOutputKey :: Ctx -> TaprootOutput -> PubKey+taprootOutputKey ctx TaprootOutput {..} =+ fromMaybe keyFail $+ tweak commitment >>= tweakAddPubKey ctx internalKey where- commitment = taprootCommitment taprootInternalKey $ mastCommitment <$> taprootMAST+ commitment =+ taprootCommitment ctx internalKey $+ mastCommitment <$> mast keyFail = error "haskoin-core taprootOutputKey: key derivation failed" -taprootCommitment :: PubKey -> Maybe (Digest SHA256) -> ByteString-taprootCommitment internalKey merkleRoot =- BA.convert . hashFinalize- . maybe id (flip hashUpdate) merkleRoot- . (`hashUpdate` keyBytes)- $ initTaggedHash "TapTweak"+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 . serialize $ XOnlyPubKey internalKey--{- | Generate the output script for a taproot output--@since 0.21.0--}-taprootScriptOutput :: TaprootOutput -> ScriptOutput-taprootScriptOutput = PayWitness 0x01 . runPutS . serialize . XOnlyPubKey . taprootOutputKey+ keyBytes = runPutS . marshalPut ctx $ XOnlyPubKey internalKey -{- | Comprehension of taproot witness data+-- | 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 -@since 0.21.0--}+-- | Comprehension of taproot witness data+--+-- @since 0.21.0 data TaprootWitness- = -- | Signature- KeyPathSpend ByteString- | ScriptPathSpend ScriptPathData- deriving (Eq, Show)+ = -- | Signature+ KeyPathSpend ByteString+ | ScriptPathSpend ScriptPathData+ deriving (Eq) -- | @since 0.21.0 data ScriptPathData = ScriptPathData- { scriptPathAnnex :: Maybe ByteString- , scriptPathStack :: [ByteString]- , scriptPathScript :: Script- , scriptPathExternalIsOdd :: Bool- , -- | This value is masked by 0xFE- scriptPathLeafVersion :: Word8- , scriptPathInternalKey :: PubKey- , scriptPathControl :: [ByteString]- }- deriving (Eq, Show)--{- | Try to interpret a 'WitnessStack' as taproot witness data.+ { annex :: Maybe ByteString,+ stack :: [ByteString],+ script :: Script,+ extIsOdd :: Bool,+ -- | This value is masked by 0xFE+ leafVersion :: Word8,+ internalKey :: PubKey,+ control :: [ByteString]+ }+ deriving (Eq) -@since 0.21.0--}-viewTaprootWitness :: WitnessStack -> Maybe TaprootWitness-viewTaprootWitness witnessStack = case reverse witnessStack of- [sig] -> Just $ KeyPathSpend sig- annexA : remainingStack- | 0x50 : _ <- BS.unpack annexA ->- parseSpendPathData (Just annexA) remainingStack- remainingStack -> parseSpendPathData Nothing remainingStack+-- | 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 scriptPathAnnex = \case- scriptBytes : controlBytes : scriptPathStack -> do- scriptPathScript <- eitherToMaybe $ runGetS deserialize scriptBytes- (v, scriptPathInternalKey, scriptPathControl) <- deconstructControl controlBytes- pure . ScriptPathSpend $- ScriptPathData- { scriptPathAnnex- , scriptPathStack- , scriptPathScript- , scriptPathExternalIsOdd = odd v- , scriptPathLeafVersion = v .&. 0xFE- , scriptPathInternalKey- , scriptPathControl- }- _ -> Nothing+ 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- k <- xOnlyPubKey <$> deserialize- proof <- many $ getByteString 32- pure (v, k, proof)--{- | Transform the high-level representation of taproot witness data into a witness stack+ v <- getWord8+ XOnlyPubKey k <- marshalGet ctx+ proof <- many $ getByteString 32+ pure (v, k, proof) -@since 0.21.0--}-encodeTaprootWitness :: TaprootWitness -> WitnessStack-encodeTaprootWitness = \case- KeyPathSpend signature -> pure signature- ScriptPathSpend scriptPathData ->- scriptPathStack scriptPathData- <> [ runPutS . serialize $ scriptPathScript scriptPathData- , mconcat- [ BS.pack [scriptPathLeafVersion scriptPathData .|. parity scriptPathData]- , runPutS . serialize . XOnlyPubKey $ scriptPathInternalKey scriptPathData- , mconcat $ scriptPathControl scriptPathData- ]- , fromMaybe mempty $ scriptPathAnnex scriptPathData- ]+-- | 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- parity = bool 0 1 . scriptPathExternalIsOdd--{- | Verify that the script path spend is valid, except for script execution.+ 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) -@since 0.21.0--}+-- | Verify that the script path spend is valid, except for script execution.+--+-- @since 0.21.0 verifyScriptPathData ::- -- | Output key- PubKey ->- ScriptPathData ->- Bool-verifyScriptPathData outputKey scriptPathData = fromMaybe False $ do- tweak commitment >>= fmap onComputedKey . tweakAddPubKey (scriptPathInternalKey scriptPathData)+ 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 outputKey == XOnlyPubKey computedKey- && expectedParity == keyParity computedKey- commitment = taprootCommitment (scriptPathInternalKey scriptPathData) (Just merkleRoot)+ XOnlyPubKey outkey == XOnlyPubKey computedKey+ && expectedParity == keyParity ctx computedKey+ commitment =+ taprootCommitment ctx spd.internalKey (Just merkleRoot) merkleRoot =- foldl' hashBranch theLeafHash- . mapMaybe (digestFromByteString @SHA256)- $ scriptPathControl scriptPathData- theLeafHash = (leafHash <$> (.&. 0xFE) . scriptPathLeafVersion <*> scriptPathScript) scriptPathData- expectedParity = bool 0 1 $ scriptPathExternalIsOdd scriptPathData+ foldl' hashBranch theLeafHash $+ mapMaybe (digestFromByteString @SHA256) spd.control+ theLeafHash =+ (leafHash <$> (.&. 0xFE) . (.leafVersion) <*> (.script))+ spd+ expectedParity = bool 0 1 spd.extIsOdd -keyParity :: PubKey -> Word8-keyParity key = case BS.unpack . runPutS . serialize $ PubKeyI key True of+keyParity :: Ctx -> PubKey -> Word8+keyParity ctx key =+ case BS.unpack . marshal ctx $ PublicKey key True of 0x02 : _ -> 0x00 _ -> 0x01
src/Haskoin/Util.hs view
@@ -1,381 +1,17 @@-{-# LANGUAGE CPP #-}-{-# 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 (- -- * ByteString Helpers- bsToInteger,- integerToBS,- 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,-) where--import Control.Monad-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.ByteString.Builder-import qualified Data.ByteString.Lazy as BL-import qualified Data.ByteString.Lazy.Base16 as BL16-import Data.Bytes.Get-import Data.Bytes.Put-import Data.Bytes.Serial-import Data.Char (toLower)-import Data.Int-import Data.IntMap (IntMap)-import qualified Data.IntMap as IntMap-import Data.List-import Data.Text (Text)-import qualified Data.Text.Encoding as E-import qualified Data.Text.Lazy as TL-import qualified Data.Text.Lazy.Encoding as EL-import Data.Word---- 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)--hexBuilder :: BL.ByteString -> Builder-hexBuilder = lazyByteStringHex--encodeHex :: ByteString -> Text-encodeHex = B16.encodeBase16---- | Encode as string of human-readable hex characters.-encodeHexLazy :: BL.ByteString -> TL.Text-encodeHexLazy = BL16.encodeBase16--decodeHex :: Text -> Maybe ByteString-decodeHex = eitherToMaybe . B16.decodeBase16 . E.encodeUtf8---- | Decode string of human-readable hex characters.-decodeHexLazy :: TL.Text -> Maybe BL.ByteString-decodeHexLazy = eitherToMaybe . BL16.decodeBase16 . EL.encodeUtf8--{- | 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 ::- -- | 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 (BS.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+-- |+-- Module : Haskoin.Util+-- Copyright : No rights reserved+-- License : MIT+-- Maintainer : jprupp@protonmail.ch+-- Stability : experimental+-- Portability : POSIX ---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+-- Marshalling and helper functions.+module Haskoin.Util+ ( module Marshal,+ module Helpers,+ )+where -getList :: MonadGet m => m a -> m [a]-getList f = do- l <- fromIntegral <$> getWord64be- replicateM l f+import Haskoin.Util.Helpers as Helpers+import Haskoin.Util.Marshal as Marshal
src/Haskoin/Util/Arbitrary.hs view
@@ -1,16 +1,16 @@-{- |-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+-- |+-- 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
src/Haskoin/Util/Arbitrary/Address.hs view
@@ -1,19 +1,18 @@ {-# LANGUAGE TupleSections #-} -{- |-Module : Haskoin.Test.Address-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX--}+-- |+-- 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.Constants-import Haskoin.Data+import Haskoin.Network.Constants+import Haskoin.Network.Data import Haskoin.Util.Arbitrary.Crypto import Haskoin.Util.Arbitrary.Util import Test.QuickCheck@@ -25,21 +24,21 @@ -- | Arbitrary address including pay-to-witness arbitraryAddressAll :: Gen Address arbitraryAddressAll =- oneof- [ arbitraryPubKeyAddress- , arbitraryScriptAddress- , arbitraryWitnessPubKeyAddress- , arbitraryWitnessScriptAddress- , arbitraryWitnessAddress- ]+ 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+ 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@@ -59,8 +58,8 @@ 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+ 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
@@ -1,16 +1,16 @@-{- |-Module : Haskoin.Test.Block-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX--}+-- |+-- 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.Data+import Haskoin.Crypto (Ctx)+import Haskoin.Network.Data import Haskoin.Util.Arbitrary.Crypto import Haskoin.Util.Arbitrary.Network import Haskoin.Util.Arbitrary.Transaction@@ -18,22 +18,23 @@ 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+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+ BlockHeader+ <$> arbitrary+ <*> arbitraryBlockHash+ <*> arbitraryHash256+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary -- | Arbitrary block hash. arbitraryBlockHash :: Gen BlockHash@@ -42,45 +43,45 @@ -- | Arbitrary 'GetBlocks' object with at least one block hash. arbitraryGetBlocks :: Gen GetBlocks arbitraryGetBlocks =- GetBlocks <$> arbitrary- <*> listOf1 arbitraryBlockHash- <*> arbitraryBlockHash+ GetBlocks+ <$> arbitrary+ <*> listOf1 arbitraryBlockHash+ <*> arbitraryBlockHash -- | Arbitrary 'GetHeaders' object with at least one block header. arbitraryGetHeaders :: Gen GetHeaders arbitraryGetHeaders =- GetHeaders <$> arbitrary- <*> listOf1 arbitraryBlockHash- <*> arbitraryBlockHash+ GetHeaders+ <$> arbitrary+ <*> listOf1 arbitraryBlockHash+ <*> arbitraryBlockHash -- | Arbitrary 'Headers' object with at least one block header. arbitraryHeaders :: Gen Headers arbitraryHeaders =- Headers <$> listOf1 ((,) <$> arbitraryBlockHeader <*> arbitraryVarInt)+ 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+ 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 =- oneof- [ BlockNode- <$> arbitraryBlockHeader- <*> choose (0, maxBound)- <*> arbitrarySizedNatural- <*> arbitraryBlockHash- ]+ BlockNode+ <$> arbitraryBlockHeader+ <*> choose (0, maxBound)+ <*> arbitrarySizedNatural+ <*> arbitraryBlockHash -- | Arbitrary 'HeaderMemory' arbitraryHeaderMemory :: Gen HeaderMemory arbitraryHeaderMemory = do- ls <- listOf $ (,) <$> arbitrary <*> arbitraryBSS- HeaderMemory (HashMap.fromList ls) <$> arbitraryBlockNode+ ls <- listOf $ (,) <$> arbitrary <*> arbitraryBSS+ HeaderMemory (HashMap.fromList ls) <$> arbitraryBlockNode
src/Haskoin/Util/Arbitrary/Crypto.hs view
@@ -1,11 +1,10 @@-{- |-Module : Haskoin.Test.Crypto-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX--}+-- |+-- 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@@ -15,19 +14,19 @@ -- | Arbitrary 160-bit hash. arbitraryHash160 :: Gen Hash160 arbitraryHash160 =- ripemd160 <$> arbitraryBSn 20+ ripemd160 <$> arbitraryBSn 20 -- | Arbitrary 256-bit hash. arbitraryHash256 :: Gen Hash256 arbitraryHash256 =- sha256 <$> arbitraryBSn 32+ sha256 <$> arbitraryBSn 32 -- | Arbitrary 512-bit hash. arbitraryHash512 :: Gen Hash512 arbitraryHash512 =- sha512 <$> arbitraryBSn 64+ sha512 <$> arbitraryBSn 64 -- | Arbitrary 32-bit checksum. arbitraryCheckSum32 :: Gen CheckSum32 arbitraryCheckSum32 =- checkSum32 <$> arbitraryBSn 4+ checkSum32 <$> arbitraryBSn 4
src/Haskoin/Util/Arbitrary/Keys.hs view
@@ -1,33 +1,38 @@-{- |-Module : Haskoin.Test.Keys-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX--}+-- |+-- 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-import Haskoin.Keys.Common-import Haskoin.Keys.Extended-import Haskoin.Keys.Extended.Internal (Fingerprint (..))+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.-arbitrarySecKeyI :: Gen SecKeyI-arbitrarySecKeyI = wrapSecKey <$> arbitrary <*> arbitrary+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 :: Gen (SecKeyI, PubKeyI)-arbitraryKeyPair = do- k <- arbitrarySecKeyI- return (k, derivePubKeyI k)+arbitraryKeyPair :: Ctx -> Gen (PrivateKey, PublicKey)+arbitraryKeyPair ctx = do+ k <- arbitraryPrivateKey+ return (k, derivePublicKey ctx k) arbitraryFingerprint :: Gen Fingerprint arbitraryFingerprint = Fingerprint <$> arbitrary@@ -35,15 +40,20 @@ -- | Arbitrary extended private key. arbitraryXPrvKey :: Gen XPrvKey arbitraryXPrvKey =- XPrvKey <$> arbitrary- <*> arbitraryFingerprint- <*> arbitrary- <*> arbitraryHash256- <*> arbitrary+ 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.-arbitraryXPubKey :: Gen (XPrvKey, XPubKey)-arbitraryXPubKey = (\k -> (k, deriveXPubKey k)) <$> arbitraryXPrvKey+arbitraryXKeyPair :: Ctx -> Gen (XPrvKey, XPubKey)+arbitraryXKeyPair ctx = (\k -> (k, deriveXPubKey ctx k)) <$> arbitraryXPrvKey {- Custom derivations -} @@ -54,10 +64,10 @@ -- | Arbitrary BIP-32 path index. Can be hardened or not. arbitraryBip32PathIndex :: Gen Bip32PathIndex arbitraryBip32PathIndex =- oneof- [ Bip32SoftIndex <$> genIndex- , Bip32HardIndex <$> genIndex- ]+ oneof+ [ Bip32SoftIndex <$> genIndex,+ Bip32HardIndex <$> genIndex+ ] -- | Arbitrary BIP-32 derivation path composed of only hardened derivations. arbitraryHardPath :: Gen HardPath@@ -71,24 +81,22 @@ arbitraryDerivPath :: Gen DerivPath arbitraryDerivPath = concatBip32Segments <$> listOf arbitraryBip32PathIndex -{- | Arbitrary parsed derivation path. Can contain 'ParsedPrv', 'ParsedPub' or- 'ParsedEmpty' elements.--}+-- | Arbitrary parsed derivation path. Can contain 'ParsedPrv', 'ParsedPub' or+-- 'ParsedEmpty' elements. arbitraryParsedPath :: Gen ParsedPath arbitraryParsedPath =- oneof- [ ParsedPrv <$> arbitraryDerivPath- , ParsedPub <$> arbitraryDerivPath- , ParsedEmpty <$> arbitraryDerivPath- ]+ 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)+-- | 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
@@ -1,14 +1,14 @@-{- |-Module : Haskoin.Test.Message-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX--}+-- |+-- 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.Data+import Haskoin.Crypto (Ctx)+import Haskoin.Network.Data import Haskoin.Network.Message import Haskoin.Util.Arbitrary.Block import Haskoin.Util.Arbitrary.Crypto@@ -19,34 +19,35 @@ -- | Arbitrary 'MessageHeader'. arbitraryMessageHeader :: Gen MessageHeader arbitraryMessageHeader =- MessageHeader <$> arbitrary- <*> arbitraryMessageCommand- <*> arbitrary- <*> arbitraryCheckSum32+ 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- ]+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
@@ -1,11 +1,10 @@-{- |-Module : Haskoin.Test.Network-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX--}+-- |+-- 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)@@ -28,20 +27,20 @@ -- | 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+ 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)@@ -62,52 +61,52 @@ -- | Arbitrary 'Version'. arbitraryVersion :: Gen Version arbitraryVersion =- Version <$> arbitrary- <*> arbitrary- <*> arbitrary- <*> arbitraryNetworkAddress- <*> arbitraryNetworkAddress- <*> arbitrary- <*> arbitraryVarString- <*> arbitrary- <*> arbitrary+ 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.--}+-- | 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+ 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- ]+ elements+ [ RejectMalformed,+ RejectInvalid,+ RejectInvalid,+ RejectDuplicate,+ RejectNonStandard,+ RejectDust,+ RejectInsufficientFee,+ RejectCheckpoint+ ] -- | Arbitrary non-empty 'GetData'. arbitraryGetData :: Gen GetData@@ -128,28 +127,27 @@ -- | Arbitrary bloom filter flags. arbitraryBloomFlags :: Gen BloomFlags arbitraryBloomFlags =- elements- [ BloomUpdateNone- , BloomUpdateAll- , BloomUpdateP2PubKeyOnly- ]+ elements+ [ BloomUpdateNone,+ BloomUpdateAll,+ BloomUpdateP2PubKeyOnly+ ] -{- | Arbitrary bloom filter with its corresponding number of elements- and false positive rate.--}+-- | 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)+ 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+ (_, _, bf) <- arbitraryBloomFilter+ return $ FilterLoad bf -- | Arbitrary 'FilterAdd'. arbitraryFilterAdd :: Gen FilterAdd@@ -158,26 +156,26 @@ -- | 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)))- ]+ 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
@@ -1,23 +1,25 @@+{-# 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.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 qualified Data.ByteString as B+import Data.ByteString qualified as B import Data.Maybe import Data.Word import Haskoin.Address-import Haskoin.Constants-import Haskoin.Data-import Haskoin.Keys.Common+import Haskoin.Crypto.Keys.Common+import Haskoin.Network.Constants+import Haskoin.Network.Data import Haskoin.Script import Haskoin.Transaction.Common import Haskoin.Util@@ -34,156 +36,156 @@ -- | 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- ]+ 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- ]+ 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@@ -196,88 +198,86 @@ -- | 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+ 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- ]+ 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 -> 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)+-- | 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 (getSigHashForkId net) && hasForkIdFlag sh+ not $+ isSigHashUnknown sh+ || isNothing net.sigHashForkId && hasForkIdFlag sh -- | Arbitrary transaction signature that could also be empty.-arbitraryTxSignatureEmpty :: Network -> Gen TxSignature-arbitraryTxSignatureEmpty net =- frequency- [ (1, return TxSignatureEmpty)- , (10, lst3 <$> arbitraryTxSignature net)- ]+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)+ 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- , arbitraryWitOutput- ]- else []+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 :: Gen ScriptOutput-arbitrarySimpleOutput =- oneof- [ arbitraryPKOutput- , arbitraryPKHashOutput- , arbitraryMSOutput- ]+-- | 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 :: Gen ScriptOutput-arbitraryPKOutput = PayPK . snd <$> arbitraryKeyPair+arbitraryPKOutput :: Ctx -> Gen ScriptOutput+arbitraryPKOutput ctx = PayPK . snd <$> arbitraryKeyPair ctx -- | Arbitrary 'ScriptOutput' of type 'PayPKHash' arbitraryPKHashOutput :: Gen ScriptOutput@@ -293,131 +293,128 @@ 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+ 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 :: Gen ScriptOutput-arbitraryMSOutput = do- (m, n) <- arbitraryMSParam- keys <- map snd <$> vectorOf n arbitraryKeyPair- return $ PayMulSig keys m+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 :: Gen ScriptOutput-arbitraryMSOutputC = do- (m, n) <- arbitraryMSParam- keys <-- map snd- <$> vectorOf n (arbitraryKeyPair `suchThat` (pubKeyCompressed . snd))- return $ PayMulSig keys m+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 . getAddrHash160 <$> arbitraryScriptAddress+arbitrarySHOutput = PayScriptHash . (.hash160) <$> 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- ]+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 -> Gen ScriptInput-arbitrarySimpleInput net =- oneof- [ arbitraryPKInput net- , arbitraryPKHashInput net- , arbitraryMSInput net- ]+-- | 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 -> Gen ScriptInput-arbitraryPKInput net = RegularInput . SpendPK <$> arbitraryTxSignatureEmpty net+arbitraryPKInput :: Network -> Ctx -> Gen ScriptInput+arbitraryPKInput net ctx = RegularInput . SpendPK <$> arbitraryTxSignatureEmpty net ctx -- | Arbitrary 'ScriptInput' of type 'SpendPK'.-arbitraryPKHashInput :: Network -> Gen ScriptInput-arbitraryPKHashInput net = do- sig <- arbitraryTxSignatureEmpty net- key <- snd <$> arbitraryKeyPair- return $ RegularInput $ SpendPKHash sig key+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 -> Gen ScriptInput-arbitraryPKHashInputFull net = do- sig <- lst3 <$> arbitraryTxSignature net- key <- snd <$> arbitraryKeyPair- return $ RegularInput $ SpendPKHash sig key+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 -> Gen ScriptInput-arbitraryPKHashInputFullC net = do- sig <- lst3 <$> arbitraryTxSignature net- key <- fmap snd $ arbitraryKeyPair `suchThat` (pubKeyCompressed . snd)- return $ RegularInput $ SpendPKHash sig key+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 -> Gen ScriptInput-arbitraryMSInput net = do- m <- fst <$> arbitraryMSParam- sigs <- vectorOf m (arbitraryTxSignatureEmpty net)- return $ RegularInput $ SpendMulSig sigs+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 -> Gen ScriptInput-arbitrarySHInput net = do- i <- arbitrarySimpleInput net- ScriptHashInput (getRegularInput i) <$> arbitrarySimpleOutput+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 -> 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'.+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 -> Gen ScriptInput-arbitraryMulSigSHInputC net =- arbitraryMSOutputC >>= \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 -> 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 -> Gen ScriptInput-arbitraryMulSigSHInputFull net =- arbitraryMSOutput >>= \case- rdm@(PayMulSig _ m) -> do- sigs <- map lst3 <$> vectorOf m (arbitraryTxSignature net)- return $ ScriptHashInput (SpendMulSig sigs) rdm- _ -> undefined+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 -> Gen ScriptInput-arbitraryMulSigSHInputFullC net =- arbitraryMSOutputC >>= \case- rdm@(PayMulSig _ m) -> do- sigs <- map lst3 <$> vectorOf m (arbitraryTxSignature net)- return $ ScriptHashInput (SpendMulSig sigs) rdm- _ -> undefined+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
@@ -1,24 +1,29 @@-{- |-Module : Haskoin.Test.Transaction-Copyright : No rights reserved-License : MIT-Maintainer : jprupp@protonmail.ch-Stability : experimental-Portability : POSIX--}+{-# 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 qualified Data.ByteString as BS+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.Constants-import Haskoin.Data-import Haskoin.Keys.Common+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@@ -27,10 +32,10 @@ -- | Wrapped coin value for testing. newtype TestCoin = TestCoin {getTestCoin :: Word64}- deriving (Eq, Show)+ deriving (Eq, Show) instance Coin TestCoin where- coinValue = getTestCoin+ coinValue = getTestCoin -- | Arbitrary transaction hash (for non-existent transaction). arbitraryTxHash :: Gen TxHash@@ -38,244 +43,241 @@ -- | Arbitrary amount of Satoshi as 'Word64' (Between 1 and 21e14) arbitrarySatoshi :: Network -> Gen TestCoin-arbitrarySatoshi net = TestCoin <$> choose (1, getMaxSatoshi net)+arbitrarySatoshi net = TestCoin <$> choose (1, net.maxSatoshi) -- | Arbitrary 'OutPoint'. arbitraryOutPoint :: Gen OutPoint arbitraryOutPoint = OutPoint <$> arbitraryTxHash <*> arbitrary -- | Arbitrary 'TxOut'.-arbitraryTxOut :: Network -> Gen TxOut-arbitraryTxOut net =- TxOut <$> (getTestCoin <$> arbitrarySatoshi net)- <*> (encodeOutputBS <$> arbitraryScriptOutput net)+arbitraryTxOut :: Network -> Ctx -> Gen TxOut+arbitraryTxOut net ctx =+ TxOut+ <$> (getTestCoin <$> arbitrarySatoshi net)+ <*> (marshal ctx <$> arbitraryScriptOutput net ctx) -- | Arbitrary 'TxIn'.-arbitraryTxIn :: Network -> Gen TxIn-arbitraryTxIn net =- TxIn <$> arbitraryOutPoint- <*> (encodeInputBS <$> arbitraryScriptInput net)- <*> arbitrary+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 -> Gen Tx-arbitraryTx net = oneof [arbitraryLegacyTx net, arbitraryWitnessTx net]+arbitraryTx :: Network -> Ctx -> Gen Tx+arbitraryTx net ctx =+ oneof [arbitraryLegacyTx net ctx, arbitraryWitnessTx net ctx] -- | Arbitrary regular transaction.-arbitraryLegacyTx :: Network -> Gen Tx-arbitraryLegacyTx net = arbitraryWLTx net False+arbitraryLegacyTx :: Network -> Ctx -> Gen Tx+arbitraryLegacyTx net ctx = arbitraryWLTx net ctx False -- | Arbitrary witness transaction (witness data is fake).-arbitraryWitnessTx :: Network -> Gen Tx-arbitraryWitnessTx net = arbitraryWLTx net True+arbitraryWitnessTx :: Network -> Ctx -> Gen Tx+arbitraryWitnessTx net ctx = arbitraryWLTx net ctx True -- | Arbitrary witness or legacy transaction.-arbitraryWLTx :: Network -> Bool -> Gen Tx-arbitraryWLTx net wit = do- ni <- choose (1, 5)- no <- choose (1, 5)- 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+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 -> Gen Tx-arbitraryAddrOnlyTx net = do- ni <- choose (1, 5)- no <- choose (1, 5)- inps <- vectorOf ni (arbitraryAddrOnlyTxIn net)- outs <- vectorOf no (arbitraryAddrOnlyTxOut net)- Tx <$> arbitrary <*> pure inps <*> pure outs <*> pure [] <*> 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 -> Gen Tx-arbitraryAddrOnlyTxFull net = do- ni <- choose (1, 5)- no <- choose (1, 5)- inps <- vectorOf ni (arbitraryAddrOnlyTxInFull net)- outs <- vectorOf no (arbitraryAddrOnlyTxOut net)- Tx <$> arbitrary <*> pure inps <*> pure outs <*> pure [] <*> arbitrary+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 -> Gen TxIn-arbitraryAddrOnlyTxIn net = do- inp <- oneof [arbitraryPKHashInput net, arbitraryMulSigSHInput net]- TxIn <$> arbitraryOutPoint <*> pure (encodeInputBS inp) <*> 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 -> Gen TxIn-arbitraryAddrOnlyTxInFull net = do- inp <-- oneof [arbitraryPKHashInputFullC net, arbitraryMulSigSHInputFullC net]- TxIn <$> arbitraryOutPoint <*> pure (encodeInputBS inp) <*> arbitrary+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 -> Gen TxOut-arbitraryAddrOnlyTxOut net = do- v <- getTestCoin <$> arbitrarySatoshi net- out <- oneof [arbitraryPKHashOutput, arbitrarySHOutput]- return $ TxOut v $ encodeOutputBS out+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 -> Gen (SigInput, [SecKeyI])-arbitrarySigInput net =- oneof- [ wrapKey <$> arbitraryPKSigInput net- , wrapKey <$> arbitraryPKHashSigInput net- , arbitraryMSSigInput net- , arbitrarySHSigInput net- , wrapKey <$> arbitraryWPKHSigInput net- , arbitraryWSHSigInput net- ]+-- | 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 -> Gen (SigInput, SecKeyI)-arbitraryPKSigInput net = arbitraryAnyInput net False+arbitraryPKSigInput :: Network -> Ctx -> Gen (SigInput, PrivateKey)+arbitraryPKSigInput net ctx = arbitraryAnyInput net ctx False -- | Arbitrary 'SigInput' with a 'ScriptOutput' of type 'PayPKHash'.-arbitraryPKHashSigInput :: Network -> Gen (SigInput, SecKeyI)-arbitraryPKHashSigInput net = arbitraryAnyInput net True+arbitraryPKHashSigInput :: Network -> Ctx -> Gen (SigInput, PrivateKey)+arbitraryPKHashSigInput net ctx = arbitraryAnyInput net ctx 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)+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)+ 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)+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 -> 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)+-- | 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 -> 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)+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 -> 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)+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 -> 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 '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 -> 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+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 -> 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)+ 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 tx [sigi] [prv]+ 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- 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- )- ]+ (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, SecKeyI) -> (SigInput, [SecKeyI])+wrapKey :: (SigInput, PrivateKey) -> (SigInput, [PrivateKey]) wrapKey (s, k) = (s, [k])
src/Haskoin/Util/Arbitrary/Util.hs view
@@ -1,16 +1,16 @@ {-# 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,+-- |+-- 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,@@ -19,37 +19,40 @@ arbitraryMaybe, arbitraryNetwork, arbitraryUTCTime,- SerialBox (..),- JsonBox (..),- NetBox (..), ReadBox (..),+ JsonBox (..),+ MarshalJsonBox (..),+ SerialBox (..),+ MarshalBox (..),+ IdentityTests (..), testIdentity, testSerial, testRead, testJson,- testNetJson,- arbitraryNetData,- genNetData,-) where+ )+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 qualified Data.ByteString.Short as BSS 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.Constants-import Haskoin.Data+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@@ -81,16 +84,16 @@ -- | 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+ 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)- ]+ frequency+ [ (1, return Nothing),+ (5, Just <$> g)+ ] -- | Generate an Network arbitraryNetwork :: Gen Network@@ -98,117 +101,148 @@ -- Helpers for creating Serial and JSON Identity tests -data SerialBox- = forall a.- (Show a, Eq a, T.Typeable a, Serial a) =>- SerialBox (Gen a)+instance Show Ctx where+ show _ = "Ctx" data ReadBox- = forall a.- (Read a, Show a, Eq a, T.Typeable a) =>- ReadBox (Gen a)+ = 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)+ = forall a.+ (Show a, Eq a, T.Typeable a, A.ToJSON a, A.FromJSON a) =>+ JsonBox (Gen a) -data NetBox- = forall a.- (Show a, Eq a, T.Typeable a) =>- NetBox- ( Network -> a -> A.Value- , Network -> a -> A.Encoding- , Network -> A.Value -> A.Parser a- , Gen (Network, a)- )+data MarshalJsonBox+ = forall s a.+ (Show a, Show s, Eq a, T.Typeable a, MarshalJSON s a) =>+ MarshalJsonBox (Gen (s, a)) -testIdentity :: [SerialBox] -> [ReadBox] -> [JsonBox] -> [NetBox] -> Spec-testIdentity serialVals readVals jsonVals netVals = do- describe "Binary Encoding" $- forM_ serialVals $ \(SerialBox g) -> testSerial g- describe "Read/Show Encoding" $- forM_ readVals $ \(ReadBox g) -> testRead g- describe "Data.Aeson Encoding" $- forM_ jsonVals $ \(JsonBox g) -> testJson g- describe "Data.Aeson Encoding with Network" $- forM_ netVals $ \(NetBox (j, e, p, g)) -> testNetJson j e p g+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+ (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+ 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 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+-- | 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 a -> Proxy a+ 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+ (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)+ 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)+ (A.decode . A.encodingToLazyByteString . A.toEncoding) (toMap x)+ == Just (toMap x) --- | Generate Data.Aeson identity tests for type that need the @Network@-testNetJson ::- (Eq a, Show a, T.Typeable a) =>- (Network -> a -> A.Value) ->- (Network -> a -> A.Encoding) ->- (Network -> A.Value -> A.Parser a) ->- Gen (Network, a) ->- Spec-testNetJson j e p g = do- prop ("Data.Aeson toJSON/fromJSON identity (with network) for " <> name) $- forAll g $ \(net, x) -> dec net (encVal net x) `shouldBe` Just x- prop ("Data.Aeson toEncoding/fromJSON identity (with network) for " <> name) $- forAll g $ \(net, x) -> dec net (encEnc net x) `shouldBe` Just 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- encVal net = A.encode . toMap . j net- encEnc net = A.encodingToLazyByteString . toMapE . e net- dec net = A.parseMaybe (p net) . fromMap <=< A.decode- name = show $ T.typeRep $ proxy j- proxy :: (Network -> a -> A.Value) -> Proxy a+ name = show $ T.typeRep $ proxy gen+ proxy :: Gen (s, a) -> Proxy a proxy = const Proxy--arbitraryNetData :: Arbitrary a => Gen (Network, a)-arbitraryNetData = do- net <- arbitraryNetwork- x <- arbitrary- return (net, x)--genNetData :: Gen a -> Gen (Network, a)-genNetData gen = do- net <- arbitraryNetwork- x <- gen- return (net, x)+ 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"
+ 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
test/Haskoin/Address/Bech32Spec.hs view
@@ -1,18 +1,17 @@+{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE OverloadedStrings #-} -module Haskoin.Address.Bech32Spec (- spec,-) where+module Haskoin.Address.Bech32Spec (spec) where import Control.Monad import Data.Bits (xor) import Data.ByteString (ByteString)-import qualified Data.ByteString as B+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 qualified Data.Text as T+import Data.Text qualified as Text import Data.Word (Word8) import Haskoin.Address import Haskoin.Address.Bech32@@ -21,238 +20,225 @@ 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") $- map (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") $- map (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" []+ 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) = 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 enc' resultHRP resultData- expectedChecksum = Just $ T.toLower checksum- assertEqual- (show checksum ++ " re-encode")- expectedChecksum- checksumEncoded+ 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)+ 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 (T.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)+ 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)+ 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+ 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"- )- ]+ [ ( 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"- ]+ [ " 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"- )- ]+ [ ( "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"- ]+ [ "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 T.pack hrpTestPermutations+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]+ a <- ['t', 'T']+ b <- ['e', 'E']+ c <- ['s', 'S']+ d <- ['t', 'T']+ return [a, b, c, d]
test/Haskoin/Address/CashAddrSpec.hs view
@@ -1,347 +1,315 @@+{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE OverloadedStrings #-} module Haskoin.Address.CashAddrSpec (spec) where import Control.Monad-import qualified Data.ByteString.Char8 as C+import Data.ByteString.Char8 qualified as Char8 import Data.Maybe import Data.String.Conversions import Data.Text (Text) import Haskoin.Address-import Haskoin.Constants+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+ 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 (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+ 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.--}+-- | 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"- )- ]+ [ ( 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
@@ -1,16 +1,18 @@+{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE OverloadedStrings #-} module Haskoin.AddressSpec (spec) where import Data.ByteString (ByteString)-import qualified Data.ByteString as BS (append, empty, pack)+import Data.ByteString qualified as B+import Data.Default (def) import Data.Maybe (fromJust, isJust) import Data.Text (Text)-import qualified Data.Text as T+import Data.Text qualified as T import Haskoin.Address-import Haskoin.Constants-import Haskoin.Data-import Haskoin.Keys+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Network.Data import Haskoin.Util import Haskoin.Util.Arbitrary import Test.HUnit@@ -18,186 +20,178 @@ import Test.Hspec.QuickCheck import Test.QuickCheck -serialVals :: [SerialBox]-serialVals = [SerialBox arbitraryAddressAll]--readVals :: [ReadBox]-readVals = [ReadBox arbitraryAddressAll]--netVals :: [NetBox]-netVals =- [NetBox (addrToJSON, addrToEncoding, addrFromJSON, arbitraryNetAddress)]+identityTests :: IdentityTests+identityTests =+ def+ { readTests = [ReadBox arbitraryAddressAll],+ serialTests = [SerialBox arbitraryAddressAll],+ marshalJsonTests = [MarshalJsonBox arbitraryNetAddress]+ } spec :: Spec-spec = do- testIdentity serialVals readVals [] netVals- 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 (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 compatWitnessVectors+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)+ 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]- , "1111cWB5HCBdLjAuqGGReWE3R3CguuwSjw6RHn39s2yuDRTS5N\- \sBgNiFpWgAnEx6VQi8csexkgYw3mdYrMHr8x9i7aEwP8kZ7vcc\- \XWqKDvGv3u1GxFKPuAkn8JCPPGDMf3vMMnbzm6Nh9zh1gcNsMv\- \H3ZNLmP5fSG6DGbbi2tuwMWPthr4boWwCxf7ewSgNQeacyozhK\- \DDQQ1qL5fQFUW52QKUZDZ5fw3KXNQJMcNTcaB723LchjeKun7M\- \uGW5qyCBZYzA1KjofN1gYBV3NqyhQJ3Ns746GNuf9N2pQPmHz4\- \xpnSrrfCvy6TVVz5d4PdrjeshsWQwpZsZGzvbdAdN8MKV5QsBDY"- , "111151KWPPBRzdWPr1ASeu172gVgLf1YfUp6VJyk6K9t4cLqYt\- \FHcMa2iX8S3NJEprUcW7W5LvaPRpz7UG7puBj5STE3nKhCGt5e\- \ckYq7mMn5nT7oTTic2BAX6zDdqrmGCnkszQkzkz8e5QLGDjf7K\- \eQgtEDm4UER6DMSdBjFQVa6cHrrJn9myVyyhUrsVnfUk2WmNFZ\- \vkWv3Tnvzo2cJ1xW62XDfUgYz1pd97eUGGPuXvDFfLsBVd1dfd\- \UhPwxW7pMPgdWHTmg5uqKGFF6vE4xXpAqZTbTxRZjCDdTn68c2\- \wrcxApm8hq3JX65Hix7VtcD13FF8b7BzBtwjXq1ze6NMjKgUcq\- \pJTN9vt"- )- ]+ [ (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+ 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"- )- ]+ [ ("", ""),+ ("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)+ 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)+ [ ("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)+ assertEqual+ "decodeBase58Check invalid match"+ resM+ (encodeHex <$> decodeBase58Check a) base58ChkInvalidVectors :: [(Text, Maybe Text)] base58ChkInvalidVectors =- [ ("3vQB7B6MrGQZaxCuFg4oh", Just "68656c6c6f20776f726c64")- , ("3vQB7B6MrGQZaxCuFg4oi", Nothing)- , ("3vQB7B6MrGQZaxCuFg4oh0IOl", Nothing)- , ("3vQB7B6MrGQZaxCuFg4oh\00IOl", Nothing)- ]+ [ ("3vQB7B6MrGQZaxCuFg4oh", Just "68656c6c6f20776f726c64"),+ ("3vQB7B6MrGQZaxCuFg4oi", Nothing),+ ("3vQB7B6MrGQZaxCuFg4oh0IOl", Nothing),+ ("3vQB7B6MrGQZaxCuFg4oh\00IOl", Nothing)+ ] -testCompatWitnessVector :: (Network, Text, Text) -> Assertion-testCompatWitnessVector (net, seckey, addr) = do- let seckeyM = fromWif net seckey- assertBool "decode seckey" (isJust seckeyM)- let pubkey = derivePubKeyI (fromJust seckeyM)- let addrM = addrToText btcTest (pubKeyCompatWitnessAddr pubkey)- assertBool "address can be encoded" (isJust addrM)- assertEqual "witness address matches" addr (fromJust addrM)+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"- )- ]+ [ ( btcTest,+ "cNUnpYpMsJXYCERYBciJnsWBpcYEFjdcbq6dxj4SskGhs7uHuJ7Q",+ "2N6PDTueBHvXzW61B4oe5SW1D3v2Z3Vpbvw"+ )+ ]
test/Haskoin/BlockSpec.hs view
@@ -1,10 +1,15 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} -module Haskoin.BlockSpec (- spec,-) where+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)@@ -12,9 +17,11 @@ import Data.Text (Text) import Data.Word (Word32) import Haskoin.Block-import Haskoin.Constants-import Haskoin.Data+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@@ -22,36 +29,36 @@ import Test.QuickCheck import Text.Printf (printf) -serialVals :: [SerialBox]-serialVals =- [ SerialBox (arbitraryBlock =<< arbitraryNetwork)- , SerialBox arbitraryBlockHash- , SerialBox arbitraryBlockHeader- , SerialBox arbitraryGetBlocks- , SerialBox arbitraryGetHeaders- , SerialBox arbitraryHeaders- , SerialBox arbitraryMerkleBlock- , SerialBox arbitraryBlockNode- ]--readVals :: [ReadBox]-readVals =- [ ReadBox (arbitraryBlock =<< arbitraryNetwork)- , ReadBox arbitraryBlockHash- , ReadBox arbitraryBlockHeader- , ReadBox arbitraryGetBlocks- , ReadBox arbitraryGetHeaders- , ReadBox arbitraryHeaders- , ReadBox arbitraryMerkleBlock- , ReadBox arbitraryBlockNode- ]--jsonVals :: [JsonBox]-jsonVals =- [ JsonBox (arbitraryBlock =<< arbitraryNetwork)- , JsonBox arbitraryBlockHash- , JsonBox arbitraryBlockHeader- ]+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@@ -59,66 +66,66 @@ withChain :: Network -> State HeaderMemory a -> a withChain net f = evalState f (initialChain net) -chain :: BlockHeaders m => Network -> BlockHeader -> Int -> m ()+chain :: (BlockHeaders m) => Network -> BlockHeader -> Int -> m () chain net bh i = do- bnsE <- connectBlocks net myTime bhs- either error (const $ return ()) bnsE+ bnsE <- connectBlocks net myTime bhs+ either error (const $ return ()) bnsE where bhs = appendBlocks net 6 bh i spec :: Spec-spec = do- testIdentity serialVals readVals jsonVals []- describe "blockchain headers" $ do- it "gets best block on bchRegTest" $- let net = bchRegTest- bb =- withChain net $ do- chain net (getGenesisHeader net) 100- getBestBlockHeader- in nodeHeight bb `shouldBe` 100- it "builds a block locator on bchRegTest" $- let net = bchRegTest- 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 on bchRegTest" $- let net = bchRegTest- bb = withChain net $ splitChain net >> getBestBlockHeader- in nodeHeight bb `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)+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 -- ↓@@ -127,40 +134,40 @@ -- → → 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)+ 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 =- unless (nodeHeight bn == n) $- error $- "Node height "- ++ show (nodeHeight bn)- ++ " of first chunk should be "- ++ show n+ 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+ 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 -} @@ -172,214 +179,208 @@ buildExtractTree :: Network -> [(TxHash, Bool)] -> Bool buildExtractTree net txs =- r == buildMerkleRoot (map fst txs) && m == map fst (filter snd 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)+ 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)+ 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)+ 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)+ 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+ -- 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+ ( "886fea311d2dc64c315519f2d647e43998d780d2170f77e53dc0d85bf2ee680c",+ [ "c9c9e5211512629fd111cc071d745b8c79bf486b4ea95489eb5de08b5d786b8e",+ "20beb0ee30dfd323ade790ce9a46ae7a174f9ea44ce22a17c4d4eb23b7016f51",+ "d4cb7dd741e78a8f57e12f6c8ddb0361ff2a5bf9365bd7d7df761060847daf9a",+ "ddbfa6fdd29d4b47aeaadf82a4bf0a93d58cd7d8401fabf860a1ae8eeb51f42e",+ "9d82bafe44abee248b968c86f165051c8413482c232659795335c52922dab471",+ "86035372d31b53efd848cea7231aa9738c209aff64d3c59b1619341afb5b6ba3",+ "11e7a7393d9658813dfaebc04fa6d4b73bac8d641bffa7067da879523d43d030",+ "2f676b9aa5bc0ebf3395032c84c466e40cac29f80434cd1138e31c2d0fcc5c13",+ "37567d559fbfae07fda9a90de0ce30b202128bc8ebdfef5ad2b53e865a3478c2",+ "0b8e6c1200c454361e94e261738429e9c9b8dcffd85ec8511bbf5dc7e2e0ada8"+ ]+ ),+ -- Block 00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048 - ( "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098"- , ["0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098"]- )- , -- Block 000000000004d160ac1f7b775d7c1823345aeadd5fcb29ca2ad2403bb7babd4c+ ( "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098",+ ["0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098"]+ ),+ -- Block 000000000004d160ac1f7b775d7c1823345aeadd5fcb29ca2ad2403bb7babd4c - ( "aae018650f513fc42d55b2210ec3ceeeb194fb1261d37989de07451fc0cbac5c"- ,- [ "a4454f22831acd7904a9902c5070a3ee4bf4c2b13bc6b2dc66735dd3c4414028"- , "45297f334278885108dd38a0b689ed95a4373dd3f7e4413e6aebdc2654fb771b"- ]- )- , -- Block 000000000001d1b13a7e86ddb20da178f20d6da5cd037a29c2a15b8b84cc774e+ ( "aae018650f513fc42d55b2210ec3ceeeb194fb1261d37989de07451fc0cbac5c",+ [ "a4454f22831acd7904a9902c5070a3ee4bf4c2b13bc6b2dc66735dd3c4414028",+ "45297f334278885108dd38a0b689ed95a4373dd3f7e4413e6aebdc2654fb771b"+ ]+ ),+ -- Block 000000000001d1b13a7e86ddb20da178f20d6da5cd037a29c2a15b8b84cc774e - ( "ca3580505feb87544760ac14a5859659e23be05f765bbed9f86a3c9aad1a5d0c"- ,- [ "60702384c6e9d34ff03c2b3e726bdc649befe603216815bd0a2974921d0d9549"- , "11f40f58941d2a81a1616a3b84b7dd8b9d07e68750827de488c11a18f54220bb"- , "d78e82527aa8cf16e375010bc666362c0258d3c0da1885a1871121706da8b633"- ]- )- , -- Block 0000000000000630a4e2266a31776e952a19b7c99a6387917d9de9032f608021+ ( "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"- ]- )- ]+ ( "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 * 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)+ 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 @@ -387,34 +388,34 @@ 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+ (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+ 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+ 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
@@ -1,16 +1,20 @@+{-# 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 qualified Data.ByteString.Char8 as C-import qualified Data.ByteString.Lazy as BL-import qualified Data.ByteString.Short as BSS+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@@ -25,53 +29,54 @@ import Test.Hspec.QuickCheck import Test.QuickCheck -serialVals :: [SerialBox]-serialVals =- [ SerialBox arbitraryBS- , SerialBox arbitraryHash160- , SerialBox arbitraryHash256- , SerialBox arbitraryHash512- ]--readVals :: [ReadBox]-readVals =- [ ReadBox arbitraryBS- , ReadBox arbitraryBSS- , ReadBox arbitraryHash160- , ReadBox arbitraryHash256- , ReadBox arbitraryHash512- ]+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 serialVals readVals [] []- 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 getHash160) ripemd160Vectors- it "Passes SHA1 test vectors" $- mapM_ (testVector sha1 getHash160) sha1Vectors- it "Passes SHA256 test vectors" $- mapM_ (testVector sha256 getHash256) sha256Vectors- it "Passes SHA512 test vectors" $- mapM_ (testVector sha512 getHash512) sha512Vectors- it "Passes HMAC_SHA256 test vectors" $- mapM_ (testHMACVector hmac256 getHash256) hmacSha256Vectors- it "Passes HMAC_SHA512 test vectors" $- mapM_ (testHMACVector hmac512 getHash512) hmacSha512Vectors+ 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)@@ -80,358 +85,317 @@ -- 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+ -- 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 -> BSS.ShortByteString) ->- (ByteString, Text) ->- Assertion+ (ByteString -> a) ->+ (a -> Short.ShortByteString) ->+ (ByteString, Text) ->+ Assertion testVector f1 f2 (i, res) =- assertEqual "Hash matches" res (encodeHex (BSS.fromShort $ f2 $ f1 i))+ assertEqual "Hash matches" res (encodeHex (Short.fromShort $ f2 $ f1 i)) testHMACVector ::- (ByteString -> ByteString -> a) ->- (a -> BSS.ShortByteString) ->- (Text, Text, Text) ->- Assertion+ (ByteString -> ByteString -> a) ->+ (a -> Short.ShortByteString) ->+ (Text, Text, Text) ->+ Assertion testHMACVector f1 f2 (k, m, res) =- assertEqual "Hash matches" res (encodeHex (BSS.fromShort $ f2 $ f1 bsK bsM))+ assertEqual "Hash matches" res (encodeHex (Short.fromShort $ f2 $ f1 bsK bsM)) where bsK = fromJust $ decodeHex k bsM = fromJust $ decodeHex m longTestString :: ByteString longTestString =- BL.toStrict $! toLazyByteString $! go [0 .. 199999]+ 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+ 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"- )- , (C.replicate 1000000 'a', "52783243c1697bdbe16d37f97f68f08325dc1528")- , (longTestString, "464243587bd146ea835cdf57bdae582f25ec45f1")- ]+ [ ("", "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"- )- , (C.replicate 1000000 'a', "34aa973cd4c4daa4f61eeb2bdbad27316534016f")- , (longTestString, "b7755760681cbfd971451668f32af5774f4656b5")- ]+ [ ("", "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"- )- ,- ( C.replicate 1000000 'a'- , "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"- )- ,- ( longTestString- , "a316d55510b49662420f49d145d42fb83f31ef8dc016aa4e32df049991a91e26"- )- ]+ [ ("", "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"- )- ,- ( C.replicate 1000000 'a'- , "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff2\- \44877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"- )- ,- ( longTestString- , "40cac46c147e6131c5193dd5f34e9d8bb4951395f27b08c558c65ff4ba2de59437de8c\- \3ef5459d76a52cedc02dc499a3c9ed9dedbfb3281afd9653b8a112fafc"- )- ]+ [ ( "",+ "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.+ [ ( "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\+ \654a6566654a6566654a6566654a6566654a6566654a6566654a6566",+ "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+ "9de4b546756c83516720a4ad7fe7bdbeac4298c6fdd82b15f895a6d10b0769a6"+ ),+ -- Test case with key length 64 bytes. - ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566\- \654a6566654a6566654a6566654a6566654a6566654a6566654a656665"- , "7768617420646f2079612077616e7420666f72206e6f7468696e673f"- , "528c609a4c9254c274585334946b7c2661bad8f1fc406b20f6892478d19163dd"- )- , -- Test case with key length 65 bytes.+ ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566\+ \654a6566654a6566654a6566654a6566654a6566654a6566654a656665",+ "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+ "528c609a4c9254c274585334946b7c2661bad8f1fc406b20f6892478d19163dd"+ ),+ -- Test case with key length 65 bytes. - ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a6566\- \654a6566654a6566654a6566654a6566654a6566654a6566654a6566654a"- , "7768617420646f2079612077616e7420666f72206e6f7468696e673f"- , "d06af337f359a2330deffb8e3cbe4b5b7aa8ca1f208528cdbd245d5dc63c4483"- )- ]+ ( "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.+ [ ( "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\+ \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",+ "7768617420646f2079612077616e7420666f72206e6f7468696e673f",+ "43aaac07bb1dd97c82c04df921f83b16a68d76815cd1a30d3455ad43a3d80484\+ \2bb35462be42cc2e4b5902de4d204c1c66d93b47d1383e3e13a3788687d61258"+ ),+ -- Test case with key length 129 bytes. - ( "4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\- \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\- \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\- \4a6566654a6566654a6566654a6566654a6566654a6566654a6566654a656665\- \4a"- , "7768617420646f2079612077616e7420666f72206e6f7468696e673f"- , "0b273325191cfc1b4b71d5075c8fcad67696309d292b1dad2cd23983a35feb8e\- \fb29795e79f2ef27f68cb1e16d76178c307a67beaad9456fac5fdffeadb16e2c"- )- ]+ ( "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
@@ -1,3 +1,5 @@+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} module Haskoin.Crypto.SignatureSpec (spec) where@@ -5,114 +7,120 @@ import Control.Monad import Data.Bits (testBit) import Data.ByteString (ByteString)-import qualified Data.ByteString as BS+import Data.ByteString qualified as BS import Data.Map.Strict (Map)-import qualified Data.Map.Strict as 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.Constants import Haskoin.Crypto-import Haskoin.Keys+import Haskoin.Network.Constants import Haskoin.Script import Haskoin.Transaction import Haskoin.Util import Haskoin.Util.Arbitrary-import Haskoin.UtilSpec (readTestFile) import Test.HUnit import Test.Hspec import Test.Hspec.QuickCheck import Test.QuickCheck spec :: Spec-spec = do- describe "Signature properties" $ do- prop "verify signature" $- forAll arbitrarySignature $ \(m, key', sig) ->- verifyHashSig m sig (derivePubKey key')- prop "s component less than half order" $- forAll arbitrarySignature $ isCanonicalHalfOrder . lst3- prop "encoded signature is canonical" $- forAll arbitrarySignature $ testIsCanonical . lst3- prop "decodeStrictSig . exportSig identity" $- forAll arbitrarySignature $- (\s -> decodeStrictSig (exportSig s) == Just s) . lst3- prop "importSig . exportSig identity" $- forAll arbitrarySignature $- (\s -> importSig (exportSig s) == Just s) . lst3- prop "getSig . putSig identity" $- forAll arbitrarySignature $- (\s -> runGet getSig (runPut $ putSig s) == Right s) . lst3- describe "Signature vectors" $- checkDistSig $ \file1 file2 -> do- vectors <- runIO (readTestFile file1 :: IO [(Text, Text, Text)])- vectorsDER <- runIO (readTestFile file2 :: IO [(Text, Text, Text)])- it "Passes the trezor rfc6979 test vectors" $- mapM_ (testRFC6979Vector . toVector) vectors- it "Passes the rfc6979 DER test vectors" $- mapM_ (testRFC6979DERVector . toVector) vectorsDER- describe "BIP143 signature vectors" $ do- 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+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 :: 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)- )+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 sig+ s = exportSig ctx sig len = fromIntegral $ BS.length s rlen = BS.index s 3 slen = BS.index s (fromIntegral rlen + 5)@@ -123,49 +131,46 @@ -- between implementations. We check the output of signMsg 1 0 data ValidImpl- = ImplCore- | ImplABC+ = ImplCore+ | ImplCash -implSig :: Text-implSig =- encodeHex $- exportSig $- signMsg- "0000000000000000000000000000000000000000000000000000000000000001"- "0000000000000000000000000000000000000000000000000000000000000000"+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"- , ImplABC- )- ]+ Map.fromList+ [ ( "3045022100a0b37f8fba683cc68f6574cd43b39f0343a50008bf6ccea9d13231\+ \d9e7e2e1e4022011edc8d307254296264aebfc3dc76cd8b668373a072fd64665\+ \b50000e9fcce52",+ ImplCore+ ),+ ( "304402200581361d23e645be9e3efe63a9a2ac2e8dd0c70ba3ac8554c9befe06\+ \0ad0b36202207d8172f1e259395834793d81b17e986f1e6131e4734969d2f4ae\+ \3a9c8bc42965",+ ImplCash+ )+ ] -getImpl :: Maybe ValidImpl-getImpl = implSig `Map.lookup` validImplMap+getImpl :: Ctx -> Maybe ValidImpl+getImpl ctx = implSig ctx `Map.lookup` validImplMap rfc6979files :: ValidImpl -> (FilePath, FilePath) rfc6979files ImplCore = ("rfc6979core.json", "rfc6979DERcore.json")-rfc6979files ImplABC = ("rfc6979abc.json", "rfc6979DERabc.json")+rfc6979files ImplCash = ("rfc6979cash.json", "rfc6979DERcash.json") -checkDistSig :: (FilePath -> FilePath -> Spec) -> Spec-checkDistSig go =- case rfc6979files <$> getImpl of- Just (file1, file2) -> go file1 file2- _ ->- it "Passes rfc6979 test vectors" $- void $ assertFailure "Invalid rfc6979 signature"+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@@ -173,272 +178,272 @@ toVector :: (Text, Text, Text) -> (SecKey, ByteString, Text) toVector (prv, m, res) = (fromJust $ (secKey <=< decodeHex) prv, cs m, res) -testRFC6979Vector :: (SecKey, ByteString, Text) -> Assertion-testRFC6979Vector (prv, m, res) = do- assertEqual "RFC 6979 Vector" res (encodeHex $ encode $ exportCompactSig s)- assertBool "Signature is valid" $ verifyHashSig h s (derivePubKey prv)- assertBool "Signature is canonical" $ testIsCanonical s- assertBool "Signature is normalized" $ isCanonicalHalfOrder s+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 prv h+ 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 :: (SecKey, ByteString, Text) -> Assertion-testRFC6979DERVector (prv, m, res) = do- assertEqual "RFC 6979 DER Vector" res (encodeHex $ exportSig s)- assertBool "DER Signature is valid" $ verifyHashSig h s (derivePubKey prv)- assertBool "DER Signature is canonical" $ testIsCanonical s- assertBool "DER Signature is normalized" $ isCanonicalHalfOrder s+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 prv h+ s = signHash ctx prv h -- Reproduce the P2WPKH example from BIP 143-testBip143p2wpkh :: Assertion-testBip143p2wpkh =- case getImpl of- Just ImplCore ->- assertEqual "BIP143 Core p2wpkh" (Right signedTxCore) generatedSignedTx- Just ImplABC ->- assertEqual "BIP143 ABC p2wpkh" (Right signedTxABC) generatedSignedTx- Nothing -> assertFailure "Invalid secp256k1 library"+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"- signedTxABC =- "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433\- \541db4e4ad969f000000004847304402200fbc9dad97500334e47c2dca50096a\- \2117c01952c2870102e320823d21c36229022007cb36c2b141d11c08ef81d948\- \f148332fc09fe8f6d226aaaf8ba6ae0d8a66ba01eeffffffef51e1b804cc89d1\- \82d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffff\- \ff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a\- \6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f016\- \7faa815988ac0002473044022011cb891cee521eb1fc7aef681655a881288553\- \fc024cff9cee5007bae5e6b8c602200b89d60ee2f98aa9a645dad59cd680b4b6\- \25f343efcd3e7fb70852100ef601890121025476c2e83188368da1ff3e292e7a\- \cafcdb3566bb0ad253f62fc70f07aeee635711000000"+ "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"+ "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541d\+ \b4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b\+ \1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb20600000000\+ \1976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d0000\+ \00001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000" Just key0 =- secHexKey- "bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866"- pubKey0 = toPubKey key0+ secHexKey+ "bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866"+ pubKey0 = toPubKey ctx key0 Just key1 =- secHexKey- "619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9"- [op0, op1] = prevOutput <$> txIn unsignedTx+ secHexKey+ "619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9"+ [op0, op1] = (.outpoint) <$> unsignedTx.inputs sigIn0 = SigInput (PayPK pubKey0) 625000000 op0 sigHashAll Nothing- WitnessPubKeyAddress h = pubKeyWitnessAddr $ toPubKey key1+ WitnessPubKeyAddress h = pubKeyWitnessAddr ctx $ toPubKey ctx key1 sigIn1 = SigInput (PayWitnessPKHash h) 600000000 op1 sigHashAll Nothing- generatedSignedTx = signTx btc unsignedTx [sigIn0, sigIn1] [key0, key1]+ generatedSignedTx = signTx btc ctx unsignedTx [sigIn0, sigIn1] [key0, key1] -- Reproduce the P2SH-P2WPKH example from BIP 143-testBip143p2shp2wpkh :: Assertion-testBip143p2shp2wpkh =- case getImpl of- Just ImplCore ->- assertEqual "BIP143 Core p2sh-p2wpkh" (Right signedTxCore) generatedSignedTx- Just ImplABC ->- assertEqual "BIP143 ABC p2sh-p2wpkh" (Right signedTxABC) generatedSignedTx- Nothing -> assertFailure "Invalid secp256k1 library"+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"- signedTxABC =- "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092\- \ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009b\- \df0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bb\- \c043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fe\- \a7ad0402e8bd8ad6d77c88ac024730440220091c78fd1e21535f6ddc45515e4c\- \afca15cdf344765d72c1529fb82d3ada2d1802204a980d5e37d0b04f5e1185a0\- \f97295c383764e9a4b08d8bd1161b33c6719139a012103ad1d8e89212f0b92c7\- \4d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000"+ "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092\+ \ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009b\+ \df0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bb\+ \c043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fe\+ \a7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a1\+ \0d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d\+ \877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c7\+ \4d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000"+ signedTxCash =+ "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092\+ \ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009b\+ \df0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bb\+ \c043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fe\+ \a7ad0402e8bd8ad6d77c88ac024730440220091c78fd1e21535f6ddc45515e4c\+ \afca15cdf344765d72c1529fb82d3ada2d1802204a980d5e37d0b04f5e1185a0\+ \f97295c383764e9a4b08d8bd1161b33c6719139a012103ad1d8e89212f0b92c7\+ \4d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000" unsignedTx =- "0100000001db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d\- \3ceb1a54770100000000feffffff02b8b4eb0b000000001976a914a457b684d7\- \f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b\- \1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac92040000"+ "0100000001db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d\+ \3ceb1a54770100000000feffffff02b8b4eb0b000000001976a914a457b684d7\+ \f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b\+ \1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac92040000" Just key0 =- secHexKey- "eb696a065ef48a2192da5b28b694f87544b30fae8327c4510137a922f32c6dcf"- op0 = prevOutput . head $ txIn unsignedTx- WitnessPubKeyAddress h = pubKeyWitnessAddr $ toPubKey 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 unsignedTx [sigIn0] [key0]+ generatedSignedTx = signNestedWitnessTx btc ctx unsignedTx [sigIn0] [key0] -- P2WSH multisig example (tested against bitcoin-core 0.19.0.1)-testP2WSHMulsig :: Assertion-testP2WSHMulsig =- case getImpl of- Just ImplCore ->- assertEqual "Core p2wsh multisig" (Right signedTxCore) generatedSignedTx- Just ImplABC ->- assertEqual "ABC p2wsh multisig" (Right signedTxABC) generatedSignedTx- Nothing -> assertFailure "Invalid secp256k1 library"+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"- signedTxABC =- "01000000000101d2e34df5d7ee565208eddd231548916b9b0e99f4f5071f8961\- \34a448c5fb07bf0100000000ffffffff01f0b9f505000000001976a9143d5a35\- \2cab583b12fbcb26d1269b4a2c951a33ad88ac0400483045022100b79bf3714a\- \50f8f0e2f946034361ba4f6567b796d55910d89e98720d2e99f98c0220134879\- \518002df23e80a058475fa8b10bc4182bedfecd5f85e446a00f211ea53014830\- \45022100ce3c77480d664430a7544c1a962d1ae31151109a528a37e5bccc92ba\- \2e460ad10220317bc9a71d0c3471058d16d4c3b1ea99616208db6b9b9040fb81\- \0a7fa27f72b40169522102ba46d3bb8db74c77c6cf082db57fc0548058fcdea8\- \11549e186526e3d10caf6721038ac8aef2dd9cea5e7d66e2f6e23f177a6c21f6\- \9ea311fa0c85d81badb6b37ceb2103d96d2bfbbc040faaf93491d69e2bfe9695\- \e2d8e007a7f26db96c2ee42db15dc953ae00000000"+ "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 $ prevOutput <$> txIn unsignedTx+ "0100000001d2e34df5d7ee565208eddd231548916b9b0e99f4f5071f896134a4\+ \48c5fb07bf0100000000ffffffff01f0b9f505000000001976a9143d5a352cab\+ \583b12fbcb26d1269b4a2c951a33ad88ac00000000"+ op0 = (head unsignedTx.inputs).outpoint Just keys =- traverse- secHexKey- [ "3030303030303030303030303030303030303030303030303030303030303031"- , "3030303030303030303030303030303030303030303030303030303030303032"- , "3030303030303030303030303030303030303030303030303030303030303033"- ]- rdm = PayMulSig (toPubKey <$> keys) 2+ traverse+ secHexKey+ [ "3030303030303030303030303030303030303030303030303030303030303031",+ "3030303030303030303030303030303030303030303030303030303030303032",+ "3030303030303030303030303030303030303030303030303030303030303033"+ ]+ rdm = PayMulSig (toPubKey ctx <$> keys) 2 sigIn =- SigInput- (toP2WSH $ encodeOutput rdm)- 100000000- op0- sigHashAll- (Just rdm)- generatedSignedTx = signTx btc unsignedTx [sigIn] (take 2 keys)+ 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 :: Assertion-testBip143p2shp2wpkhMulsig =- case getImpl of- Just ImplCore ->- assertEqual- "BIP143 Core p2sh-p2wsh multisig"- (Right signedTxCore)- generatedSignedTx- Just ImplABC ->- assertEqual- "BIP143 Core p2sh-p2wsh multisig"- (Right signedTxABC)- generatedSignedTx- Nothing -> assertFailure "Invalid secp256k1 library"+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"- signedTxABC =- "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"+ "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 $ prevOutput <$> txIn unsignedTx+ "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1c\+ \a29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd\+ \9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a3\+ \3f950689af511e6e84c138dbbd3c3ee41588ac00000000"+ op0 = (head unsignedTx.inputs).outpoint rawKeys =- [ "730fff80e1413068a05b57d6a58261f07551163369787f349438ea38ca80fac6"- , "11fa3d25a17cbc22b29c44a484ba552b5a53149d106d3d853e22fdd05a2d8bb3"- , "77bf4141a87d55bdd7f3cd0bdccf6e9e642935fec45f2f30047be7b799120661"- , "14af36970f5025ea3e8b5542c0f8ebe7763e674838d08808896b63c3351ffe49"- , "fe9a95c19eef81dde2b95c1284ef39be497d128e2aa46916fb02d552485e0323"- , "428a7aee9f0c2af0cd19af3cf1c78149951ea528726989b2e83e4778d2c3f890"- ]+ [ "730fff80e1413068a05b57d6a58261f07551163369787f349438ea38ca80fac6",+ "11fa3d25a17cbc22b29c44a484ba552b5a53149d106d3d853e22fdd05a2d8bb3",+ "77bf4141a87d55bdd7f3cd0bdccf6e9e642935fec45f2f30047be7b799120661",+ "14af36970f5025ea3e8b5542c0f8ebe7763e674838d08808896b63c3351ffe49",+ "fe9a95c19eef81dde2b95c1284ef39be497d128e2aa46916fb02d552485e0323",+ "428a7aee9f0c2af0cd19af3cf1c78149951ea528726989b2e83e4778d2c3f890"+ ] Just keys = traverse secHexKey rawKeys- rdm = PayMulSig (toPubKey <$> keys) 6- sigIn sh = SigInput (toP2WSH $ encodeOutput rdm) 987654321 op0 sh (Just rdm)+ rdm = PayMulSig (toPubKey ctx <$> keys) 6+ sigIn sh = SigInput (toP2WSH $ encodeOutput ctx rdm) 987654321 op0 sh (Just rdm) sigHashesA = [sigHashAll, sigHashNone, sigHashSingle]- sigHashesB = setAnyoneCanPayFlag <$> sigHashesA+ sigHashesB = setAnyoneCanPay <$> sigHashesA sigIns = sigIn <$> (sigHashesA <> sigHashesB) generatedSignedTx = foldM addSig unsignedTx $ zip sigIns keys- addSig tx (sigIn', key') = signNestedWitnessTx btc tx [sigIn'] [key']+ addSig tx (sigIn', key') = signNestedWitnessTx btc ctx tx [sigIn'] [key'] secHexKey :: Text -> Maybe SecKey secHexKey = decodeHex >=> secKey -toPubKey :: SecKey -> PubKeyI-toPubKey = derivePubKeyI . wrapSecKey True+toPubKey :: Ctx -> SecKey -> PublicKey+toPubKey ctx = derivePublicKey ctx . wrapSecKey True
− test/Haskoin/Keys/ExtendedSpec.hs
@@ -1,621 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}--module Haskoin.Keys.ExtendedSpec (spec) where--import Control.Monad (forM_)-import Data.Aeson as A-import Data.Bits ((.&.))-import qualified Data.ByteString.Lazy.Char8 as B8-import Data.Bytes.Get-import Data.Bytes.Put-import Data.Bytes.Serial-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.Constants-import Haskoin.Keys-import Haskoin.Util-import Haskoin.Util.Arbitrary-import Haskoin.UtilSpec (customCerealID)-import Test.HUnit (Assertion, assertBool, assertEqual)-import Test.Hspec-import Test.Hspec.QuickCheck-import Test.QuickCheck hiding ((.&.))--serialVals :: [SerialBox]-serialVals =- [ SerialBox arbitraryDerivPath- , SerialBox arbitraryHardPath- , SerialBox arbitrarySoftPath- ]--readVals :: [ReadBox]-readVals =- [ ReadBox arbitraryDerivPath- , ReadBox arbitraryHardPath- , ReadBox arbitrarySoftPath- , ReadBox arbitraryXPrvKey- , ReadBox (snd <$> arbitraryXPubKey)- , ReadBox arbitraryParsedPath- , ReadBox arbitraryBip32PathIndex- ]--jsonVals :: [JsonBox]-jsonVals =- [ JsonBox arbitraryDerivPath- , JsonBox arbitraryHardPath- , JsonBox arbitrarySoftPath- , JsonBox arbitraryParsedPath- ]--netVals :: [NetBox]-netVals =- [ NetBox- ( xPrvToJSON- , xPrvToEncoding- , xPrvFromJSON- , genNetData arbitraryXPrvKey- )- , NetBox- ( xPubToJSON- , xPubToEncoding- , xPubFromJSON- , genNetData (snd <$> arbitraryXPubKey)- )- ]--spec :: Spec-spec = do- testIdentity serialVals readVals jsonVals netVals- describe "Custom identity tests" $ do- prop "encodes and decodes extended private key" $- forAll arbitraryNetwork $ \net ->- forAll arbitraryXPrvKey $- customCerealID (getXPrvKey net) (putXPrvKey net)- prop "encodes and decodes extended public key" $- forAll arbitraryNetwork $ \net ->- forAll arbitraryXPubKey $- customCerealID (getXPubKey net) (putXPubKey net) . snd- describe "bip32 subkey derivation vector 1" $ vectorSpec m1 vector1- describe "bip32 subkey derivation vector 2" $ vectorSpec m2 vector2- describe "bip32 subkey derivation vector 3" $ vectorSpec m3 vector3- 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 "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- 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 $ \(_, k) ->- xPubImport net (xPubExport net k) == Just k--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--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---- BIP 0032 Test Vectors--- https://en.bitcoin.it/wiki/BIP_0032_TestVectors--bip44Addr :: DerivPath-bip44Addr = Deriv :| 44 :| 0 :| 0 :/ 0 :/ 0--vectorSpec :: TestKey -> [TestVector] -> Spec-vectorSpec mTxt vecTxt =- forM_ (parseVector mTxt vecTxt) $ \(d, m, v) ->- it ("chain " <> cs d) $ runVector m v--runVector :: XPrvKey -> TestVector -> Assertion-runVector m v = do- assertBool "xPrvID" $ encodeHex (runPutS . serialize $ xPrvID m) == v !! 0- assertBool "xPrvFP" $ encodeHex (runPutS . serialize $ xPrvFP m) == v !! 1- assertBool "xPrvAddr" $- addrToText btc (xPubAddr $ deriveXPubKey m) == Just (v !! 2)- assertBool "bip44Addr" $- addrToText btc (xPubAddr $ deriveXPubKey $ derivePath bip44Addr m)- == Just (v !! 3)- assertBool "prvKey" $ encodeHex (getSecKey $ xPrvKey m) == v !! 4- assertBool "xPrvWIF" $ xPrvWif btc m == v !! 5- assertBool "pubKey" $- encodeHex (exportPubKey True $ xPubKey $ deriveXPubKey m) == v !! 6- assertBool "chain code" $ encodeHex (runPutS . serialize $ xPrvChain m) == v !! 7- assertBool "Hex PubKey" $- encodeHex (runPutS $ putXPubKey btc $ deriveXPubKey m) == v !! 8- assertBool "Hex PrvKey" $ encodeHex (runPutS (putXPrvKey btc m)) == v !! 9- assertBool "Base58 PubKey" $ xPubExport btc (deriveXPubKey m) == v !! 10- assertBool "Base58 PrvKey" $ xPrvExport btc m == v !! 11---- This function was used to generate addition data for the test vectors-genVector :: XPrvKey -> [(Text, Text)]-genVector m =- [ ("xPrvID", encodeHex (runPutS . serialize $ xPrvID m))- , ("xPrvFP", encodeHex (runPutS . serialize $ xPrvFP m))- , ("xPrvAddr", fromJust $ addrToText btc (xPubAddr $ deriveXPubKey m))- ,- ( "bip44Addr"- , fromJust $- addrToText btc (xPubAddr $ deriveXPubKey $ derivePath bip44Addr m)- )- , ("prvKey", encodeHex (getSecKey $ xPrvKey m))- , ("xPrvWIF", xPrvWif btc m)- , ("pubKey", encodeHex (exportPubKey True $ xPubKey $ deriveXPubKey m))- , ("chain code", encodeHex (runPutS . serialize $ xPrvChain m))- , ("Hex PubKey", encodeHex (runPutS $ putXPubKey btc $ deriveXPubKey m))- , ("Hex PrvKey", encodeHex (runPutS (putXPrvKey btc m)))- ]--parseVector :: TestKey -> [TestVector] -> [(Text, XPrvKey, TestVector)]-parseVector mTxt vs =- go <$> vs- where- mast = makeXPrvKey $ fromJust $ decodeHex mTxt- go (d : vec) =- let deriv = getParsedPath $ fromJust $ parsePath $ cs d- in (d, derivePath 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/Keys/MnemonicSpec.hs
@@ -1,498 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Haskoin.Keys.MnemonicSpec (spec) where--import Control.Monad (zipWithM_)-import Data.Bits (shiftR, (.&.))-import qualified Data.ByteString as BS-import Data.Either (fromRight)-import Data.List (isPrefixOf)-import Data.Maybe (fromJust)-import Data.Serialize (Serialize, encode)-import Data.Text (Text)-import qualified Data.Text as T-import Data.Word (Word32, Word64)-import Haskoin.Keys-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--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/Haskoin/KeysSpec.hs
@@ -1,266 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Haskoin.KeysSpec (spec) where--import Control.Lens-import Control.Monad-import Data.Aeson as A-import Data.Aeson.Lens-import qualified Data.ByteString as BS-import qualified Data.ByteString.Char8 as C-import Data.Bytes.Get-import Data.Bytes.Put-import Data.Bytes.Serial-import Data.Maybe-import qualified Data.Serialize as S-import Data.String (fromString)-import Data.String.Conversions (cs)-import Data.Text (Text)-import Haskoin.Address-import Haskoin.Constants-import Haskoin.Crypto-import Haskoin.Keys-import Haskoin.Script-import Haskoin.Util-import Haskoin.Util.Arbitrary-import Haskoin.UtilSpec (readTestFile)-import Test.HUnit-import Test.Hspec-import Test.Hspec.QuickCheck-import Test.QuickCheck--serialVals :: [SerialBox]-serialVals =- [ SerialBox (snd <$> arbitraryKeyPair) -- PubKeyI- ]--readVals :: [ReadBox]-readVals =- [ ReadBox (arbitrary :: Gen SecKey)- , ReadBox arbitrarySecKeyI- , ReadBox (snd <$> arbitraryKeyPair) -- PubKeyI- ]--jsonVals :: [JsonBox]-jsonVals =- [ JsonBox (snd <$> arbitraryKeyPair) -- PubKeyI- ]--spec :: Spec-spec = do- testIdentity serialVals readVals jsonVals []- describe "PubKey properties" $ do- prop "Public key is canonical" $- forAll arbitraryKeyPair (isCanonicalPubKey . snd)- prop "Public key fromString identity" $- forAll arbitraryKeyPair $ \(_, k) ->- fromString (cs . encodeHex $ runPutS $ serialize k) == k- describe "SecKey properties" $- prop "fromWif . toWif identity" $- forAll arbitraryNetwork $ \net ->- forAll arbitraryKeyPair $ \(pk, _) ->- fromWif net (toWif net pk) == Just pk- 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- it "Passes address matching tests" testMatchingAddress- it "Passes signature verification" testSigs- it "Passes deterministic signing tests" testDetSigning- 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 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 vectors---- 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 = runPutS $ serialize p--testMiniKey :: Assertion-testMiniKey =- assertEqual "fromMiniKey" (Just res) (go "S6c56bnXQiBjk9mqSYE7ykVQ7NzrRy")- where- go = fmap (encodeHex . runPutS . S.put . secKeyData) . fromMiniKey- res = "4c7a9640c72dc2099f23715d0c8a0d8a35f8906e3cab61dd3f78b67bf887c9ab"---- Test vectors from:--- https://github.com/bitcoin/bitcoin/blob/master/src/test/key_io_tests.cpp--testKeyIOValidVector :: (Text, Text, A.Value) -> Assertion-testKeyIOValidVector (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 . runPutS . S.put . secKeyData <$> prvKeyM- assertBool "Valid PrvKey" $ isJust prvKeyM- assertEqual "Valid compression" (Just isComp) (secKeyCompressed <$> prvKeyM)- assertEqual "WIF matches payload" (Just payload) prvKeyHexM- let prvAsPubM = (eitherToMaybe . decodeOutputBS <=< decodeHex) a- assertBool "PrvKey is invalid ScriptOutput" $ isNothing prvAsPubM- -- Test from SecKey to WIF- let secM = eitherToMaybe . runGetS S.get =<< 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 . encodeOutputBS . addressToOutput <$> addrM- assertBool ("Valid Address " <> cs a) $ isJust addrM- assertEqual "Address matches payload" (Just payload) scriptM- let pubAsWifM = fromWif net a- pubAsSecM =- eitherToMaybe . runGetS S.get- =<< decodeHex a ::- Maybe SecKey- assertBool "Address is invalid Wif" $ isNothing pubAsWifM- assertBool "Address is invalid PrvKey" $ isNothing pubAsSecM- -- Test Script to Addr- let outM = eitherToMaybe . decodeOutputBS =<< decodeHex payload- resM = addrToText net =<< outputAddress =<< 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 :: [Text] -> Assertion-testKeyIOInvalidVector [a] = do- let wifMs = (`fromWif` a) <$> allNets- secKeyM = (eitherToMaybe . runGetS S.get <=< decodeHex) a :: Maybe SecKey- scriptM = (eitherToMaybe . decodeOutputBS <=< decodeHex) a :: Maybe ScriptOutput- 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 $ secKeyCompressed sec1- assertBool "Key 2" $ not $ secKeyCompressed sec2- assertBool "Key 1C" $ secKeyCompressed sec1C- assertBool "Key 2C" $ secKeyCompressed sec2C--testKeyCompressed :: Assertion-testKeyCompressed = do- assertBool "Key 1" $ not $ pubKeyCompressed pub1- assertBool "Key 2" $ not $ pubKeyCompressed pub2- assertBool "Key 1C" $ pubKeyCompressed pub1C- assertBool "Key 2C" $ pubKeyCompressed pub2C--testMatchingAddress :: Assertion-testMatchingAddress = do- assertEqual "Key 1" (Just addr1) $ addrToText btc (pubKeyAddr pub1)- assertEqual "Key 2" (Just addr2) $ addrToText btc (pubKeyAddr pub2)- assertEqual "Key 1C" (Just addr1C) $ addrToText btc (pubKeyAddr pub1C)- assertEqual "Key 2C" (Just addr2C) $ addrToText btc (pubKeyAddr pub2C)--testSigs :: Assertion-testSigs = forM_ sigMsg $ testSignature . doubleSHA256--sigMsg :: [BS.ByteString]-sigMsg =- [ mconcat ["Very secret message ", C.pack (show (i :: Int)), ": 11"]- | i <- [0 .. 15]- ]--testSignature :: Hash256 -> Assertion-testSignature 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)--testDetSigning :: Assertion-testDetSigning = do- let m = doubleSHA256 ("Very deterministic message" :: BS.ByteString)- assertEqual- "Det sig 1"- (signHash (secKeyData sec1) m)- (signHash (secKeyData sec1C) m)- assertEqual- "Det sig 2"- (signHash (secKeyData sec2) m)- (signHash (secKeyData sec2C) m)--strSecret1, strSecret2, strSecret1C, strSecret2C :: Text-strSecret1 = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"-strSecret2 = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"-strSecret1C = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"-strSecret2C = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"--sec1, sec2, sec1C, sec2C :: SecKeyI-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 :: PubKeyI-pub1 = derivePubKeyI sec1-pub2 = derivePubKeyI sec2-pub1C = derivePubKeyI sec1C-pub2C = derivePubKeyI sec2C
test/Haskoin/NetworkSpec.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} module Haskoin.NetworkSpec (spec) where@@ -5,72 +7,76 @@ 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.Constants-import Haskoin.Keys+import Haskoin.Crypto import Haskoin.Network+import Haskoin.Network.Constants import Haskoin.Transaction import Haskoin.Util import Haskoin.Util.Arbitrary-import Haskoin.UtilSpec (customCerealID) import Test.HUnit (Assertion, assertBool, assertEqual) import Test.Hspec import Test.Hspec.QuickCheck import Test.QuickCheck -serialVals :: [SerialBox]-serialVals =- [ 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- ]+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 = do- testIdentity serialVals [] [] []- describe "Custom identity tests" $ do- prop "Data.Serialize Encoding for type Message" $- forAll arbitraryNetwork $ \net ->- forAll (arbitraryMessage net) $- 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- describe "relevant bloom filter update" $ do- it "Relevant Update" relevantOutputUpdated- it "Irrelevant Update" irrelevantOutputNotUpdated+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+ 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@@ -88,97 +94,97 @@ bloomFilter2 :: Assertion bloomFilter2 = bloomFilter 2147483649 "03ce4299050000000100008001" -bloomFilter3 :: Assertion-bloomFilter3 =- assertBool "Bloom filter serialization is incorrect" $- runPutS (serialize f2) == bs+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 $ runPutS $ serialize p- f2 = bloomInsert f1 $ runPutS $ serialize $ getAddrHash160 $ pubKeyAddr p+ f1 = bloomInsert f0 $ marshal ctx p+ f2 = bloomInsert f1 $ runPutS $ serialize (pubKeyAddr ctx p).hash160 k = fromJust $ fromWif btc "5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C"- p = derivePubKeyI k+ p = derivePublicKey ctx k bs = fromJust $ decodeHex "038fc16b080000000000000001" -relevantOutputUpdated :: Assertion-relevantOutputUpdated =- assertBool "Bloom filter output updated" $- any (bloomContains bf2) spendTxInput+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 bf1 relevantTx- spendTxInput = runPutS . serialize . prevOutput <$> txIn spendRelevantTx+ bf2 = fromJust $ bloomRelevantUpdate ctx bf1 relevantTx+ spendTxInput = runPutS . serialize . (.outpoint) <$> spendRelevantTx.inputs -irrelevantOutputNotUpdated :: Assertion-irrelevantOutputNotUpdated = assertEqual "Bloom filter not updated" Nothing bf2+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 bf1 unrelatedTx+ bf2 = bloomRelevantUpdate ctx bf1 unrelatedTx -- 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- }+ 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- { 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- }+ 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- { 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- }+ 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
@@ -1,3 +1,6 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} module Haskoin.ScriptSpec (spec) where@@ -5,10 +8,11 @@ import Control.Monad import Data.Aeson as A import Data.ByteString (ByteString)-import qualified Data.ByteString as B+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@@ -17,212 +21,212 @@ import Data.Text (Text) import Data.Word import Haskoin.Address-import Haskoin.Constants-import Haskoin.Data-import Haskoin.Keys+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 Haskoin.UtilSpec (readTestFile) import Test.HUnit as HUnit import Test.Hspec import Test.Hspec.QuickCheck import Test.QuickCheck import Text.Read -serialVals :: [SerialBox]-serialVals =- [ SerialBox arbitraryScriptOp- , SerialBox arbitraryScript- ]--readVals :: [ReadBox]-readVals =- [ ReadBox arbitrarySigHash- , ReadBox arbitrarySigHashFlag- , ReadBox arbitraryScript- , ReadBox arbitraryPushDataType- , ReadBox arbitraryScriptOp- , ReadBox (arbitraryScriptOutput =<< arbitraryNetwork)- ]--jsonVals :: [JsonBox]-jsonVals =- [ JsonBox $ arbitraryScriptOutput =<< arbitraryNetwork- , JsonBox arbitraryOutPoint- , JsonBox arbitrarySigHash- , JsonBox $ fst <$> (arbitrarySigInput =<< arbitraryNetwork)- ]+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 = do- testIdentity serialVals readVals jsonVals []- describe "btc scripts" $ props btc- describe "bch scripts" $ props bch- describe "multi signatures" $- zipWithM_ (curry mapMulSigVector) mulSigVectors [0 ..]- describe "signature decoding" $- zipWithM_ (curry (sigDecodeMap btc)) 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 -> Spec-props net = do- standardSpec net- strictSigSpec net- scriptSpec net- txSigHashForkIdSpec net- forkIdScriptSpec net- sigHashSpec net- txSigHashSpec net+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 -standardSpec :: Network -> Spec-standardSpec net = do- prop "has intToScriptOp . scriptOpToInt identity" $- forAll arbitraryIntScriptOp $ \i ->- intToScriptOp <$> scriptOpToInt i `shouldBe` Right i- prop "has decodeOutput . encodeOutput identity" $- forAll (arbitraryScriptOutput net) $ \so ->- decodeOutput (encodeOutput so) `shouldBe` Right so- prop "has decodeInput . encodeOutput identity" $- forAll (arbitraryScriptInput net) $ \si ->- decodeInput net (encodeInput si) `shouldBe` Right si- prop "can sort multisig scripts" $- forAll arbitraryMSOutput $ \out ->- map- (runPutS . serialize)- (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 $ runPutS $ serialize 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))+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 -scriptSpec :: Network -> Spec-scriptSpec net =- when (getNetworkName net == "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+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)) - 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)+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 -> Spec-forkIdScriptSpec net =- when (isJust (getSigHashForkId net)) $- 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- 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+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+ Tx 1 [txI] [txO] [] 0 where- txO = TxOut{outValue = val, scriptOutput = scriptPubKey}+ txO = TxOut {value = val, script = scriptPubKey} txI =- TxIn- { prevOutput = nullOutPoint- , scriptInput = runPutS $ serialize $ Script [OP_0, OP_0]- , txInSequence = maxBound- }+ 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+ Tx 1 [txI] [txO] [] 0 where- txO = TxOut{outValue = val, scriptOutput = B.empty}+ txO = TxOut {value = val, script = B.empty} txI =- TxIn- { prevOutput = OutPoint (txHash $ creditTx scriptPubKey val) 0- , scriptInput = scriptSig- , txInSequence = maxBound- }+ 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+ B.concat $ fromMaybe err $ mapM f $ words str where f = decodeHex . cs . dropHex . replaceToken dropHex ('0' : 'x' : xs) = xs@@ -231,233 +235,238 @@ replaceToken :: String -> String replaceToken str = case readMaybe $ "OP_" <> str of- Just opcode -> "0x" <> cs (encodeHex $ runPutS $ serialize (opcode :: ScriptOp))- _ -> str+ Just opcode -> "0x" <> cs (encodeHex $ runPutS $ serialize (opcode :: ScriptOp))+ _ -> str -strictSigSpec :: Network -> Spec-strictSigSpec net =- when (getNetworkName net == "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 ->- decodeTxSig net sig `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 ->- decodeTxSig net sig `shouldSatisfy` isLeft+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 (getNetworkName net == "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+ 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 (getNetworkName net == "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+ 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 -> Spec-sigHashSpec net = 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- 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+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 $ txIn tx)- ==> if length (txIn tx) > length (txOut tx)- then res `shouldBe` one- else res `shouldNotBe` one+ 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 (txIn tx) - 1) (f sigHashSingle)+ res = txSigHash net tx s val (length tx.inputs - 1) (f sigHashSingle) one = "0100000000000000000000000000000000000000000000000000000000000000" f =- if acp- then setAnyoneCanPayFlag- else id+ if acp+ then setAnyoneCanPay+ else id {- Parse tests from bitcoin-qt repository -} -mapMulSigVector :: ((Text, Text), Int) -> Spec-mapMulSigVector (v, i) =- it name $ runMulSigVector v+mapMulSigVector :: Ctx -> ((Text, Text), Int) -> Spec+mapMulSigVector ctx (v, i) =+ it name $ runMulSigVector ctx v where name = "check multisig vector " <> show i -runMulSigVector :: (Text, Text) -> Assertion-runMulSigVector (a, ops) = assertBool "multisig vector" $ Just a == b+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'+ s' <- decodeHex ops+ eitherToMaybe $ runGetS deserialize s' b = do- o <- s- d <- eitherToMaybe $ decodeOutput o- addrToText btc $ payToScriptAddress d+ o <- s+ d <- eitherToMaybe $ decodeOutput ctx o+ addrToText btc $ payToScriptAddress ctx d -sigDecodeMap :: Network -> (Text, Int) -> Spec-sigDecodeMap net (_, i) =- it ("check signature " ++ show i) func+sigDecodeMap :: Network -> Ctx -> (Text, Int) -> Spec+sigDecodeMap net ctx (_, i) =+ it ("check signature " ++ show i) func where- func = testSigDecode net $ scriptSigSignatures !! i+ func = testSigDecode net ctx $ 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+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"- )- ]+ [ ( "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- -- 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"- ]+ -- 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)+ assertEqual "Encode script" res (encodeHex $ runPutS $ serialize s) where res =- "514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58b\- \bfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d\- \348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2f\- \cfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b\- \25c15342af52ae"+ "514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58b\+ \bfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d\+ \348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2f\+ \cfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b\+ \25c15342af52ae" s =- Script- [ OP_1- , opPushData $- d- "04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef5\- \8bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d\- \11fcdd0d348ac4"- , opPushData $- d- "0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcf\- \deb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39\- \f58b25c15342af"- , OP_2- , OP_CHECKMULTISIG- ]+ 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
@@ -1,513 +1,550 @@+{-# 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 as S+import Data.Serialize import Data.Text (Text)-import Test.HUnit (Assertion, assertBool, assertEqual)-import Test.Hspec-import Test.QuickCheck--import Control.Monad ((<=<))-import Data.Aeson (FromJSON, parseJSON, withObject, (.:))-import Data.Bifunctor (first)-import Data.ByteString.Base64 (decodeBase64)-import qualified Data.Text as Text+import Data.Text qualified as Text import Data.Text.Encoding (encodeUtf8) import Haskoin.Address-import Haskoin.Constants import Haskoin.Crypto-import Haskoin.Data-import Haskoin.Keys+import Haskoin.Network.Constants+import Haskoin.Network.Data import Haskoin.Script import Haskoin.Transaction import Haskoin.Util import Haskoin.Util.Arbitrary-import Haskoin.UtilSpec (readTestFile)+import Test.HUnit (Assertion, assertBool, assertEqual)+import Test.Hspec+import Test.QuickCheck 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+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" $- 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+ (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 $ verifyNonWitnessPSBT btc . unfinalizedMsPSBT btc- it "encodes and decodes psbt with final witness script" $- (fmap (encodeHex . S.encode) . decodeHexPSBT) validVec7Hex == Right validVec7Hex- it "handles complex psbts correctly" complexPsbtTest- it "calculates keys properly" psbtSignerTest+ 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 :: 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)+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 . inputRedeemScript $ inputs psbt !! 1- assertBool "p2wpkh" $ (isPayWitnessPKHash <$> decodeOutput rdmScript) == Right True+ let rdmScript = fromJust (psbt.inputs !! 1).inputRedeemScript+ assertBool "p2wpkh" $ (isPayWitnessPKHash <$> decodeOutput ctx 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+ 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) (outputs psbt)+ mapM_ (assertEqual "outputs are empty" emptyOutput) psbt.outputs -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)+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 :: 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)+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 rdmScript = fromJust $ inputRedeemScript secondInput- assertBool "p2wpkh" $ (isPayWitnessPKHash <$> decodeOutput rdmScript) == Right True+ let Just rdmScript = secondInput.inputRedeemScript+ assertBool "p2wpkh" $ (isPayWitnessPKHash <$> decodeOutput ctx rdmScript) == Right True - let scrptPubKey = witnessScriptPubKey secondInput- rdmScriptP2SH = toP2SH rdmScript- assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey- assertEqual "expected redeem script" expectedOut rdmScriptP2SH+ 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` outputs psbt+ assertBool "all non-empty outputs" $ emptyOutput `notElem` psbt.outputs -vec5Test :: Assertion-vec5Test = do- psbt <- decodeHexPSBTM "Cannot parse validVec5" validVec5Hex- assertEqual "Correctly decode PSBT" expectedPsbt psbt- let input = head $ inputs psbt+vec5Test :: Ctx -> Assertion+vec5Test ctx = do+ psbt <- decodeHexPSBTM ctx "Cannot parse validVec5" validVec5Hex+ assertEqual "Correctly decode PSBT" expectedPsbt psbt+ let (input : _) = psbt.inputs - let rdmScript = fromJust $ inputRedeemScript input- assertBool "p2wsh" $ (isPayWitnessScriptHash <$> decodeOutput rdmScript) == Right True+ let Just rdmScript = input.inputRedeemScript+ assertBool "p2wsh" $ (isPayWitnessScriptHash <$> decodeOutput ctx rdmScript) == Right True - let scrptPubKey = witnessScriptPubKey input- rdmScriptP2SH = toP2SH rdmScript- assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey- assertEqual "expected redeem script" expectedOut2 rdmScriptP2SH+ 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")- . decodeOutputBS- . fromJust- $ decodeHex "a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87"+ fromRight (error "could not decode expected output")+ . unmarshal ctx+ . fromJust+ $ decodeHex "a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87" -- From the bitcoind decodepsbt rpc call expectedPsbt =- PartiallySignedTransaction- { unsignedTransaction =- Tx- { txVersion = 2- , txIn =- [ TxIn- { prevOutput =- OutPoint- { outPointHash = "39bc5c3b33d66ce3d7852a7942331e3ec10f8ba50f225fc41fb5dfa523239a27"- , outPointIndex = 0- }- , scriptInput = ""- , txInSequence = 4294967295- }- ]- , txOut =- [ TxOut- { outValue = 199908000- , scriptOutput = (fromJust . decodeHex) "76a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac"- }- ]- , txWitness = mempty- , txLockTime = 0- }- , globalUnknown = mempty- , inputs =- [ Input- { nonWitnessUtxo = Nothing- , witnessUtxo =- Just- ( TxOut- { outValue = 199909013- , scriptOutput = (fromJust . decodeHex) "a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87"- }- )- , partialSigs =- fromList- [- ( PubKeyI- { pubKeyPoint = "03b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46"- , pubKeyCompressed = 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- [- ( PubKeyI- { pubKeyPoint = "03b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46"- , pubKeyCompressed = True- }- , ("b4a6ba67", [hardIndex 0, hardIndex 0, hardIndex 4])- )- ,- ( PubKeyI- { pubKeyPoint = "03de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd"- , pubKeyCompressed = True- }- , ("b4a6ba67", [hardIndex 0, hardIndex 0, hardIndex 5])- )- ]- , finalScriptSig = Nothing- , finalScriptWitness = Nothing- , inputUnknown = mempty- }- ]- , outputs =- [ Output- { outputRedeemScript = Nothing- , outputWitnessScript = Nothing- , outputHDKeypaths = mempty- , outputUnknown = mempty- }- ]- }+ 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 :: Assertion-vec6Test = do- psbt <- decodeHexPSBTM "Cannot parse validVec6" validVec6Hex- assertEqual "1 input" 1 (length $ inputs psbt)- assertEqual "1 output" 1 (length $ outputs psbt)+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 = unsignedTransaction psbt- assertEqual "correct transaction" "75c5c9665a570569ad77dd1279e6fd4628a093c4dcbf8d41532614044c14c115" (txHash tx)+ let tx = psbt.unsignedTransaction+ assertEqual "correct transaction" "75c5c9665a570569ad77dd1279e6fd4628a093c4dcbf8d41532614044c14c115" (txHash tx) - assertEqual "correct unknowns" expectedUnknowns (inputUnknown . head $ inputs psbt)+ assertEqual "correct unknowns" expectedUnknowns (head psbt.inputs).inputUnknown where expectedUnknowns =- UnknownMap $- singleton- (Key 0x0f (fromJust $ decodeHex "010203040506070809"))- (fromJust $ decodeHex "0102030405060708090a0b0c0d0e0f")+ UnknownMap $+ singleton+ (Key 0x0f (fromJust $ decodeHex "010203040506070809"))+ (fromJust $ decodeHex "0102030405060708090a0b0c0d0e0f") -complexPsbtTest :: Assertion-complexPsbtTest = do- complexPsbtData <- readTestFile "complex_psbt.json"+complexPsbtTest :: Ctx -> Assertion+complexPsbtTest ctx = do+ complex <- readTestFileParser (parseComplexJSON ctx) "complex_psbt.json" - let computedCombinedPsbt = mergeMany $ complexSignedPsbts complexPsbtData- expectedCombinedPsbt = stripRedundantUtxos $ complexCombinedPsbt complexPsbtData- assertEqual "combined psbt" computedCombinedPsbt (Just expectedCombinedPsbt)+ let computedCombinedPsbt = mergeMany $ complexSignedPsbts complex+ expectedCombinedPsbt = stripRedundantUtxos $ complexCombinedPsbt complex+ assertEqual "combined psbt" computedCombinedPsbt (Just expectedCombinedPsbt) - let computedCompletePsbt = complete $ complexCombinedPsbt complexPsbtData- expectedCompletePsbt = complexCompletePsbt complexPsbtData- assertEqual "complete psbt" computedCompletePsbt expectedCompletePsbt+ let computedCompletePsbt = complete ctx $ complexCombinedPsbt complex+ expectedCompletePsbt = complexCompletePsbt complex+ assertEqual "complete psbt" computedCompletePsbt expectedCompletePsbt - let computedFinalTx = finalTransaction $ complexCompletePsbt complexPsbtData- assertEqual "final tx" computedFinalTx (complexFinalTx complexPsbtData)+ let computedFinalTx = finalTransaction $ complexCompletePsbt complex+ assertEqual "final tx" computedFinalTx (complexFinalTx complex) where- stripRedundantUtxos psbt = psbt{inputs = stripRedundantUtxo <$> inputs psbt}+ stripRedundantUtxos PSBT {..} = PSBT {inputs = stripRedundantUtxo <$> inputs, ..} stripRedundantUtxo input- | Just{} <- witnessUtxo input = input{nonWitnessUtxo = Nothing}- | otherwise = input+ | Just {} <- input.witnessUtxo = input {nonWitnessUtxo = Nothing}+ | otherwise = input -psbtSignerTest :: Assertion-psbtSignerTest = 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)))+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 theSecKey <> xPrvSigner xprv (Just origin)+ signer = secKeySigner ctx theSecKey <> xPrvSigner ctx xprv (Just origin) Just theSecKey = secKey "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"- thePubKey = PubKeyI{pubKeyPoint = derivePubKey theSecKey, pubKeyCompressed = True}+ thePubKey = PublicKey {point = derivePubKey ctx theSecKey, compress = True} - rootXPrv = makeXPrvKey "psbtSignerTest"- rootFP = xPubFP $ deriveXPubKey rootXPrv- xprv = derivePath keyPath rootXPrv- keyFP = xPubFP $ deriveXPubKey xprv+ 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 = xPrvKey $ derivePath originKeyPath rootXPrv- originPathPubKey = PubKeyI{pubKeyPoint = derivePubKey originPathSecKey, pubKeyCompressed = True}+ originPathSecKey = (derivePath ctx originKeyPath rootXPrv).key+ originPathPubKey = PublicKey {point = derivePubKey ctx originPathSecKey, compress = True} directPath = Deriv :/ 1- directPathSecKey = xPrvKey $ derivePath directPath xprv- directPathPubKey = PubKeyI{pubKeyPoint = derivePubKey directPathSecKey, pubKeyCompressed = True}+ directPathSecKey = (derivePath ctx directPath xprv).key+ directPathPubKey = PublicKey {point = derivePubKey ctx directPathSecKey, compress = True} -expectedOut :: ScriptOutput-expectedOut =- fromRight (error "could not decode expected output")- . decodeOutputBS- . fromJust- $ decodeHex "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"+expectedOut :: Ctx -> ScriptOutput+expectedOut ctx =+ fromRight (error "could not decode expected output")+ . unmarshal ctx+ . fromJust+ $ decodeHex "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787" -witnessScriptPubKey :: Input -> ScriptOutput-witnessScriptPubKey =- fromRight (error "could not decode witness utxo")- . decodeOutputBS- . scriptOutput- . fromJust- . witnessUtxo+witnessScriptPubKey :: Ctx -> Input -> ScriptOutput+witnessScriptPubKey ctx =+ fromRight (error "could not decode witness utxo")+ . unmarshal ctx+ . (.script)+ . fromJust+ . (.witnessUtxo) -decodeHexPSBT :: Text -> Either String PartiallySignedTransaction-decodeHexPSBT = S.decode . fromJust . decodeHex+decodeHexPSBT :: Ctx -> Text -> Either String PSBT+decodeHexPSBT ctx = runGet (getPSBT ctx) . fromJust . decodeHex -decodeHexPSBTM :: (Monad m, MonadFail m) => String -> Text -> m PartiallySignedTransaction-decodeHexPSBTM errMsg = either (fail . (errMsg <>) . (": " <>)) return . decodeHexPSBT+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+ either (error "Could not decode script") encodeScript+ . runGetS deserialize+ . fromJust+ . decodeHex where encodeScript :: Script -> ByteString encodeScript = runPutS . serialize -invalidVecTest :: Text -> Assertion-invalidVecTest = assertBool "invalid psbt" . isLeft . decodeHexPSBT+invalidVecTest :: Ctx -> Text -> Assertion+invalidVecTest ctx = assertBool "invalid psbt" . isLeft . decodeHexPSBT ctx -decodeVecTest :: Int -> Text -> Assertion-decodeVecTest i = assertBool (show i <> " decodes correctly") . isRight . decodeHexPSBT+decodeVecTest :: Ctx -> Int -> Text -> Assertion+decodeVecTest ctx i = assertBool (show i <> " decodes correctly") . isRight . decodeHexPSBT ctx -encodeVecTest :: PartiallySignedTransaction -> Text -> Assertion-encodeVecTest psbt hex = assertEqual "encodes correctly" (S.encode psbt) (fromJust $ decodeHex hex)+encodeVecTest :: Ctx -> PSBT -> Text -> Assertion+encodeVecTest ctx psbt hex =+ assertEqual+ "encodes correctly"+ ((runPut . putPSBT ctx) psbt)+ ((fromJust . decodeHex) hex) -trivialPSBT :: PartiallySignedTransaction+trivialPSBT :: PSBT trivialPSBT =- PartiallySignedTransaction- { unsignedTransaction = Tx{txVersion = 2, txIn = [], txOut = [], txWitness = [], txLockTime = 0}- , globalUnknown = UnknownMap mempty- , inputs = []- , outputs = []- }+ PSBT+ { unsignedTransaction = Tx {version = 2, inputs = [], outputs = [], witness = [], locktime = 0},+ globalUnknown = UnknownMap mempty,+ inputs = [],+ outputs = []+ } trivialPSBTHex :: Text trivialPSBTHex = "70736274ff01000a0200000000000000000000" -nonEmptyTransactionPSBT :: PartiallySignedTransaction+nonEmptyTransactionPSBT :: PSBT nonEmptyTransactionPSBT = emptyPSBT testTx1 -verifyNonWitnessPSBT :: Network -> PartiallySignedTransaction -> Bool-verifyNonWitnessPSBT net psbt = verifyStdTx net (finalTransaction (complete psbt)) sigData+verifyNonWitnessPSBT :: Network -> Ctx -> PSBT -> Bool+verifyNonWitnessPSBT net ctx psbt =+ verifyStdTx net ctx (finalTransaction (complete ctx 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)+ 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 -> (SecKeyI, PubKeyI) -> PartiallySignedTransaction-unfinalizedPkhPSBT net (prvKey, pubKey) =- (emptyPSBT currTx)- { inputs = [emptyInput{nonWitnessUtxo = Just prevTx, partialSigs = singleton pubKey sig}]- }+unfinalizedPkhPSBT :: Network -> Ctx -> (PrivateKey, PublicKey) -> PSBT+unfinalizedPkhPSBT net ctx (prvKey, pubKey) =+ let PSBT {..} = emptyPSBT currTx+ in PSBT {inputs = [input], ..} where- currTx = unfinalizedTx (txHash prevTx)- prevTx = testUtxo [prevOut]- prevOutScript = addressToScript (pubKeyAddr pubKey)+ input =+ emptyInput+ { nonWitnessUtxo = Just prevTx,+ partialSigs = singleton pubKey sig+ }+ currTx =+ unfinalizedTx (txHash prevTx)+ prevTx =+ testUtxo [prevOut]+ prevOutScript =+ addressToScript ctx (pubKeyAddr ctx pubKey) prevOut =- TxOut- { outValue = 200000000- , scriptOutput = runPutS (serialize prevOutScript)- }- h = txSigHash net currTx prevOutScript (outValue prevOut) 0 sigHashAll- sig = encodeTxSig $ TxSignature (signHash (secKeyData prvKey) h) sigHashAll+ 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 :: Gen ([(SecKeyI, PubKeyI)], Int)-arbitraryMultiSig = do- (m, n) <- arbitraryMSParam- keys <- vectorOf n arbitraryKeyPair- return (keys, m)+arbitraryMultiSig :: Ctx -> Gen ([(PrivateKey, PublicKey)], Int)+arbitraryMultiSig ctx = do+ (m, n) <- arbitraryMSParam+ keys <- vectorOf n (arbitraryKeyPair ctx)+ 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- }- ]- }+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 $ PayMulSig (map snd keys) m- prevOut = TxOut{outValue = 200000000, scriptOutput = encodeOutputBS (toP2SH prevOutScript)}- h = txSigHash net currTx prevOutScript (outValue prevOut) 0 sigHashAll+ 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, pubKey) = (pubKey, encodeTxSig $ TxSignature (signHash (secKeyData prvKey) h) sigHashAll)+ 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- { 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- }+ 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"- ]+ [ "0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300",+ "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000",+ "70736274ff0100fd0a010200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be4000000006a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa88292feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000",+ "70736274ff000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000",+ "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000000",+ "70736274ff020001550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+ "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac000000000002010020955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+ "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87210203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+ "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a01020400220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+ "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d568102050047522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+ "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae210603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd10b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000",+ "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f0000000000020000bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",+ "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000020700da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",+ "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903020800da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",+ "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00210203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58710d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000",+ "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0203000100000000010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00",+ "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0002000016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00",+ "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c00010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a6521010025512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00"+ ] -validEncodeVec :: [(PartiallySignedTransaction, Text)]+validEncodeVec :: [(PSBT, 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- }+ 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- { 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- }+ 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{outValue = 200000000, scriptOutput = hexScript "76a91485cff1097fd9e008bb34af709c62197b38978a4888ac"}- , TxOut{outValue = 190303501938, scriptOutput = hexScript "a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587"}- ]+ testUtxo+ [ TxOut {value = 200000000, script = hexScript "76a91485cff1097fd9e008bb34af709c62197b38978a4888ac"},+ TxOut {value = 190303501938, script = hexScript "a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587"}+ ] -validVec1 :: PartiallySignedTransaction-validVec1 = (emptyPSBT testTx1){inputs = [emptyInput{nonWitnessUtxo = Just testUtxo1}]}+validVec1 :: PSBT+validVec1 =+ let PSBT {..} = emptyPSBT testTx1+ in PSBT {inputs = [emptyInput {nonWitnessUtxo = Just testUtxo1}], ..} validVec :: [Text] validVec = [validVec1Hex, validVec2Hex, validVec3Hex, validVec4Hex, validVec5Hex, validVec6Hex]@@ -534,35 +571,45 @@ validVec7Hex :: Text validVec7Hex = "70736274ff0100520200000001815dd29e16fd2f567a040ce24f5337fb9cfd0c05bacd8890714a33edc7cbbc920000000000ffffffff0192f1052a01000000160014ef9ade26f63015d57f4ecdb268d1a9b8d6cd8872000000000001008402000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03510101ffffffff0200f2052a010000001600145f4ffa19dbbe464561c50fc4d8d8ac0b41009dd20000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90000000001011f00f2052a010000001600145f4ffa19dbbe464561c50fc4d8d8ac0b41009dd201086b02473044022026a9f7afdb0128970bb3577e536ec3d3dc10c1e82650d11c9da1df9003b31d0202202258b11f962f12e0897c642cd6f38a0181db17197f3693a530c9431eb44dde7d0121033dc786e9628bb6c41c08fceb9b37458ad7a95e7e6b04e0bde45b6879398c3ac100220203a6affb58dda998a4ffdce652feb91038fdfc78c748ae687372e11292af8d312d101c4c5bfc00000080000000800100008000" -data ComplexPsbtData = ComplexPsbtData- { complexSignedPsbts :: [PartiallySignedTransaction]- , complexCombinedPsbt :: PartiallySignedTransaction- , complexCompletePsbt :: PartiallySignedTransaction- , complexFinalTx :: Tx- }- deriving (Eq, Show)+data ComplexPSBT = ComplexPSBT+ { complexSignedPsbts :: [PSBT],+ complexCombinedPsbt :: PSBT,+ complexCompletePsbt :: PSBT,+ complexFinalTx :: Tx+ }+ deriving (Eq, Show) -instance FromJSON ComplexPsbtData where- parseJSON = withObject "ComplexPsbtData" $ \obj -> do- ComplexPsbtData- <$> 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 . (S.decode <=< maybe (Left "hex") Right . decodeHex)- parsePsbt = either fail pure . (S.decode <=< first Text.unpack . decodeBase64) . encodeUtf8- psbtField fieldName obj = obj .: fieldName >>= parsePsbt+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
@@ -1,189 +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 (FromJSON (parseJSON), withObject, (.:), (.:?))-import Data.Aeson.Types (Parser)-import qualified Data.ByteArray as BA+import Data.Aeson+import Data.Aeson.Types+import Data.ByteArray qualified as BA import Data.ByteString (ByteString)-import qualified Data.ByteString as BS+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 (- MAST (..),- PubKey,- PubKeyI (PubKeyI),- ScriptOutput,- ScriptPathData (..),- TaprootOutput (TaprootOutput),- TaprootWitness (ScriptPathSpend),- XOnlyPubKey (..),- addrToText,- btc,- decodeHex,- encodeTaprootWitness,- getMerkleProofs,- mastCommitment,- outputAddress,- taprootInternalKey,- taprootMAST,- taprootOutputKey,- taprootScriptOutput,- verifyScriptPathData,- )-import Haskoin.UtilSpec (readTestFile)+import Haskoin import Test.HUnit (assertBool, (@?=)) import Test.Hspec (Spec, describe, it, runIO) spec :: Spec-spec = do- TestVector{testScriptPubKey} <- runIO $ readTestFile "bip341.json"- describe "Taproot" $ do- it "should calculate the correct hashes" $ mapM_ testHashes testScriptPubKey- it "should build the correct output key" $ mapM_ testOutputKey testScriptPubKey- it "should build the correct script output" $ mapM_ testScriptOutput testScriptPubKey- it "should calculate the correct control blocks" $ mapM_ testControlBlocks testScriptPubKey- it "should arrive at the correct address" $ mapM_ testAddress testScriptPubKey+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 $ (taprootMAST . tspkGiven) 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+ -- 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+ MASTBranch branchL branchR -> getLeafHashes branchL <> getLeafHashes branchR+ leaf@MASTLeaf {} -> [BA.convert $ mastCommitment leaf]+ MASTCommitment {} -> mempty -- The test vectors have complete trees -testOutputKey :: TestScriptPubKey -> IO ()-testOutputKey testData = do- XOnlyPubKey (taprootOutputKey theOutput) @?= theOutputKey+testOutputKey :: Ctx -> TestScriptPubKey -> IO ()+testOutputKey ctx testData = do+ XOnlyPubKey (taprootOutputKey ctx theOutput) @?= theOutputKey where theOutput = tspkGiven testData theOutputKey = XOnlyPubKey . spkiTweakedPubKey $ tspkIntermediary testData -testScriptOutput :: TestScriptPubKey -> IO ()-testScriptOutput testData =- taprootScriptOutput (tspkGiven testData) @?= (spkeScriptPubKey . tspkExpected) testData+testScriptOutput :: Ctx -> TestScriptPubKey -> IO ()+testScriptOutput ctx testData =+ taprootScriptOutput ctx (tspkGiven testData) @?= (spkeScriptPubKey . tspkExpected) testData -testControlBlocks :: TestScriptPubKey -> IO ()-testControlBlocks testData = do- mapM_ onExamples exampleControlBlocks- mapM_ checkVerification scriptPathSpends+testControlBlocks :: Ctx -> TestScriptPubKey -> IO ()+testControlBlocks ctx testData = do+ mapM_ onExamples exampleControlBlocks+ mapM_ checkVerification scriptPathSpends where theOutput = tspkGiven testData- theOutputKey = taprootOutputKey theOutput+ theOutputKey = taprootOutputKey ctx theOutput exampleControlBlocks = spkeControlBlocks $ tspkExpected testData calculatedControlBlocks =- (!! 1) . encodeTaprootWitness . ScriptPathSpend <$> scriptPathSpends+ (!! 1) . encodeTaprootWitness ctx . ScriptPathSpend <$> scriptPathSpends scriptPathSpends =- fmap mkScriptPathSpend- . maybe mempty getMerkleProofs- $ taprootMAST theOutput- mkScriptPathSpend (scriptPathLeafVersion, scriptPathScript, proof) =- ScriptPathData- { scriptPathAnnex = Nothing- , scriptPathStack = mempty- , scriptPathScript- , scriptPathExternalIsOdd = odd $ keyParity theOutputKey- , scriptPathLeafVersion- , scriptPathInternalKey = taprootInternalKey theOutput- , scriptPathControl = BA.convert <$> proof- }+ 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 theOutputKey+ checkVerification = assertBool "Script verifies" . verifyScriptPathData ctx theOutputKey -keyParity :: PubKey -> Word8-keyParity key = case BS.unpack . runPutS . serialize $ PubKeyI key True of+keyParity :: Ctx -> PubKey -> Word8+keyParity ctx key =+ case BS.unpack . marshal ctx $ PublicKey key True of 0x02 : _ -> 0x00 _ -> 0x01 -testAddress :: TestScriptPubKey -> IO ()-testAddress testData = computedAddress @?= (Just . spkeAddress . tspkExpected) testData+testAddress :: Ctx -> TestScriptPubKey -> IO ()+testAddress ctx testData =+ computedAddress @?= (Just . spkeAddress . tspkExpected) testData where- computedAddress = (addrToText btc <=< outputAddress) . taprootScriptOutput $ tspkGiven testData+ computedAddress =+ (addrToText btc <=< outputAddress ctx)+ . taprootScriptOutput ctx+ $ tspkGiven testData newtype SpkGiven = SpkGiven {unSpkGiven :: TaprootOutput} -instance FromJSON SpkGiven where- parseJSON = withObject "SpkGiven" $ \obj ->- fmap SpkGiven $- TaprootOutput- <$> (xOnlyPubKey <$> obj .: "internalPubkey")- <*> (obj .:? "scriptTree" >>= traverse parseScriptTree)- 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+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- }+ { spkiLeafHashes :: Maybe [ByteString],+ spkiMerkleRoot :: Maybe ByteString,+ spkiTweakedPubKey :: PubKey+ } -instance FromJSON SpkIntermediary where- parseJSON = withObject "SpkIntermediary" $ \obj ->- SpkIntermediary- <$> (obj .:? "leafHashes" >>= (traverse . traverse) jsonHex)- <*> (obj .: "merkleRoot" >>= traverse jsonHex)- <*> (xOnlyPubKey <$> obj .: "tweakedPubkey")+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- }+ { spkeScriptPubKey :: ScriptOutput,+ spkeControlBlocks :: Maybe [ByteString],+ spkeAddress :: Text+ } -instance FromJSON SpkExpected where- parseJSON = withObject "SpkExpected" $ \obj ->- SpkExpected- <$> obj .: "scriptPubKey"- <*> (obj .:? "scriptPathControlBlocks" >>= (traverse . traverse) jsonHex)- <*> obj .: "bip350Address"+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- }+ { tspkGiven :: TaprootOutput,+ tspkIntermediary :: SpkIntermediary,+ tspkExpected :: SpkExpected+ } -instance FromJSON TestScriptPubKey where- parseJSON = withObject "TestScriptPubKey" $ \obj ->- TestScriptPubKey- <$> (unSpkGiven <$> obj .: "given")- <*> obj .: "intermediary"- <*> obj .: "expected"+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]- }+ { testScriptPubKey :: [TestScriptPubKey]+ } -instance FromJSON TestVector where- parseJSON = withObject "TestVector" $ \obj ->- TestVector <$> obj .: "scriptPubKey"+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
@@ -1,11 +1,18 @@+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-} module Haskoin.TransactionSpec (spec) where -import qualified Data.ByteString as B+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)@@ -13,9 +20,9 @@ import Data.Text (Text) import Data.Word (Word32, Word64) import Haskoin.Address-import Haskoin.Constants-import Haskoin.Data-import Haskoin.Keys+import Haskoin.Crypto+import Haskoin.Network.Constants+import Haskoin.Network.Data import Haskoin.Script import Haskoin.Transaction import Haskoin.Util@@ -25,345 +32,316 @@ import Test.Hspec.QuickCheck import Test.QuickCheck -serialVals :: [SerialBox]-serialVals =- [ SerialBox $ arbitraryTx =<< arbitraryNetwork- , SerialBox $ arbitraryWitnessTx =<< arbitraryNetwork- , SerialBox $ arbitraryLegacyTx =<< arbitraryNetwork- , SerialBox $ arbitraryTxIn =<< arbitraryNetwork- , SerialBox $ arbitraryTxOut =<< arbitraryNetwork- , SerialBox arbitraryOutPoint- ]--readVals :: [ReadBox]-readVals =- [ ReadBox arbitraryTxHash- , ReadBox $ arbitraryTx =<< arbitraryNetwork- , ReadBox $ arbitraryTxIn =<< arbitraryNetwork- , ReadBox $ arbitraryTxOut =<< arbitraryNetwork- , ReadBox arbitraryOutPoint- ]--jsonVals :: [JsonBox]-jsonVals =- [ JsonBox arbitraryTxHash- , JsonBox $ arbitraryTx =<< arbitraryNetwork- , JsonBox $ arbitraryWitnessTx =<< arbitraryNetwork- , JsonBox $ arbitraryLegacyTx =<< arbitraryNetwork- , JsonBox $ arbitraryTxIn =<< arbitraryNetwork- , JsonBox $ arbitraryTxOut =<< arbitraryNetwork- , JsonBox arbitraryOutPoint- ]+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 = do- testIdentity serialVals readVals jsonVals []- 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- prop "guess transaction size" $- forAll arbitraryNetwork $ \net ->- forAll (arbitraryAddrOnlyTxFull net) (testGuessSize net)- 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) (testDetSignTx net)- prop "sign and validate (nested) transaction" $- forAll arbitraryNetwork $ \net ->- forAll (arbitrarySigningData net) (testDetSignNestedTx net)- prop "merge partially signed transactions" $- forAll arbitraryNetwork $ \net ->- property $ forAll (arbitraryPartialTxs net) (testMergeTx net)- describe "Transaction vectors" $ do- it "compute txid from tx" $ mapM_ testTxidVector txidVectors- it "build pkhash transaction (generated from bitcoind)" $- mapM_ testPKHashVector pkHashVectors+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)+ 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"- )- ]+ [ ( "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 :: ([(Text, Word32)], [(Text, Word64)], Text) -> Assertion-testPKHashVector (is, os, res) =- assertEqual- "Build PKHash Tx"- (Right res)- (encodeHex . runPutS . serialize <$> txE)+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 (map f is) os+ 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",+ 14 )- ,- (- [- ( "eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db"- , 0- )- ,- ( "0001000000000000000000000000000000000000000000000000000000000000"- , 2147483647- )- ]- , []- , "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a\- \1eb29eb0000000000ffffffff000000000000000000000000000000000000000000\- \0000000000000000000100ffffff7f00ffffffff0000000000"+ ],+ [("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb", 90000000)],+ "0100000001db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654\+ \a1eb29eb0e00000000ffffffff01804a5d05000000001976a91424aa604689cc58\+ \2292b97668bedd91dd5bf9374c88ac00000000"+ ),+ ( [ ( "eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",+ 0+ ),+ ( "0001000000000000000000000000000000000000000000000000000000000000",+ 2147483647 )- ,- ( []- ,- [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb", 1)- , ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n", 2100000000000000)- ]- , "01000000000201000000000000001976a91424aa604689cc582292b97668bedd91d\- \d5bf9374c88ac0040075af07507001976a9145d16672f53981ff21c5f42b40d1954\- \993cbca54f88ac00000000"+ ],+ [ ("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 -> Address -> TestCoin -> Bool-testBuildAddrTx net a (TestCoin v)- | isPubKeyAddress a = Right (PayPKHash (getAddrHash160 a)) == out- | isScriptAddress a = Right (PayScriptHash (getAddrHash160 a)) == out- | otherwise = undefined+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- tx = buildAddrTx net [] [(fromJust (addrToText net a), v)]- out =- decodeOutputBS $- scriptOutput $- head $ txOut (fromRight (error "Could not build transaction") tx)+ 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 -> Tx -> Bool-testGuessSize net tx =- guess >= len && guess <= len + 2 * delta+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 $ txIn tx- f i =- fromRight (error "Could not decode input") $- decodeInputBS net $ scriptInput i+ 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- ( fromRight (error "Could not decode transaction output")- . decodeOutputBS- . scriptOutput- )- $ txOut tx+ 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+ 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+ (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+ 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+testDetSignTx :: Network -> Ctx -> (Tx, [SigInput], [PrivateKey]) -> Bool+testDetSignTx net ctx (tx, sigis, prv) =+ not verify1 && not verify2 && verify3 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+ 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 -> (Tx, [SigInput], [SecKeyI]) -> Bool-testDetSignNestedTx net (tx, sigis, prv) =- not (verifyStdTx net tx verData)- && not (verifyStdTx net txSigP verData)- && verifyStdTx net txSigC verData+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 =- fromRight (error "Could not decode transaction") $- signNestedWitnessTx net tx sigis (secKeyData <$> tail prv)+ either error id $+ signNestedWitnessTx net ctx tx sigis ((.key) <$> tail prv) txSigC =- fromRight (error "Could not decode transaction") $- signNestedWitnessTx net txSigP sigis [secKeyData (head prv)]+ either error id $+ signNestedWitnessTx net ctx txSigP sigis [(head prv).key] verData = handleSegwit <$> sigis handleSegwit (SigInput s v o _ _)- | isSegwit s = (toP2SH $ encodeOutput s, v, o)- | otherwise = (s, v, o)+ | isSegwit s = (toP2SH (encodeOutput ctx 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))- ]+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 txs outs+ mergeRes = mergeTxs net ctx txs outs mergedTx = fromRight (error "Could not merge") mergeRes- isValid = verifyStdTx net mergedTx outs+ isValid = verifyStdTx net ctx mergedTx outs enoughSigs = all (\(m, c) -> c >= m) sigMap- sigMap =- map (\((_, _, _, m, _), inp) -> (m, sigCnt inp)) $- zip os $ txIn mergedTx+ sigFun (_, _, _, m, _) inp = (m, sigCnt inp)+ sigMap = zipWith sigFun os mergedTx.inputs 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"+ 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
@@ -1,58 +1,67 @@-module Haskoin.UtilSpec (- spec,- customCerealID,- readTestFile,-) where+{-# LANGUAGE ImportQualifiedPost #-} -import Data.Aeson (FromJSON, ToJSON)-import qualified Data.Aeson as A+module Haskoin.UtilSpec (spec) where++import Data.Aeson import Data.Aeson.Encoding (encodingToLazyByteString)-import Data.Aeson.Types (Parser, parseMaybe)-import qualified Data.ByteString as BS+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 qualified Data.Sequence as Seq+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+import Test.QuickCheck (forAll) +identityTests :: IdentityTests+identityTests =+ def+ { readTests =+ [ ReadBox arbitraryNetwork+ ]+ }+ spec :: Spec spec =- describe "utility functions" $ do- 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+ 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 :: BS.ByteString -> Bool+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)+ 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+ if null as+ then 0+ else i `mod` length as bs = permutations as !! idx testMatchTemplateLen :: [Int] -> [Int] -> Bool@@ -67,14 +76,3 @@ testMaybeToEither :: Maybe Int -> String -> Bool testMaybeToEither (Just v) str = maybeToEither str (Just v) == Right v testMaybeToEither m str = maybeToEither str m == Left str--{-- Test Utilities --}--customCerealID :: Eq a => Get a -> Putter a -> a -> Bool-customCerealID g p a = runGet g (runPut (p a)) == Right a--readTestFile :: A.FromJSON a => FilePath -> IO a-readTestFile fp =- A.eitherDecodeFileStrict ("data/" <> fp) >>= either (error . message) return- where- message aesonErr = "Could not read test file " <> fp <> ": " <> aesonErr